mirror of
https://github.com/opnsense/src.git
synced 2026-06-05 06:42:56 -04:00
Vendor import of compiler-rt trunk r290819:
https://llvm.org/svn/llvm-project/compiler-rt/trunk@290819
This commit is contained in:
parent
0230fcf22f
commit
316d58822d
588 changed files with 17337 additions and 7478 deletions
108
CMakeLists.txt
108
CMakeLists.txt
|
|
@ -1,14 +1,14 @@
|
|||
# CMake build for CompilerRT.
|
||||
#
|
||||
# This build assumes that CompilerRT is checked out into the
|
||||
# 'projects/compiler-rt' inside of an LLVM tree.
|
||||
# 'projects/compiler-rt' or 'runtimes/compiler-rt' inside of an LLVM tree.
|
||||
# Standalone build system for CompilerRT is not yet ready.
|
||||
#
|
||||
# An important constraint of the build is that it only produces libraries
|
||||
# based on the ability of the host toolchain to target various platforms.
|
||||
|
||||
# Check if compiler-rt is built as a standalone project.
|
||||
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR COMPILER_RT_STANDALONE_BUILD)
|
||||
project(CompilerRT C CXX ASM)
|
||||
set(COMPILER_RT_STANDALONE_BUILD TRUE)
|
||||
endif()
|
||||
|
|
@ -32,42 +32,17 @@ list(INSERT CMAKE_MODULE_PATH 0
|
|||
)
|
||||
|
||||
include(base-config-ix)
|
||||
include(CompilerRTUtils)
|
||||
|
||||
option(COMPILER_RT_BUILD_BUILTINS "Build builtins" ON)
|
||||
mark_as_advanced(COMPILER_RT_BUILD_BUILTINS)
|
||||
option(COMPILER_RT_BUILD_SANITIZERS "Build sanitizers" ON)
|
||||
mark_as_advanced(COMPILER_RT_BUILD_SANITIZERS)
|
||||
option(COMPILER_RT_BUILD_XRAY "Build xray" ON)
|
||||
mark_as_advanced(COMPILER_RT_BUILD_XRAY)
|
||||
|
||||
if (COMPILER_RT_STANDALONE_BUILD)
|
||||
if (NOT LLVM_CONFIG_PATH)
|
||||
find_program(LLVM_CONFIG_PATH "llvm-config"
|
||||
DOC "Path to llvm-config binary")
|
||||
if (NOT LLVM_CONFIG_PATH)
|
||||
message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
|
||||
endif()
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root"
|
||||
RESULT_VARIABLE HAD_ERROR
|
||||
OUTPUT_VARIABLE CONFIG_OUTPUT)
|
||||
if (HAD_ERROR)
|
||||
message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
|
||||
endif()
|
||||
string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
|
||||
list(GET CONFIG_OUTPUT 0 LLVM_BINARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 1 LLVM_TOOLS_BINARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 2 LLVM_LIBRARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 3 LLVM_MAIN_SRC_DIR)
|
||||
|
||||
# Make use of LLVM CMake modules.
|
||||
file(TO_CMAKE_PATH ${LLVM_BINARY_DIR} LLVM_BINARY_DIR_CMAKE_STYLE)
|
||||
set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
|
||||
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
|
||||
# Get some LLVM variables from LLVMConfig.
|
||||
include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
|
||||
|
||||
set(LLVM_LIBRARY_OUTPUT_INTDIR
|
||||
${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
|
||||
load_llvm_config()
|
||||
|
||||
# Find Python interpreter.
|
||||
set(Python_ADDITIONAL_VERSIONS 2.7 2.6 2.5)
|
||||
|
|
@ -86,29 +61,10 @@ if (COMPILER_RT_STANDALONE_BUILD)
|
|||
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
|
||||
endif()
|
||||
|
||||
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${TARGET_TRIPLE} CACHE STRING
|
||||
"Default triple for which compiler-rt runtimes will be built.")
|
||||
if(DEFINED COMPILER_RT_TEST_TARGET_TRIPLE)
|
||||
# Backwards compatibility: this variable used to be called
|
||||
# COMPILER_RT_TEST_TARGET_TRIPLE.
|
||||
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${COMPILER_RT_TEST_TARGET_TRIPLE})
|
||||
endif()
|
||||
|
||||
string(REPLACE "-" ";" TARGET_TRIPLE_LIST ${COMPILER_RT_DEFAULT_TARGET_TRIPLE})
|
||||
list(GET TARGET_TRIPLE_LIST 0 COMPILER_RT_DEFAULT_TARGET_ARCH)
|
||||
list(GET TARGET_TRIPLE_LIST 1 COMPILER_RT_DEFAULT_TARGET_OS)
|
||||
list(GET TARGET_TRIPLE_LIST 2 COMPILER_RT_DEFAULT_TARGET_ABI)
|
||||
# Determine if test target triple is specified explicitly, and doesn't match the
|
||||
# default.
|
||||
if(NOT COMPILER_RT_DEFAULT_TARGET_TRIPLE STREQUAL TARGET_TRIPLE)
|
||||
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE TRUE)
|
||||
else()
|
||||
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE FALSE)
|
||||
endif()
|
||||
construct_compiler_rt_default_triple()
|
||||
if ("${COMPILER_RT_DEFAULT_TARGET_ABI}" STREQUAL "androideabi")
|
||||
set(ANDROID 1)
|
||||
endif()
|
||||
include(CompilerRTUtils)
|
||||
|
||||
set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
|
@ -129,12 +85,25 @@ option(COMPILER_RT_EXTERNALIZE_DEBUGINFO
|
|||
# COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in.
|
||||
pythonize_bool(COMPILER_RT_DEBUG)
|
||||
|
||||
include(config-ix)
|
||||
|
||||
if(APPLE AND SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.9")
|
||||
# Mac OS X prior to 10.9 had problems with exporting symbols from
|
||||
# libc++/libc++abi.
|
||||
set(use_cxxabi_default OFF)
|
||||
elseif(MSVC)
|
||||
set(use_cxxabi_default OFF)
|
||||
else()
|
||||
set(use_cxxabi_default ON)
|
||||
endif()
|
||||
|
||||
option(SANITIZER_CAN_USE_CXXABI "Sanitizers can use cxxabi" ${use_cxxabi_default})
|
||||
pythonize_bool(SANITIZER_CAN_USE_CXXABI)
|
||||
|
||||
#================================
|
||||
# Setup Compiler Flags
|
||||
#================================
|
||||
|
||||
include(config-ix)
|
||||
|
||||
if(MSVC)
|
||||
# Override any existing /W flags with /W4. This is what LLVM does. Failing to
|
||||
# remove other /W[0-4] flags will result in a warning about overriding a
|
||||
|
|
@ -160,7 +129,9 @@ if(NOT COMPILER_RT_HAS_FUNC_SYMBOL)
|
|||
endif()
|
||||
|
||||
# Provide some common commmandline flags for Sanitizer runtimes.
|
||||
append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC SANITIZER_COMMON_CFLAGS)
|
||||
if(NOT WIN32)
|
||||
append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC SANITIZER_COMMON_CFLAGS)
|
||||
endif()
|
||||
append_list_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin SANITIZER_COMMON_CFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COMMON_CFLAGS)
|
||||
if(NOT COMPILER_RT_DEBUG)
|
||||
|
|
@ -194,7 +165,7 @@ if(MSVC)
|
|||
# VS 2015 (version 1900) added support for thread safe static initialization.
|
||||
# However, ASan interceptors run before CRT initialization, which causes the
|
||||
# new thread safe code to crash. Disable this feature for now.
|
||||
if (MSVC_VERSION GREATER 1899)
|
||||
if (MSVC_VERSION GREATER 1899 OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS /Zc:threadSafeInit-)
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -224,8 +195,20 @@ if(COMPILER_RT_HAS_GLINE_TABLES_ONLY_FLAG AND NOT COMPILER_RT_DEBUG)
|
|||
list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only)
|
||||
elseif(COMPILER_RT_HAS_G_FLAG)
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -g)
|
||||
elseif(COMPILER_RT_HAS_Zi_FLAG)
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS /Zi)
|
||||
elseif(MSVC)
|
||||
# Use /Z7 instead of /Zi for the asan runtime. This avoids the LNK4099
|
||||
# warning from the MS linker complaining that it can't find the 'vc140.pdb'
|
||||
# file used by our object library compilations.
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS /Z7)
|
||||
llvm_replace_compiler_option(CMAKE_CXX_FLAGS "/Z[i7I]" "/Z7")
|
||||
llvm_replace_compiler_option(CMAKE_CXX_FLAGS_DEBUG "/Z[i7I]" "/Z7")
|
||||
llvm_replace_compiler_option(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Z[i7I]" "/Z7")
|
||||
endif()
|
||||
|
||||
if(LLVM_ENABLE_MODULES)
|
||||
# Sanitizers cannot be built with -fmodules. The interceptors intentionally
|
||||
# don't include system headers, which is incompatible with modules.
|
||||
list(APPEND SANITIZER_COMMON_CFLAGS -fno-modules)
|
||||
endif()
|
||||
|
||||
# Turn off several warnings.
|
||||
|
|
@ -242,17 +225,6 @@ append_list_if(COMPILER_RT_HAS_WD4800_FLAG /wd4800 SANITIZER_COMMON_CFLAGS)
|
|||
# Warnings to turn off for all libraries, not just sanitizers.
|
||||
append_string_if(COMPILER_RT_HAS_WUNUSED_PARAMETER_FLAG -Wno-unused-parameter CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
|
||||
|
||||
if(APPLE AND SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.9")
|
||||
# Mac OS X prior to 10.9 had problems with exporting symbols from
|
||||
# libc++/libc++abi.
|
||||
set(SANITIZER_CAN_USE_CXXABI FALSE)
|
||||
elseif(MSVC)
|
||||
set(SANITIZER_CAN_USE_CXXABI FALSE)
|
||||
else()
|
||||
set(SANITIZER_CAN_USE_CXXABI TRUE)
|
||||
endif()
|
||||
pythonize_bool(SANITIZER_CAN_USE_CXXABI)
|
||||
|
||||
add_subdirectory(include)
|
||||
|
||||
set(COMPILER_RT_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx)
|
||||
|
|
|
|||
275
Makefile
275
Makefile
|
|
@ -1,275 +0,0 @@
|
|||
SubDirs := lib
|
||||
|
||||
# Set default rule before anything else.
|
||||
all: help
|
||||
|
||||
include make/config.mk
|
||||
include make/util.mk
|
||||
# If SRCROOT is defined, assume we are doing an Apple style build. We should be
|
||||
# able to use RC_XBS for this but that is unused during "make installsrc".
|
||||
ifdef SRCROOT
|
||||
include make/AppleBI.mk
|
||||
endif
|
||||
|
||||
# Make sure we don't build with a missing ProjObjRoot.
|
||||
ifeq ($(ProjObjRoot),)
|
||||
$(error Refusing to build with empty ProjObjRoot variable)
|
||||
endif
|
||||
|
||||
##############
|
||||
|
||||
###
|
||||
# Rules
|
||||
|
||||
###
|
||||
# Top level targets
|
||||
|
||||
# FIXME: Document the available subtargets.
|
||||
help:
|
||||
@echo "usage: make [{VARIABLE=VALUE}*] target"
|
||||
@echo
|
||||
@echo "User variables:"
|
||||
@echo " VERBOSE=1: Use to show all commands [default=0]"
|
||||
@echo
|
||||
@echo "Available targets:"
|
||||
@echo " <platform name>: build the libraries for 'platform'"
|
||||
@echo " clean: clean all configurations"
|
||||
@echo " test: run unit tests"
|
||||
@echo
|
||||
@echo " info-platforms: list available platforms"
|
||||
@echo " help-devel: print additional help for developers"
|
||||
@echo
|
||||
|
||||
help-devel: help
|
||||
@echo "Development targets:"
|
||||
@echo " <platform name>-<config name>:"
|
||||
@echo " build the libraries for a single platform config"
|
||||
@echo " <platform name>-<config name>-<arch name>:"
|
||||
@echo " build the libraries for a single config and arch"
|
||||
@echo " info-functions: list available compiler-rt functions"
|
||||
@echo " help-hidden: print help for Makefile debugging"
|
||||
@echo
|
||||
|
||||
help-hidden: help-devel
|
||||
@echo "Debugging variables:"
|
||||
@echo " DEBUGMAKE=1: enable some Makefile logging [default=]"
|
||||
@echo " =2: enable more Makefile logging"
|
||||
@echo
|
||||
@echo "Debugging targets:"
|
||||
@echo " make-print-FOO: print information on the variable 'FOO'"
|
||||
@echo
|
||||
|
||||
info-functions:
|
||||
@echo "compiler-rt Available Functions"
|
||||
@echo
|
||||
@echo "All Functions: $(AvailableFunctions)"
|
||||
@$(foreach fn,$(AvailableFunctions),\
|
||||
printf " %-20s - available in (%s)\n" $(fn)\
|
||||
"$(foreach key,$(AvailableIn.$(fn)),$($(key).Dir))";)
|
||||
|
||||
info-platforms:
|
||||
@echo "compiler-rt Available Platforms"
|
||||
@echo
|
||||
@echo "Platforms:"
|
||||
@$(foreach key,$(PlatformKeys),\
|
||||
printf " %s - from '%s'\n" $($(key).Name) $($(key).Path);\
|
||||
printf " %s\n" "$($(key).Description)";\
|
||||
printf " Configurations: %s\n\n" "$($(key).Configs)";)
|
||||
|
||||
# Provide default clean target which is extended by other templates.
|
||||
.PHONY: clean
|
||||
clean::
|
||||
|
||||
# Test
|
||||
.PHONY: test
|
||||
test:
|
||||
cd test/Unit && ./test
|
||||
|
||||
###
|
||||
# Directory handling magic.
|
||||
|
||||
# Create directories as needed, and timestamp their creation.
|
||||
%/.dir:
|
||||
$(Summary) " MKDIR: $*"
|
||||
$(Verb) $(MKDIR) $* > /dev/null
|
||||
$(Verb) echo 'Created.' > $@
|
||||
|
||||
# Remove directories
|
||||
%/.remove:
|
||||
$(Verb) $(RM) -r $*
|
||||
|
||||
###
|
||||
# Include child makefile fragments
|
||||
|
||||
Dir := .
|
||||
include make/subdir.mk
|
||||
include make/lib_info.mk
|
||||
include make/lib_util.mk
|
||||
include make/lib_platforms.mk
|
||||
|
||||
###
|
||||
# Define Platform Rules
|
||||
|
||||
define PerPlatform_template
|
||||
$(call Set,Tmp.Key,$(1))
|
||||
$(call Set,Tmp.Name,$($(Tmp.Key).Name))
|
||||
$(call Set,Tmp.Configs,$($(Tmp.Key).Configs))
|
||||
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name))
|
||||
|
||||
# Top-Level Platform Target
|
||||
$(Tmp.Name):: $(Tmp.Configs:%=$(Tmp.Name)-%)
|
||||
.PHONY: $(Tmp.Name)
|
||||
|
||||
clean::
|
||||
$(Verb) rm -rf $(Tmp.ObjPath)
|
||||
|
||||
# Per-Config Libraries
|
||||
$(foreach config,$(Tmp.Configs),\
|
||||
$(call PerPlatformConfig_template,$(config)))
|
||||
endef
|
||||
|
||||
define PerPlatformConfig_template
|
||||
$(call Set,Tmp.Config,$(1))
|
||||
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config))
|
||||
$(call Set,Tmp.SHARED_LIBRARY,$(strip \
|
||||
$(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.SHARED_LIBRARY_SUFFIX,$(strip \
|
||||
$(call GetCNAVar,SHARED_LIBRARY_SUFFIX,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
|
||||
# Compute the library suffix.
|
||||
$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)),
|
||||
$(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)),
|
||||
$(call Set,Tmp.LibrarySuffix,a))
|
||||
|
||||
# Compute the archs to build, depending on whether this is a universal build or
|
||||
# not.
|
||||
$(call Set,Tmp.ArchsToBuild,\
|
||||
$(if $(call IsDefined,$(Tmp.Key).UniversalArchs),\
|
||||
$(strip \
|
||||
$(or $($(Tmp.Key).UniversalArchs.$(Tmp.Config)),\
|
||||
$($(Tmp.Key).UniversalArchs))),\
|
||||
$(call VarOrDefault,$(Tmp.Key).Arch.$(Tmp.Config),$($(Tmp.Key).Arch))))
|
||||
|
||||
# Copy or lipo to create the per-config library.
|
||||
$(call Set,Tmp.Inputs,$(Tmp.ArchsToBuild:%=$(Tmp.ObjPath)/%/libcompiler_rt.$(Tmp.LibrarySuffix)))
|
||||
$(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix): $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " FINAL-ARCHIVE: $(Tmp.Name)/$(Tmp.Config): $$@"
|
||||
-$(Verb) $(RM) $$@
|
||||
$(if $(call streq,1,$(words $(Tmp.ArchsToBuild))), \
|
||||
$(Verb) $(CP) $(Tmp.Inputs) $$@, \
|
||||
$(Verb) $(LIPO) -create -output $$@ $(Tmp.Inputs))
|
||||
.PRECIOUS: $(Tmp.ObjPath)/.dir
|
||||
|
||||
# Per-Config Targets
|
||||
$(Tmp.Name)-$(Tmp.Config):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix)
|
||||
.PHONY: $(Tmp.Name)-$(Tmp.Config)
|
||||
|
||||
# Per-Config-Arch Libraries
|
||||
$(foreach arch,$(Tmp.ArchsToBuild),\
|
||||
$(call PerPlatformConfigArch_template,$(arch)))
|
||||
endef
|
||||
|
||||
define PerPlatformConfigArch_template
|
||||
$(call Set,Tmp.Arch,$(1))
|
||||
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch))
|
||||
$(call Set,Tmp.Functions,$(strip \
|
||||
$(AlwaysRequiredModules) \
|
||||
$(call GetCNAVar,FUNCTIONS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.Optimized,$(strip \
|
||||
$(call GetCNAVar,OPTIMIZED,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.AR,$(strip \
|
||||
$(call GetCNAVar,AR,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.ARFLAGS,$(strip \
|
||||
$(call GetCNAVar,ARFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.CC,$(strip \
|
||||
$(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.LDFLAGS,$(strip \
|
||||
$(call GetCNAVar,LDFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.RANLIB,$(strip \
|
||||
$(call GetCNAVar,RANLIB,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.RANLIBFLAGS,$(strip \
|
||||
$(call GetCNAVar,RANLIBFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.SHARED_LIBRARY,$(strip \
|
||||
$(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
|
||||
# Compute the library suffix.
|
||||
$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)),
|
||||
$(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)),
|
||||
$(call Set,Tmp.LibrarySuffix,a))
|
||||
|
||||
# Compute the object inputs for this library.
|
||||
$(call Set,Tmp.Inputs,\
|
||||
$(foreach fn,$(sort $(Tmp.Functions)),\
|
||||
$(call Set,Tmp.FnDir,\
|
||||
$(call SelectFunctionDir,$(Tmp.Config),$(Tmp.Arch),$(fn),$(Tmp.Optimized)))\
|
||||
$(Tmp.ObjPath)/$(Tmp.FnDir)/$(fn).o))
|
||||
$(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " ARCHIVE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
|
||||
-$(Verb) $(RM) $$@
|
||||
$(Verb) $(Tmp.AR) $(Tmp.ARFLAGS) $$@ $(Tmp.Inputs)
|
||||
$(Verb) $(Tmp.RANLIB) $(Tmp.RANLIBFLAGS) $$@
|
||||
$(Tmp.ObjPath)/libcompiler_rt.dylib: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " DYLIB: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
|
||||
$(Verb) $(Tmp.CC) -arch $(Tmp.Arch) -dynamiclib -o $$@ \
|
||||
$(Tmp.Inputs) $(Tmp.LDFLAGS)
|
||||
$(Tmp.ObjPath)/libcompiler_rt.so: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " SO: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
|
||||
$(Verb) $(Tmp.CC) -shared -o $$@ \
|
||||
$(Tmp.Inputs) $(Tmp.LDFLAGS)
|
||||
.PRECIOUS: $(Tmp.ObjPath)/.dir
|
||||
|
||||
# Per-Config-Arch Targets
|
||||
$(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix)
|
||||
.PHONY: $(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch)
|
||||
|
||||
# Per-Config-Arch-SubDir Objects
|
||||
$(foreach key,$(SubDirKeys),\
|
||||
$(call PerPlatformConfigArchSubDir_template,$(key)))
|
||||
endef
|
||||
|
||||
define PerPlatformConfigArchSubDir_template
|
||||
$(call Set,Tmp.SubDirKey,$(1))
|
||||
$(call Set,Tmp.SubDir,$($(Tmp.SubDirKey).Dir))
|
||||
$(call Set,Tmp.SrcPath,$(ProjSrcRoot)/$(Tmp.SubDir))
|
||||
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch)/$(Tmp.SubDirKey))
|
||||
$(call Set,Tmp.Dependencies,$($(Tmp.SubDirKey).Dependencies))
|
||||
$(call Set,Tmp.CC,$(strip \
|
||||
$(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.KERNEL_USE,$(strip \
|
||||
$(call GetCNAVar,KERNEL_USE,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.VISIBILITY_HIDDEN,$(strip \
|
||||
$(call GetCNAVar,VISIBILITY_HIDDEN,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
$(call Set,Tmp.CFLAGS,$(strip \
|
||||
$(if $(call IsDefined,$(Tmp.Key).UniversalArchs),-arch $(Tmp.Arch),)\
|
||||
$(if $(call streq,$(Tmp.VISIBILITY_HIDDEN),1),\
|
||||
-fvisibility=hidden -DVISIBILITY_HIDDEN,)\
|
||||
$(if $(call streq,$(Tmp.KERNEL_USE),1),\
|
||||
-mkernel -DKERNEL_USE,)\
|
||||
$(call GetCNAVar,CFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
|
||||
|
||||
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.s $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " ASSEMBLE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
|
||||
$(Verb) $(Tmp.CC) $(COMMON_ASMFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
|
||||
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.S $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " ASSEMBLE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
|
||||
$(Verb) $(Tmp.CC) $(COMMON_ASMFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
|
||||
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.c $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
|
||||
$(Verb) $(Tmp.CC) $(COMMON_CFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
|
||||
$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.cc $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir
|
||||
$(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<"
|
||||
$(Verb) $(Tmp.CC) $(COMMON_CXXFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$<
|
||||
.PRECIOUS: $(Tmp.ObjPath)/.dir
|
||||
|
||||
endef
|
||||
|
||||
# Run templates.
|
||||
$(foreach key,$(PlatformKeys),\
|
||||
$(eval $(call PerPlatform_template,$(key))))
|
||||
|
||||
###
|
||||
|
||||
ifneq ($(DEBUGMAKE),)
|
||||
$(info MAKE: Done processing Makefile)
|
||||
$(info )
|
||||
endif
|
||||
|
|
@ -77,6 +77,15 @@ macro(format_object_libs output suffix)
|
|||
endforeach()
|
||||
endmacro()
|
||||
|
||||
function(add_compiler_rt_component name)
|
||||
add_custom_target(${name})
|
||||
set_target_properties(${name} PROPERTIES FOLDER "Compiler-RT Misc")
|
||||
if(COMMAND runtime_register_component)
|
||||
runtime_register_component(${name})
|
||||
endif()
|
||||
add_dependencies(compiler-rt ${name})
|
||||
endfunction()
|
||||
|
||||
# Adds static or shared runtime for a list of architectures and operating
|
||||
# systems and puts it in the proper directory in the build and install trees.
|
||||
# add_compiler_rt_runtime(<name>
|
||||
|
|
@ -164,6 +173,7 @@ function(add_compiler_rt_runtime name type)
|
|||
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
|
||||
set_target_properties(install-${LIB_PARENT_TARGET} PROPERTIES
|
||||
FOLDER "Compiler-RT Misc")
|
||||
add_dependencies(install-compiler-rt install-${LIB_PARENT_TARGET})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -185,8 +195,14 @@ function(add_compiler_rt_runtime name type)
|
|||
set_target_properties(${libname} PROPERTIES
|
||||
OUTPUT_NAME ${output_name_${libname}})
|
||||
set_target_properties(${libname} PROPERTIES FOLDER "Compiler-RT Runtime")
|
||||
if(LIB_LINK_LIBS AND ${type} STREQUAL "SHARED")
|
||||
target_link_libraries(${libname} ${LIB_LINK_LIBS})
|
||||
if(${type} STREQUAL "SHARED")
|
||||
if(LIB_LINK_LIBS)
|
||||
target_link_libraries(${libname} ${LIB_LINK_LIBS})
|
||||
endif()
|
||||
if(WIN32 AND NOT CYGWIN AND NOT MINGW)
|
||||
set_target_properties(${libname} PROPERTIES IMPORT_PREFIX "")
|
||||
set_target_properties(${libname} PROPERTIES IMPORT_SUFFIX ".lib")
|
||||
endif()
|
||||
endif()
|
||||
install(TARGETS ${libname}
|
||||
ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}
|
||||
|
|
@ -253,12 +269,6 @@ if(MSVC)
|
|||
|
||||
# gtest use a lot of stuff marked as deprecated on Windows.
|
||||
list(APPEND COMPILER_RT_GTEST_CFLAGS -Wno-deprecated-declarations)
|
||||
|
||||
# Visual Studio 2012 only supports up to 8 template parameters in
|
||||
# std::tr1::tuple by default, but gtest requires 10
|
||||
if(MSVC_VERSION EQUAL 1700)
|
||||
list(APPEND COMPILER_RT_GTEST_CFLAGS -D_VARIADIC_MAX=10)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Link objects into a single executable with COMPILER_RT_TEST_COMPILER,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
include(CMakeCheckCompilerFlagCommonPatterns)
|
||||
|
||||
# This function takes an OS and a list of architectures and identifies the
|
||||
# subset of the architectures list that the installed toolchain can target.
|
||||
|
|
@ -10,7 +11,13 @@ function(try_compile_only output)
|
|||
file(WRITE ${SIMPLE_C} "${ARG_SOURCE}\n")
|
||||
string(REGEX MATCHALL "<[A-Za-z0-9_]*>" substitutions
|
||||
${CMAKE_C_COMPILE_OBJECT})
|
||||
string(REPLACE ";" " " extra_flags "${ARG_FLAGS}")
|
||||
|
||||
set(TRY_COMPILE_FLAGS "${ARG_FLAGS}")
|
||||
if(CMAKE_C_COMPILER_ID MATCHES Clang AND CMAKE_C_COMPILER_TARGET)
|
||||
list(APPEND TRY_COMPILE_FLAGS "-target ${CMAKE_C_COMPILER_TARGET}")
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" " " extra_flags "${TRY_COMPILE_FLAGS}")
|
||||
|
||||
set(test_compile_command "${CMAKE_C_COMPILE_OBJECT}")
|
||||
foreach(substitution ${substitutions})
|
||||
|
|
@ -41,7 +48,19 @@ function(try_compile_only output)
|
|||
OUTPUT_VARIABLE TEST_OUTPUT
|
||||
ERROR_VARIABLE TEST_ERROR
|
||||
)
|
||||
if(result EQUAL 0)
|
||||
|
||||
CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckCCompilerFlag_COMMON_PATTERNS)
|
||||
set(ERRORS_FOUND OFF)
|
||||
foreach(var ${_CheckCCompilerFlag_COMMON_PATTERNS})
|
||||
if("${var}" STREQUAL "FAIL_REGEX")
|
||||
continue()
|
||||
endif()
|
||||
if("${TEST_ERROR}" MATCHES "${var}" OR "${TEST_OUTPUT}" MATCHES "${var}")
|
||||
set(ERRORS_FOUND ON)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(result EQUAL 0 AND NOT ERRORS_FOUND)
|
||||
set(${output} True PARENT_SCOPE)
|
||||
else()
|
||||
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@ function(find_darwin_sdk_dir var sdk_name)
|
|||
# Let's first try the internal SDK, otherwise use the public SDK.
|
||||
execute_process(
|
||||
COMMAND xcodebuild -version -sdk ${sdk_name}.internal Path
|
||||
RESULT_VARIABLE result_process
|
||||
OUTPUT_VARIABLE var_internal
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_FILE /dev/null
|
||||
)
|
||||
if("" STREQUAL "${var_internal}")
|
||||
if((NOT result_process EQUAL 0) OR "" STREQUAL "${var_internal}")
|
||||
execute_process(
|
||||
COMMAND xcodebuild -version -sdk ${sdk_name} Path
|
||||
RESULT_VARIABLE result_process
|
||||
OUTPUT_VARIABLE var_internal
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_FILE /dev/null
|
||||
|
|
@ -21,7 +23,9 @@ function(find_darwin_sdk_dir var sdk_name)
|
|||
else()
|
||||
set(${var}_INTERNAL ${var_internal} PARENT_SCOPE)
|
||||
endif()
|
||||
set(${var} ${var_internal} PARENT_SCOPE)
|
||||
if(result_process EQUAL 0)
|
||||
set(${var} ${var_internal} PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# There isn't a clear mapping of what architectures are supported with a given
|
||||
|
|
@ -256,30 +260,6 @@ function(darwin_filter_builtin_sources output_var exclude_or_include excluded_li
|
|||
set(${output_var} ${intermediate} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(darwin_add_eprintf_library)
|
||||
cmake_parse_arguments(LIB
|
||||
""
|
||||
""
|
||||
"CFLAGS"
|
||||
${ARGN})
|
||||
|
||||
add_library(clang_rt.eprintf STATIC eprintf.c)
|
||||
set_target_compile_flags(clang_rt.eprintf
|
||||
-isysroot ${DARWIN_osx_SYSROOT}
|
||||
${DARWIN_osx_BUILTIN_MIN_VER_FLAG}
|
||||
-arch i386
|
||||
${LIB_CFLAGS})
|
||||
set_target_properties(clang_rt.eprintf PROPERTIES
|
||||
OUTPUT_NAME clang_rt.eprintf${COMPILER_RT_OS_SUFFIX})
|
||||
set_target_properties(clang_rt.eprintf PROPERTIES
|
||||
OSX_ARCHITECTURES i386)
|
||||
add_dependencies(builtins clang_rt.eprintf)
|
||||
set_target_properties(clang_rt.eprintf PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR})
|
||||
install(TARGETS clang_rt.eprintf
|
||||
ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR})
|
||||
endfunction()
|
||||
|
||||
# Generates builtin libraries for all operating systems specified in ARGN. Each
|
||||
# OS library is constructed by lipo-ing together single-architecture libraries.
|
||||
macro(darwin_add_builtin_libraries)
|
||||
|
|
@ -350,8 +330,6 @@ macro(darwin_add_builtin_libraries)
|
|||
endif()
|
||||
endforeach()
|
||||
|
||||
darwin_add_eprintf_library(CFLAGS ${CFLAGS})
|
||||
|
||||
# We put the x86 sim slices into the archives for their base OS
|
||||
foreach (os ${ARGN})
|
||||
if(NOT ${os} MATCHES ".*sim$")
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ macro(append_string_if condition value)
|
|||
endmacro()
|
||||
|
||||
macro(append_rtti_flag polarity list)
|
||||
if(polarity)
|
||||
if(${polarity})
|
||||
append_list_if(COMPILER_RT_HAS_FRTTI_FLAG -frtti ${list})
|
||||
append_list_if(COMPILER_RT_HAS_GR_FLAG /GR ${list})
|
||||
else()
|
||||
|
|
@ -76,6 +76,18 @@ macro(list_intersect output input1 input2)
|
|||
endforeach()
|
||||
endmacro()
|
||||
|
||||
function(list_replace input_list old new)
|
||||
set(replaced_list)
|
||||
foreach(item ${${input_list}})
|
||||
if(${item} STREQUAL ${old})
|
||||
list(APPEND replaced_list ${new})
|
||||
else()
|
||||
list(APPEND replaced_list ${item})
|
||||
endif()
|
||||
endforeach()
|
||||
set(${input_list} "${replaced_list}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Takes ${ARGN} and puts only supported architectures in @out_var list.
|
||||
function(filter_available_targets out_var)
|
||||
set(archs ${${out_var}})
|
||||
|
|
@ -88,6 +100,13 @@ function(filter_available_targets out_var)
|
|||
set(${out_var} ${archs} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Add $arch as supported with no additional flags.
|
||||
macro(add_default_target_arch arch)
|
||||
set(TARGET_${arch}_CFLAGS "")
|
||||
set(CAN_TARGET_${arch} 1)
|
||||
list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
|
||||
endmacro()
|
||||
|
||||
function(check_compile_definition def argstring out_var)
|
||||
if("${def}" STREQUAL "")
|
||||
set(${out_var} TRUE PARENT_SCOPE)
|
||||
|
|
@ -113,20 +132,26 @@ macro(test_target_arch arch def)
|
|||
set(argstring "${argstring} ${arg}")
|
||||
endforeach()
|
||||
check_compile_definition("${def}" "${argstring}" HAS_${arch}_DEF)
|
||||
if(NOT HAS_${arch}_DEF)
|
||||
set(CAN_TARGET_${arch} FALSE)
|
||||
elseif(TEST_COMPILE_ONLY)
|
||||
try_compile_only(CAN_TARGET_${arch} ${TARGET_${arch}_CFLAGS})
|
||||
else()
|
||||
set(argstring "${CMAKE_EXE_LINKER_FLAGS} ${argstring}")
|
||||
try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE}
|
||||
COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}"
|
||||
OUTPUT_VARIABLE TARGET_${arch}_OUTPUT
|
||||
CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}")
|
||||
if(NOT DEFINED CAN_TARGET_${arch})
|
||||
if(NOT HAS_${arch}_DEF)
|
||||
set(CAN_TARGET_${arch} FALSE)
|
||||
elseif(TEST_COMPILE_ONLY)
|
||||
try_compile_only(CAN_TARGET_${arch} ${TARGET_${arch}_CFLAGS})
|
||||
else()
|
||||
set(argstring "${CMAKE_EXE_LINKER_FLAGS} ${argstring}")
|
||||
set(FLAG_NO_EXCEPTIONS "")
|
||||
if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG)
|
||||
set(FLAG_NO_EXCEPTIONS " -fno-exceptions ")
|
||||
endif()
|
||||
try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE}
|
||||
COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS} ${FLAG_NO_EXCEPTIONS}"
|
||||
OUTPUT_VARIABLE TARGET_${arch}_OUTPUT
|
||||
CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}")
|
||||
endif()
|
||||
endif()
|
||||
if(${CAN_TARGET_${arch}})
|
||||
list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
|
||||
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "${arch}" AND
|
||||
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" STREQUAL "${arch}" AND
|
||||
COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE)
|
||||
# Bail out if we cannot target the architecture we plan to test.
|
||||
message(FATAL_ERROR "Cannot compile for ${arch}:\n${TARGET_${arch}_OUTPUT}")
|
||||
|
|
@ -166,3 +191,73 @@ macro(detect_target_arch)
|
|||
add_default_target_arch(wasm64)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(load_llvm_config)
|
||||
if (NOT LLVM_CONFIG_PATH)
|
||||
find_program(LLVM_CONFIG_PATH "llvm-config"
|
||||
DOC "Path to llvm-config binary")
|
||||
if (NOT LLVM_CONFIG_PATH)
|
||||
message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
|
||||
endif()
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root"
|
||||
RESULT_VARIABLE HAD_ERROR
|
||||
OUTPUT_VARIABLE CONFIG_OUTPUT)
|
||||
if (HAD_ERROR)
|
||||
message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
|
||||
endif()
|
||||
string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
|
||||
list(GET CONFIG_OUTPUT 0 BINARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 2 LIBRARY_DIR)
|
||||
list(GET CONFIG_OUTPUT 3 MAIN_SRC_DIR)
|
||||
|
||||
set(LLVM_BINARY_DIR ${BINARY_DIR} CACHE PATH "Path to LLVM build tree")
|
||||
set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin")
|
||||
set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib")
|
||||
set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
|
||||
|
||||
# Make use of LLVM CMake modules.
|
||||
file(TO_CMAKE_PATH ${LLVM_BINARY_DIR} LLVM_BINARY_DIR_CMAKE_STYLE)
|
||||
set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
|
||||
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
|
||||
# Get some LLVM variables from LLVMConfig.
|
||||
include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
|
||||
|
||||
set(LLVM_LIBRARY_OUTPUT_INTDIR
|
||||
${LLVM_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
|
||||
endmacro()
|
||||
|
||||
macro(construct_compiler_rt_default_triple)
|
||||
if(COMPILER_RT_DEFAULT_TARGET_ONLY)
|
||||
if(DEFINED COMPILER_RT_DEFAULT_TARGET_TRIPLE)
|
||||
message(FATAL_ERROR "COMPILER_RT_DEFAULT_TARGET_TRIPLE isn't supported when building for default target only")
|
||||
endif()
|
||||
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${CMAKE_C_COMPILER_TARGET})
|
||||
else()
|
||||
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${TARGET_TRIPLE} CACHE STRING
|
||||
"Default triple for which compiler-rt runtimes will be built.")
|
||||
endif()
|
||||
|
||||
if(DEFINED COMPILER_RT_TEST_TARGET_TRIPLE)
|
||||
# Backwards compatibility: this variable used to be called
|
||||
# COMPILER_RT_TEST_TARGET_TRIPLE.
|
||||
set(COMPILER_RT_DEFAULT_TARGET_TRIPLE ${COMPILER_RT_TEST_TARGET_TRIPLE})
|
||||
endif()
|
||||
|
||||
string(REPLACE "-" ";" TARGET_TRIPLE_LIST ${COMPILER_RT_DEFAULT_TARGET_TRIPLE})
|
||||
list(GET TARGET_TRIPLE_LIST 0 COMPILER_RT_DEFAULT_TARGET_ARCH)
|
||||
list(GET TARGET_TRIPLE_LIST 1 COMPILER_RT_DEFAULT_TARGET_OS)
|
||||
list(LENGTH TARGET_TRIPLE_LIST TARGET_TRIPLE_LIST_LENGTH)
|
||||
if(TARGET_TRIPLE_LIST_LENGTH GREATER 2)
|
||||
list(GET TARGET_TRIPLE_LIST 2 COMPILER_RT_DEFAULT_TARGET_ABI)
|
||||
endif()
|
||||
# Determine if test target triple is specified explicitly, and doesn't match the
|
||||
# default.
|
||||
if(NOT COMPILER_RT_DEFAULT_TARGET_TRIPLE STREQUAL TARGET_TRIPLE)
|
||||
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE TRUE)
|
||||
else()
|
||||
set(COMPILER_RT_HAS_EXPLICIT_DEFAULT_TARGET_TRIPLE FALSE)
|
||||
endif()
|
||||
endmacro()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ check_include_file(unwind.h HAVE_UNWIND_H)
|
|||
|
||||
# Top level target used to build all compiler-rt libraries.
|
||||
add_custom_target(compiler-rt ALL)
|
||||
add_custom_target(install-compiler-rt)
|
||||
set_target_properties(compiler-rt PROPERTIES FOLDER "Compiler-RT Misc")
|
||||
|
||||
# Setting these variables from an LLVM build is sufficient that compiler-rt can
|
||||
|
|
@ -80,9 +81,20 @@ if(APPLE)
|
|||
set(OSX_SYSROOT_FLAG "-isysroot${OSX_SYSROOT}")
|
||||
endif()
|
||||
|
||||
option(COMPILER_RT_ENABLE_IOS "Enable building for iOS" Off)
|
||||
option(COMPILER_RT_ENABLE_IOS "Enable building for iOS" On)
|
||||
option(COMPILER_RT_ENABLE_WATCHOS "Enable building for watchOS - Experimental" Off)
|
||||
option(COMPILER_RT_ENABLE_TVOS "Enable building for tvOS - Experimental" Off)
|
||||
else()
|
||||
option(COMPILER_RT_DEFAULT_TARGET_ONLY "Build builtins only for the default target" Off)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT MINGW AND NOT CYGWIN)
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX_C "")
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "")
|
||||
set(CMAKE_STATIC_LIBRARY_PREFIX_C "")
|
||||
set(CMAKE_STATIC_LIBRARY_PREFIX_CXX "")
|
||||
set(CMAKE_STATIC_LIBRARY_SUFFIX_C ".lib")
|
||||
set(CMAKE_STATIC_LIBRARY_SUFFIX_CXX ".lib")
|
||||
endif()
|
||||
|
||||
macro(test_targets)
|
||||
|
|
@ -117,7 +129,9 @@ macro(test_targets)
|
|||
detect_target_arch()
|
||||
set(COMPILER_RT_OS_SUFFIX "-android")
|
||||
elseif(NOT APPLE) # Supported archs for Apple platforms are generated later
|
||||
if("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "i[2-6]86|x86|amd64")
|
||||
if(COMPILER_RT_DEFAULT_TARGET_ONLY)
|
||||
add_default_target_arch(${COMPILER_RT_DEFAULT_TARGET_ARCH})
|
||||
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "i[2-6]86|x86|amd64")
|
||||
if(NOT MSVC)
|
||||
test_target_arch(x86_64 "" "-m64")
|
||||
# FIXME: We build runtimes for both i686 and i386, as "clang -m32" may
|
||||
|
|
@ -153,8 +167,12 @@ macro(test_targets)
|
|||
test_target_arch(mips "" "-mips32r2" "--target=mips-linux-gnu")
|
||||
test_target_arch(mips64 "" "-mips64r2" "--target=mips64-linux-gnu" "-mabi=64")
|
||||
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "arm")
|
||||
test_target_arch(arm "" "-march=armv7-a" "-mfloat-abi=soft")
|
||||
test_target_arch(armhf "" "-march=armv7-a" "-mfloat-abi=hard")
|
||||
if(WIN32)
|
||||
test_target_arch(arm "" "" "")
|
||||
else()
|
||||
test_target_arch(arm "" "-march=armv7-a" "-mfloat-abi=soft")
|
||||
test_target_arch(armhf "" "-march=armv7-a" "-mfloat-abi=hard")
|
||||
endif()
|
||||
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "aarch32")
|
||||
test_target_arch(aarch32 "" "-march=armv8-a")
|
||||
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "aarch64")
|
||||
|
|
|
|||
|
|
@ -4,18 +4,17 @@ include(CheckCSourceCompiles)
|
|||
# Make all the tests only check the compiler
|
||||
set(TEST_COMPILE_ONLY On)
|
||||
|
||||
# Check host compiler support for certain flags
|
||||
builtin_check_c_compiler_flag(-fPIC COMPILER_RT_HAS_FPIC_FLAG)
|
||||
builtin_check_c_compiler_flag(-fPIE COMPILER_RT_HAS_FPIE_FLAG)
|
||||
builtin_check_c_compiler_flag(-fno-builtin COMPILER_RT_HAS_FNO_BUILTIN_FLAG)
|
||||
builtin_check_c_compiler_flag(-std=c99 COMPILER_RT_HAS_STD_C99_FLAG)
|
||||
builtin_check_c_compiler_flag(-std=c11 COMPILER_RT_HAS_STD_C11_FLAG)
|
||||
builtin_check_c_compiler_flag(-fvisibility=hidden COMPILER_RT_HAS_VISIBILITY_HIDDEN_FLAG)
|
||||
builtin_check_c_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG)
|
||||
builtin_check_c_compiler_flag(-ffreestanding COMPILER_RT_HAS_FREESTANDING_FLAG)
|
||||
builtin_check_c_compiler_flag(-mfloat-abi=soft COMPILER_RT_HAS_FLOAT_ABI_SOFT_FLAG)
|
||||
builtin_check_c_compiler_flag(-mfloat-abi=hard COMPILER_RT_HAS_FLOAT_ABI_HARD_FLAG)
|
||||
builtin_check_c_compiler_flag(-static COMPILER_RT_HAS_STATIC_FLAG)
|
||||
builtin_check_c_compiler_flag(-fxray-instrument COMPILER_RT_HAS_XRAY_COMPILER_FLAG)
|
||||
|
||||
builtin_check_c_compiler_source(COMPILER_RT_SUPPORTS_ATOMIC_KEYWORD
|
||||
builtin_check_c_compiler_source(COMPILER_RT_HAS_ATOMIC_KEYWORD
|
||||
"
|
||||
int foo(int x, int y) {
|
||||
_Atomic int result = x * y;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ check_cxx_compiler_flag(-std=c++11 COMPILER_RT_HAS_STD_CXX11_FLAG)
|
|||
check_cxx_compiler_flag(-ftls-model=initial-exec COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC)
|
||||
check_cxx_compiler_flag(-fno-lto COMPILER_RT_HAS_FNO_LTO_FLAG)
|
||||
check_cxx_compiler_flag("-Werror -msse3" COMPILER_RT_HAS_MSSE3_FLAG)
|
||||
check_cxx_compiler_flag("-Werror -msse4.2" COMPILER_RT_HAS_MSSE4_2_FLAG)
|
||||
check_cxx_compiler_flag(--sysroot=. COMPILER_RT_HAS_SYSROOT_FLAG)
|
||||
|
||||
if(NOT WIN32 AND NOT CYGWIN)
|
||||
|
|
@ -93,14 +94,7 @@ set(COMPILER_RT_SUPPORTED_ARCH)
|
|||
# runtime libraries supported by our current compilers cross-compiling
|
||||
# abilities.
|
||||
set(SIMPLE_SOURCE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple.cc)
|
||||
file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\n#include <limits>\nint main() {}\n")
|
||||
|
||||
# Add $arch as supported with no additional flags.
|
||||
macro(add_default_target_arch arch)
|
||||
set(TARGET_${arch}_CFLAGS "")
|
||||
set(CAN_TARGET_${arch} 1)
|
||||
list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
|
||||
endmacro()
|
||||
file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\n#include <stdio.h>\nint main() { printf(\"hello, world\"); }\n")
|
||||
|
||||
# Detect whether the current target platform is 32-bit or 64-bit, and setup
|
||||
# the correct commandline flags needed to attempt to target 32-bit and 64-bit.
|
||||
|
|
@ -128,6 +122,24 @@ function(get_target_flags_for_arch arch out_var)
|
|||
endif()
|
||||
endfunction()
|
||||
|
||||
# Returns a compiler and CFLAGS that should be used to run tests for the
|
||||
# specific architecture. When cross-compiling, this is controled via
|
||||
# COMPILER_RT_TEST_COMPILER and COMPILER_RT_TEST_COMPILER_CFLAGS.
|
||||
macro(get_test_cc_for_arch arch cc_out cflags_out)
|
||||
if(ANDROID OR ${arch} MATCHES "arm|aarch64")
|
||||
# This is only true if we are cross-compiling.
|
||||
# Build all tests with host compiler and use host tools.
|
||||
set(${cc_out} ${COMPILER_RT_TEST_COMPILER})
|
||||
set(${cflags_out} ${COMPILER_RT_TEST_COMPILER_CFLAGS})
|
||||
else()
|
||||
get_target_flags_for_arch(${arch} ${cflags_out})
|
||||
if(APPLE)
|
||||
list(APPEND ${cflags_out} ${DARWIN_osx_CFLAGS})
|
||||
endif()
|
||||
string(REPLACE ";" " " ${cflags_out} "${${cflags_out}}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
set(ARM64 aarch64)
|
||||
set(ARM32 arm armhf)
|
||||
set(X86 i386 i686)
|
||||
|
|
@ -148,19 +160,20 @@ endif()
|
|||
set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64}
|
||||
${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${S390X})
|
||||
set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
|
||||
${MIPS32} ${MIPS64} ${PPC64})
|
||||
${MIPS32} ${MIPS64} ${PPC64} ${S390X})
|
||||
set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
|
||||
set(ALL_LSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
|
||||
set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64})
|
||||
set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC64}
|
||||
${MIPS32} ${MIPS64})
|
||||
${MIPS32} ${MIPS64} ${S390X})
|
||||
set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64})
|
||||
set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
|
||||
${MIPS32} ${MIPS64} ${PPC64} ${S390X})
|
||||
set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${MIPS32} ${MIPS64})
|
||||
set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64})
|
||||
set(ALL_ESAN_SUPPORTED_ARCH ${X86_64})
|
||||
set(ALL_SCUDO_SUPPORTED_ARCH ${X86_64})
|
||||
set(ALL_ESAN_SUPPORTED_ARCH ${X86_64} ${MIPS64})
|
||||
set(ALL_SCUDO_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32})
|
||||
set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64})
|
||||
|
||||
if(APPLE)
|
||||
include(CompilerRTDarwinUtils)
|
||||
|
|
@ -173,6 +186,14 @@ if(APPLE)
|
|||
find_darwin_sdk_dir(DARWIN_tvossim_SYSROOT appletvsimulator)
|
||||
find_darwin_sdk_dir(DARWIN_tvos_SYSROOT appletvos)
|
||||
|
||||
if(NOT DARWIN_osx_SYSROOT)
|
||||
if(EXISTS /usr/include)
|
||||
set(DARWIN_osx_SYSROOT /)
|
||||
else()
|
||||
message(ERROR "Could not detect OS X Sysroot. Either install Xcode or the Apple Command Line Tools")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_ENABLE_IOS)
|
||||
list(APPEND DARWIN_EMBEDDED_PLATFORMS ios)
|
||||
set(DARWIN_ios_MIN_VER_FLAG -miphoneos-version-min)
|
||||
|
|
@ -208,7 +229,11 @@ if(APPLE)
|
|||
set(SANITIZER_MIN_OSX_VERSION 10.9)
|
||||
endif()
|
||||
if(SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.7")
|
||||
message(FATAL_ERROR "Too old OS X version: ${SANITIZER_MIN_OSX_VERSION}")
|
||||
message(FATAL_ERROR "macOS deployment target '${SANITIZER_MIN_OSX_VERSION}' is too old.")
|
||||
endif()
|
||||
if(SANITIZER_MIN_OSX_VERSION VERSION_GREATER "10.9")
|
||||
message(WARNING "macOS deployment target '${SANITIZER_MIN_OSX_VERSION}' is too new, setting to '10.9' instead.")
|
||||
set(SANITIZER_MIN_OSX_VERSION 10.9)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -268,7 +293,7 @@ if(APPLE)
|
|||
DARWIN_${platform}sim_ARCHS
|
||||
${toolchain_arches})
|
||||
message(STATUS "${platform} Simulator supported arches: ${DARWIN_${platform}sim_ARCHS}")
|
||||
if(DARWIN_${platform}_ARCHS)
|
||||
if(DARWIN_${platform}sim_ARCHS)
|
||||
list(APPEND SANITIZER_COMMON_SUPPORTED_OS ${platform}sim)
|
||||
list(APPEND PROFILE_SUPPORTED_OS ${platform}sim)
|
||||
if(DARWIN_${platform}_SYSROOT_INTERNAL)
|
||||
|
|
@ -350,6 +375,9 @@ if(APPLE)
|
|||
list_intersect(SCUDO_SUPPORTED_ARCH
|
||||
ALL_SCUDO_SUPPORTED_ARCH
|
||||
SANITIZER_COMMON_SUPPORTED_ARCH)
|
||||
list_intersect(XRAY_SUPPORTED_ARCH
|
||||
ALL_XRAY_SUPPORTED_ARCH
|
||||
SANITIZER_COMMON_SUPPORTED_ARCH)
|
||||
else()
|
||||
# Architectures supported by compiler-rt libraries.
|
||||
filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH
|
||||
|
|
@ -371,8 +399,8 @@ else()
|
|||
${ALL_SAFESTACK_SUPPORTED_ARCH})
|
||||
filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH})
|
||||
filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH})
|
||||
filter_available_targets(SCUDO_SUPPORTED_ARCH
|
||||
${ALL_SCUDO_SUPPORTED_ARCH})
|
||||
filter_available_targets(SCUDO_SUPPORTED_ARCH ${ALL_SCUDO_SUPPORTED_ARCH})
|
||||
filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH})
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
|
|
@ -387,6 +415,11 @@ else()
|
|||
set(CAN_SYMBOLIZE 1)
|
||||
endif()
|
||||
|
||||
find_program(GOLD_EXECUTABLE NAMES ${LLVM_DEFAULT_TARGET_TRIPLE}-ld.gold ld.gold ${LLVM_DEFAULT_TARGET_TRIPLE}-ld ld DOC "The gold linker")
|
||||
|
||||
if(COMPILER_RT_SUPPORTED_ARCH)
|
||||
list(REMOVE_DUPLICATES COMPILER_RT_SUPPORTED_ARCH)
|
||||
endif()
|
||||
message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}")
|
||||
|
||||
if(ANDROID)
|
||||
|
|
@ -395,9 +428,14 @@ else()
|
|||
set(OS_NAME "${CMAKE_SYSTEM_NAME}")
|
||||
endif()
|
||||
|
||||
set(ALL_SANITIZERS asan;dfsan;msan;tsan;safestack;cfi;esan;scudo)
|
||||
set(COMPILER_RT_SANITIZERS_TO_BUILD ${ALL_SANITIZERS} CACHE STRING
|
||||
"sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
|
||||
list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
|
||||
|
||||
if (SANITIZER_COMMON_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
|
||||
(OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD" OR
|
||||
(OS_NAME MATCHES "Windows" AND MSVC)))
|
||||
(OS_NAME MATCHES "Windows" AND (NOT MINGW AND NOT CYGWIN))))
|
||||
set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE)
|
||||
else()
|
||||
set(COMPILER_RT_HAS_SANITIZER_COMMON FALSE)
|
||||
|
|
@ -444,7 +482,7 @@ else()
|
|||
set(COMPILER_RT_HAS_MSAN FALSE)
|
||||
endif()
|
||||
|
||||
if (PROFILE_SUPPORTED_ARCH AND
|
||||
if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
|
||||
OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows")
|
||||
set(COMPILER_RT_HAS_PROFILE TRUE)
|
||||
else()
|
||||
|
|
@ -493,3 +531,9 @@ else()
|
|||
set(COMPILER_RT_HAS_SCUDO FALSE)
|
||||
endif()
|
||||
|
||||
if (COMPILER_RT_HAS_SANITIZER_COMMON AND XRAY_SUPPORTED_ARCH AND
|
||||
OS_NAME MATCHES "Linux")
|
||||
set(COMPILER_RT_HAS_XRAY TRUE)
|
||||
else()
|
||||
set(COMPILER_RT_HAS_XRAY FALSE)
|
||||
endif()
|
||||
|
|
|
|||
66
docs/TestingGuide.rst
Normal file
66
docs/TestingGuide.rst
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
========================================
|
||||
Compiler-rt Testing Infrastructure Guide
|
||||
========================================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
This document is the reference manual for the compiler-rt modifications to the
|
||||
testing infrastructure. Documentation for the infrastructure itself can be found at
|
||||
:ref:`llvm_testing_guide`.
|
||||
|
||||
LLVM testing infrastructure organization
|
||||
========================================
|
||||
|
||||
The compiler-rt testing infrastructure contains regression tests which are run
|
||||
as part of the usual ``make check-all`` and are expected to always pass -- they
|
||||
should be run before every commit.
|
||||
|
||||
Quick start
|
||||
===========
|
||||
|
||||
The regressions tests are in the "compiler-rt" module and are normally checked
|
||||
out in the directory ``llvm/projects/compiler-rt/test``. Use ``make check-all``
|
||||
to run the regression tests after building compiler-rt.
|
||||
|
||||
REQUIRES, XFAIL, etc.
|
||||
---------------------
|
||||
|
||||
Sometimes it is necessary to restrict a test to a specific target or mark it as
|
||||
an "expected fail" or XFAIL. This is normally achieved using ``REQUIRES:`` or
|
||||
``XFAIL:`` with a substring of LLVM's default target triple. Unfortunately, the
|
||||
behaviour of this is somewhat quirky in compiler-rt. There are two main
|
||||
pitfalls to avoid.
|
||||
|
||||
The first pitfall is that these directives perform a substring match on the
|
||||
triple and as such ``XFAIL: mips`` affects more triples than expected. For
|
||||
example, ``mips-linux-gnu``, ``mipsel-linux-gnu``, ``mips64-linux-gnu``, and
|
||||
``mips64el-linux-gnu`` will all match a ``XFAIL: mips`` directive. Including a
|
||||
trailing ``-`` such as in ``XFAIL: mips-`` can help to mitigate this quirk but
|
||||
even that has issues as described below.
|
||||
|
||||
The second pitfall is that the default target triple is often inappropriate for
|
||||
compiler-rt tests since compiler-rt tests may be compiled for multiple targets.
|
||||
For example, a typical build on an ``x86_64-linux-gnu`` host will often run the
|
||||
tests for both x86_64 and i386. In this situation ``XFAIL: x86_64`` will mark
|
||||
both the x86_64 and i386 tests as an expected failure while ``XFAIL: i386``
|
||||
will have no effect at all.
|
||||
|
||||
To remedy both pitfalls, compiler-rt tests provide a feature string which can
|
||||
be used to specify a single target. This string is of the form
|
||||
``target-is-${arch}`` where ``${arch}}`` is one of the values from the
|
||||
following lines of the CMake output::
|
||||
|
||||
-- Compiler-RT supported architectures: x86_64;i386
|
||||
-- Builtin supported architectures: i386;x86_64
|
||||
|
||||
So for example ``XFAIL: target-is-x86_64`` will mark a test as expected to fail
|
||||
on x86_64 without also affecting the i386 test and ``XFAIL: target-is-i386``
|
||||
will mark a test as expected to fail on i386 even if the default target triple
|
||||
is ``x86_64-linux-gnu``. Directives that use these ``target-is-${arch}`` string
|
||||
require exact matches so ``XFAIL: target-is-mips``,
|
||||
``XFAIL: target-is-mipsel``, ``XFAIL: target-is-mips64``, and
|
||||
``XFAIL: target-is-mips64el`` all refer to different MIPS targets.
|
||||
|
|
@ -10,11 +10,18 @@ set(SANITIZER_HEADERS
|
|||
sanitizer/msan_interface.h
|
||||
sanitizer/tsan_interface_atomic.h)
|
||||
|
||||
set(XRAY_HEADERS
|
||||
xray/xray_interface.h)
|
||||
|
||||
set(COMPILER_RT_HEADERS
|
||||
${SANITIZER_HEADERS}
|
||||
${XRAY_HEADERS})
|
||||
|
||||
set(output_dir ${COMPILER_RT_OUTPUT_DIR}/include)
|
||||
|
||||
# Copy compiler-rt headers to the build tree.
|
||||
set(out_files)
|
||||
foreach( f ${SANITIZER_HEADERS} )
|
||||
foreach( f ${COMPILER_RT_HEADERS} )
|
||||
set( src ${CMAKE_CURRENT_SOURCE_DIR}/${f} )
|
||||
set( dst ${output_dir}/${f} )
|
||||
add_custom_command(OUTPUT ${dst}
|
||||
|
|
@ -32,3 +39,7 @@ set_target_properties(compiler-rt-headers PROPERTIES FOLDER "Compiler-RT Misc")
|
|||
install(FILES ${SANITIZER_HEADERS}
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
|
||||
DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/sanitizer)
|
||||
# Install xray headers.
|
||||
install(FILES ${XRAY_HEADERS}
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
|
||||
DESTINATION ${COMPILER_RT_INSTALL_PATH}/include/xray)
|
||||
|
|
|
|||
|
|
@ -117,6 +117,16 @@ extern "C" {
|
|||
// Print the stack trace leading to this call. Useful for debugging user code.
|
||||
void __sanitizer_print_stack_trace();
|
||||
|
||||
// Symbolizes the supplied 'pc' using the format string 'fmt'.
|
||||
// Outputs at most 'out_buf_size' bytes into 'out_buf'.
|
||||
// The format syntax is described in
|
||||
// lib/sanitizer_common/sanitizer_stacktrace_printer.h.
|
||||
void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf,
|
||||
size_t out_buf_size);
|
||||
// Same as __sanitizer_symbolize_pc, but for data section (i.e. globals).
|
||||
void __sanitizer_symbolize_global(void *data_ptr, const char *fmt,
|
||||
char *out_buf, size_t out_buf_size);
|
||||
|
||||
// Sets the callback to be called right before death on error.
|
||||
// Passing 0 will unset the callback.
|
||||
void __sanitizer_set_death_callback(void (*callback)(void));
|
||||
|
|
@ -169,7 +179,16 @@ extern "C" {
|
|||
// use-after-return detection.
|
||||
void __sanitizer_start_switch_fiber(void **fake_stack_save,
|
||||
const void *bottom, size_t size);
|
||||
void __sanitizer_finish_switch_fiber(void *fake_stack_save);
|
||||
void __sanitizer_finish_switch_fiber(void *fake_stack_save,
|
||||
const void **bottom_old,
|
||||
size_t *size_old);
|
||||
|
||||
// Get full module name and calculate pc offset within it.
|
||||
// Returns 1 if pc belongs to some module, 0 if module was not found.
|
||||
int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path,
|
||||
size_t module_path_len,
|
||||
void **pc_offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ extern "C" {
|
|||
void __sanitizer_cov_init();
|
||||
// Record and dump coverage info.
|
||||
void __sanitizer_cov_dump();
|
||||
|
||||
// Dump collected coverage info. Sorts pcs by module into individual
|
||||
// .sancov files.
|
||||
void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len);
|
||||
|
||||
// Open <name>.sancov.packed in the coverage directory and return the file
|
||||
// descriptor. Returns -1 on failure, or if coverage dumping is disabled.
|
||||
// This is intended for use by sandboxing code.
|
||||
|
|
@ -41,13 +46,6 @@ extern "C" {
|
|||
// Some of the entries in *data will be zero.
|
||||
uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data);
|
||||
|
||||
// Set *data to the growing buffer with covered PCs and return the size
|
||||
// of the buffer. The entries are never zero.
|
||||
// When only unique pcs are collected, the size is equal to
|
||||
// __sanitizer_get_total_unique_coverage.
|
||||
// WARNING: EXPERIMENTAL API.
|
||||
uintptr_t __sanitizer_get_coverage_pc_buffer(uintptr_t **data);
|
||||
|
||||
// The coverage instrumentation may optionally provide imprecise counters.
|
||||
// Rather than exposing the counter values to the user we instead map
|
||||
// the counters to a bitset.
|
||||
|
|
@ -65,6 +63,7 @@ extern "C" {
|
|||
// __sanitizer_get_number_of_counters bytes long and 8-aligned.
|
||||
uintptr_t
|
||||
__sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
65
include/xray/xray_interface.h
Normal file
65
include/xray/xray_interface.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
//===-- xray_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 XRay, a dynamic runtime instrumentation system.
|
||||
//
|
||||
// APIs for controlling XRay functionality explicitly.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef XRAY_XRAY_INTERFACE_H
|
||||
#define XRAY_XRAY_INTERFACE_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" {
|
||||
|
||||
enum XRayEntryType { ENTRY = 0, EXIT = 1, TAIL = 2 };
|
||||
|
||||
// Provide a function to invoke for when instrumentation points are hit. This is
|
||||
// a user-visible control surface that overrides the default implementation. The
|
||||
// function provided should take the following arguments:
|
||||
//
|
||||
// - function id: an identifier that indicates the id of a function; this id
|
||||
// is generated by xray; the mapping between the function id
|
||||
// and the actual function pointer is available through
|
||||
// __xray_table.
|
||||
// - entry type: identifies what kind of instrumentation point was encountered
|
||||
// (function entry, function exit, etc.). See the enum
|
||||
// XRayEntryType for more details.
|
||||
//
|
||||
// The user handler must handle correctly spurious calls after this handler is
|
||||
// removed or replaced with another handler, because it would be too costly for
|
||||
// XRay runtime to avoid spurious calls.
|
||||
// To prevent circular calling, the handler function itself and all its
|
||||
// direct&indirect callees must not be instrumented with XRay, which can be
|
||||
// achieved by marking them all with: __attribute__((xray_never_instrument))
|
||||
//
|
||||
// Returns 1 on success, 0 on error.
|
||||
extern int __xray_set_handler(void (*entry)(int32_t, XRayEntryType));
|
||||
|
||||
// This removes whatever the currently provided handler is. Returns 1 on
|
||||
// success, 0 on error.
|
||||
extern int __xray_remove_handler();
|
||||
|
||||
enum XRayPatchingStatus {
|
||||
NOT_INITIALIZED = 0,
|
||||
SUCCESS = 1,
|
||||
ONGOING = 2,
|
||||
FAILED = 3,
|
||||
};
|
||||
|
||||
// This tells XRay to patch the instrumentation points. See XRayPatchingStatus
|
||||
// for possible result values.
|
||||
extern XRayPatchingStatus __xray_patch();
|
||||
|
||||
// Reverses the effect of __xray_patch(). See XRayPatchingStatus for possible
|
||||
// result values.
|
||||
extern XRayPatchingStatus __xray_unpatch();
|
||||
}
|
||||
|
||||
#endif
|
||||
80
include/xray/xray_records.h
Normal file
80
include/xray/xray_records.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//===-- xray_records.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 XRay, a dynamic runtime instrumentation system.
|
||||
//
|
||||
// This header exposes some record types useful for the XRay in-memory logging
|
||||
// implementation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef XRAY_XRAY_RECORDS_H
|
||||
#define XRAY_XRAY_RECORDS_H
|
||||
|
||||
namespace __xray {
|
||||
|
||||
enum FileTypes {
|
||||
NAIVE_LOG = 0,
|
||||
};
|
||||
|
||||
// This data structure is used to describe the contents of the file. We use this
|
||||
// for versioning the supported XRay file formats.
|
||||
struct alignas(32) XRayFileHeader {
|
||||
uint16_t Version = 0;
|
||||
|
||||
// The type of file we're writing out. See the FileTypes enum for more
|
||||
// information. This allows different implementations of the XRay logging to
|
||||
// have different files for different information being stored.
|
||||
uint16_t Type = 0;
|
||||
|
||||
// What follows are a set of flags that indicate useful things for when
|
||||
// reading the data in the file.
|
||||
bool ConstantTSC : 1;
|
||||
bool NonstopTSC : 1;
|
||||
|
||||
// The frequency by which TSC increases per-second.
|
||||
alignas(8) uint64_t CycleFrequency = 0;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(XRayFileHeader) == 32, "XRayFileHeader != 32 bytes");
|
||||
|
||||
enum RecordTypes {
|
||||
NORMAL = 0,
|
||||
};
|
||||
|
||||
struct alignas(32) XRayRecord {
|
||||
// This is the type of the record being written. We use 16 bits to allow us to
|
||||
// treat this as a discriminant, and so that the first 4 bytes get packed
|
||||
// properly. See RecordTypes for more supported types.
|
||||
uint16_t RecordType = 0;
|
||||
|
||||
// The CPU where the thread is running. We assume number of CPUs <= 256.
|
||||
uint8_t CPU = 0;
|
||||
|
||||
// The type of the event. Usually either ENTER = 0 or EXIT = 1.
|
||||
uint8_t Type = 0;
|
||||
|
||||
// The function ID for the record.
|
||||
int32_t FuncId = 0;
|
||||
|
||||
// Get the full 8 bytes of the TSC when we get the log record.
|
||||
uint64_t TSC = 0;
|
||||
|
||||
// The thread ID for the currently running thread.
|
||||
uint32_t TId = 0;
|
||||
|
||||
// Use some bytes in the end of the record for buffers.
|
||||
char Buffer[4] = {};
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(XRayRecord) == 32, "XRayRecord != 32 bytes");
|
||||
|
||||
} // namespace __xray
|
||||
|
||||
#endif // XRAY_XRAY_RECORDS_H
|
||||
|
|
@ -4,56 +4,59 @@
|
|||
include(AddCompilerRT)
|
||||
include(SanitizerUtils)
|
||||
|
||||
# Hoist the building of sanitizer_common on whether we're building either the
|
||||
# sanitizers or xray (or both).
|
||||
#
|
||||
#TODO: Refactor sanitizer_common into smaller pieces (e.g. flag parsing, utils).
|
||||
if (COMPILER_RT_HAS_SANITIZER_COMMON AND
|
||||
(COMPILER_RT_BUILD_SANITIZERS OR COMPILER_RT_BUILD_XRAY))
|
||||
add_subdirectory(sanitizer_common)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_BUILD_BUILTINS)
|
||||
add_subdirectory(builtins)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_BUILD_SANITIZERS)
|
||||
if(COMPILER_RT_HAS_INTERCEPTION)
|
||||
add_subdirectory(interception)
|
||||
function(compiler_rt_build_runtime runtime)
|
||||
string(TOUPPER ${runtime} runtime_uppercase)
|
||||
if(COMPILER_RT_HAS_${runtime_uppercase})
|
||||
add_subdirectory(${runtime})
|
||||
foreach(directory ${ARGN})
|
||||
add_subdirectory(${directory})
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(compiler_rt_build_sanitizer sanitizer)
|
||||
string(TOUPPER ${sanitizer} sanitizer_uppercase)
|
||||
string(TOLOWER ${sanitizer} sanitizer_lowercase)
|
||||
list(FIND COMPILER_RT_SANITIZERS_TO_BUILD ${sanitizer_lowercase} result)
|
||||
if(NOT ${result} EQUAL -1)
|
||||
compiler_rt_build_runtime(${sanitizer} ${ARGN})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(COMPILER_RT_BUILD_SANITIZERS)
|
||||
compiler_rt_build_runtime(interception)
|
||||
|
||||
if(COMPILER_RT_HAS_SANITIZER_COMMON)
|
||||
add_subdirectory(sanitizer_common)
|
||||
add_subdirectory(stats)
|
||||
add_subdirectory(lsan)
|
||||
add_subdirectory(ubsan)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_ASAN)
|
||||
add_subdirectory(asan)
|
||||
endif()
|
||||
compiler_rt_build_sanitizer(asan)
|
||||
compiler_rt_build_sanitizer(dfsan)
|
||||
compiler_rt_build_sanitizer(msan)
|
||||
compiler_rt_build_sanitizer(tsan tsan/dd)
|
||||
compiler_rt_build_sanitizer(safestack)
|
||||
compiler_rt_build_sanitizer(cfi)
|
||||
compiler_rt_build_sanitizer(esan)
|
||||
compiler_rt_build_sanitizer(scudo)
|
||||
|
||||
if(COMPILER_RT_HAS_DFSAN)
|
||||
add_subdirectory(dfsan)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_MSAN)
|
||||
add_subdirectory(msan)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_PROFILE)
|
||||
add_subdirectory(profile)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_TSAN)
|
||||
add_subdirectory(tsan)
|
||||
add_subdirectory(tsan/dd)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_SAFESTACK)
|
||||
add_subdirectory(safestack)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_CFI)
|
||||
add_subdirectory(cfi)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_ESAN)
|
||||
add_subdirectory(esan)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_HAS_SCUDO)
|
||||
add_subdirectory(scudo)
|
||||
endif()
|
||||
compiler_rt_build_runtime(profile)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_BUILD_XRAY)
|
||||
compiler_rt_build_runtime(xray)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
#===- lib/Makefile.mk --------------------------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
SubDirs :=
|
||||
|
||||
# Add submodules.
|
||||
SubDirs += builtins
|
||||
|
|
@ -4,9 +4,12 @@ set(ASAN_SOURCES
|
|||
asan_allocator.cc
|
||||
asan_activation.cc
|
||||
asan_debugging.cc
|
||||
asan_descriptions.cc
|
||||
asan_errors.cc
|
||||
asan_fake_stack.cc
|
||||
asan_flags.cc
|
||||
asan_globals.cc
|
||||
asan_globals_win.cc
|
||||
asan_interceptors.cc
|
||||
asan_linux.cc
|
||||
asan_mac.cc
|
||||
|
|
@ -35,14 +38,9 @@ include_directories(..)
|
|||
set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
|
||||
append_rtti_flag(OFF ASAN_CFLAGS)
|
||||
|
||||
set(ASAN_COMMON_DEFINITIONS
|
||||
ASAN_HAS_EXCEPTIONS=1)
|
||||
|
||||
set(ASAN_DYNAMIC_LINK_FLAGS)
|
||||
|
||||
if(ANDROID)
|
||||
list(APPEND ASAN_COMMON_DEFINITIONS
|
||||
ASAN_LOW_MEMORY=1)
|
||||
# On Android, -z global does not do what it is documented to do.
|
||||
# On Android, -z global moves the library ahead in the lookup order,
|
||||
# placing it right after the LD_PRELOADs. This is used to compensate for the fact
|
||||
|
|
@ -105,8 +103,7 @@ if(NOT APPLE)
|
|||
endif()
|
||||
|
||||
# Build ASan runtimes shipped with Clang.
|
||||
add_custom_target(asan)
|
||||
set_target_properties(asan PROPERTIES FOLDER "Compiler-RT Misc")
|
||||
add_compiler_rt_component(asan)
|
||||
|
||||
if(APPLE)
|
||||
add_compiler_rt_runtime(clang_rt.asan
|
||||
|
|
@ -207,15 +204,25 @@ else()
|
|||
STATIC
|
||||
ARCHS ${arch}
|
||||
SOURCES asan_win_dll_thunk.cc
|
||||
asan_globals_win.cc
|
||||
$<TARGET_OBJECTS:RTInterception.${arch}>
|
||||
CFLAGS ${ASAN_CFLAGS} -DASAN_DLL_THUNK
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS}
|
||||
PARENT_TARGET asan)
|
||||
|
||||
set(DYNAMIC_RUNTIME_THUNK_CFLAGS "-DASAN_DYNAMIC_RUNTIME_THUNK")
|
||||
if(MSVC)
|
||||
list(APPEND DYNAMIC_RUNTIME_THUNK_CFLAGS "-Zl")
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES Clang)
|
||||
list(APPEND DYNAMIC_RUNTIME_THUNK_CFLAGS "-nodefaultlibs")
|
||||
endif()
|
||||
|
||||
add_compiler_rt_runtime(clang_rt.asan_dynamic_runtime_thunk
|
||||
STATIC
|
||||
ARCHS ${arch}
|
||||
SOURCES asan_win_dynamic_runtime_thunk.cc
|
||||
CFLAGS ${ASAN_CFLAGS} -DASAN_DYNAMIC_RUNTIME_THUNK -Zl
|
||||
asan_globals_win.cc
|
||||
CFLAGS ${ASAN_CFLAGS} ${DYNAMIC_RUNTIME_THUNK_CFLAGS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS}
|
||||
PARENT_TARGET asan)
|
||||
endif()
|
||||
|
|
@ -223,7 +230,6 @@ else()
|
|||
endif()
|
||||
|
||||
add_compiler_rt_resource_file(asan_blacklist asan_blacklist.txt asan)
|
||||
add_dependencies(compiler-rt asan)
|
||||
|
||||
add_subdirectory(scripts)
|
||||
|
||||
|
|
|
|||
|
|
@ -79,11 +79,13 @@ static struct AsanDeactivatedFlags {
|
|||
Report(
|
||||
"quarantine_size_mb %d, max_redzone %d, poison_heap %d, "
|
||||
"malloc_context_size %d, alloc_dealloc_mismatch %d, "
|
||||
"allocator_may_return_null %d, coverage %d, coverage_dir %s\n",
|
||||
"allocator_may_return_null %d, coverage %d, coverage_dir %s, "
|
||||
"allocator_release_to_os_interval_ms %d\n",
|
||||
allocator_options.quarantine_size_mb, allocator_options.max_redzone,
|
||||
poison_heap, malloc_context_size,
|
||||
allocator_options.alloc_dealloc_mismatch,
|
||||
allocator_options.may_return_null, coverage, coverage_dir);
|
||||
allocator_options.may_return_null, coverage, coverage_dir,
|
||||
allocator_options.release_to_os_interval_ms);
|
||||
}
|
||||
} asan_deactivated_flags;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,3 +33,4 @@ COMMON_ACTIVATION_FLAG(bool, coverage)
|
|||
COMMON_ACTIVATION_FLAG(const char *, coverage_dir)
|
||||
COMMON_ACTIVATION_FLAG(int, verbosity)
|
||||
COMMON_ACTIVATION_FLAG(bool, help)
|
||||
COMMON_ACTIVATION_FLAG(s32, allocator_release_to_os_interval_ms)
|
||||
|
|
|
|||
|
|
@ -207,25 +207,27 @@ QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
|
|||
|
||||
void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
|
||||
quarantine_size_mb = f->quarantine_size_mb;
|
||||
thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb;
|
||||
min_redzone = f->redzone;
|
||||
max_redzone = f->max_redzone;
|
||||
may_return_null = cf->allocator_may_return_null;
|
||||
alloc_dealloc_mismatch = f->alloc_dealloc_mismatch;
|
||||
release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms;
|
||||
}
|
||||
|
||||
void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
|
||||
f->quarantine_size_mb = quarantine_size_mb;
|
||||
f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb;
|
||||
f->redzone = min_redzone;
|
||||
f->max_redzone = max_redzone;
|
||||
cf->allocator_may_return_null = may_return_null;
|
||||
f->alloc_dealloc_mismatch = alloc_dealloc_mismatch;
|
||||
cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms;
|
||||
}
|
||||
|
||||
struct Allocator {
|
||||
static const uptr kMaxAllowedMallocSize =
|
||||
FIRST_32_SECOND_64(3UL << 30, 1ULL << 40);
|
||||
static const uptr kMaxThreadLocalQuarantine =
|
||||
FIRST_32_SECOND_64(1 << 18, 1 << 20);
|
||||
|
||||
AsanAllocator allocator;
|
||||
AsanQuarantine quarantine;
|
||||
|
|
@ -254,7 +256,7 @@ struct Allocator {
|
|||
void SharedInitCode(const AllocatorOptions &options) {
|
||||
CheckOptions(options);
|
||||
quarantine.Init((uptr)options.quarantine_size_mb << 20,
|
||||
kMaxThreadLocalQuarantine);
|
||||
(uptr)options.thread_local_quarantine_size_kb << 10);
|
||||
atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch,
|
||||
memory_order_release);
|
||||
atomic_store(&min_redzone, options.min_redzone, memory_order_release);
|
||||
|
|
@ -262,22 +264,59 @@ struct Allocator {
|
|||
}
|
||||
|
||||
void Initialize(const AllocatorOptions &options) {
|
||||
allocator.Init(options.may_return_null);
|
||||
allocator.Init(options.may_return_null, options.release_to_os_interval_ms);
|
||||
SharedInitCode(options);
|
||||
}
|
||||
|
||||
void RePoisonChunk(uptr chunk) {
|
||||
// This could a user-facing chunk (with redzones), or some internal
|
||||
// housekeeping chunk, like TransferBatch. Start by assuming the former.
|
||||
AsanChunk *ac = GetAsanChunk((void *)chunk);
|
||||
uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
|
||||
uptr beg = ac->Beg();
|
||||
uptr end = ac->Beg() + ac->UsedSize(true);
|
||||
uptr chunk_end = chunk + allocated_size;
|
||||
if (chunk < beg && beg < end && end <= chunk_end) {
|
||||
// Looks like a valid AsanChunk. Or maybe not. Be conservative and only
|
||||
// poison the redzones.
|
||||
PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
|
||||
uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
|
||||
FastPoisonShadowPartialRightRedzone(
|
||||
end_aligned_down, end - end_aligned_down,
|
||||
chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
|
||||
} else {
|
||||
// This can not be an AsanChunk. Poison everything. It may be reused as
|
||||
// AsanChunk later.
|
||||
PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
|
||||
}
|
||||
}
|
||||
|
||||
void ReInitialize(const AllocatorOptions &options) {
|
||||
allocator.SetMayReturnNull(options.may_return_null);
|
||||
allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms);
|
||||
SharedInitCode(options);
|
||||
|
||||
// Poison all existing allocation's redzones.
|
||||
if (CanPoisonMemory()) {
|
||||
allocator.ForceLock();
|
||||
allocator.ForEachChunk(
|
||||
[](uptr chunk, void *alloc) {
|
||||
((Allocator *)alloc)->RePoisonChunk(chunk);
|
||||
},
|
||||
this);
|
||||
allocator.ForceUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
void GetOptions(AllocatorOptions *options) const {
|
||||
options->quarantine_size_mb = quarantine.GetSize() >> 20;
|
||||
options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10;
|
||||
options->min_redzone = atomic_load(&min_redzone, memory_order_acquire);
|
||||
options->max_redzone = atomic_load(&max_redzone, memory_order_acquire);
|
||||
options->may_return_null = allocator.MayReturnNull();
|
||||
options->alloc_dealloc_mismatch =
|
||||
atomic_load(&alloc_dealloc_mismatch, memory_order_acquire);
|
||||
options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs();
|
||||
}
|
||||
|
||||
// -------------------- Helper methods. -------------------------
|
||||
|
|
@ -356,7 +395,7 @@ struct Allocator {
|
|||
if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
|
||||
Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
|
||||
(void*)size);
|
||||
return allocator.ReturnNullOrDie();
|
||||
return allocator.ReturnNullOrDieOnBadRequest();
|
||||
}
|
||||
|
||||
AsanThread *t = GetCurrentThread();
|
||||
|
|
@ -373,8 +412,7 @@ struct Allocator {
|
|||
allocator.Allocate(cache, needed_size, 8, false, check_rss_limit);
|
||||
}
|
||||
|
||||
if (!allocated)
|
||||
return allocator.ReturnNullOrDie();
|
||||
if (!allocated) return allocator.ReturnNullOrDieOnOOM();
|
||||
|
||||
if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) {
|
||||
// Heap poisoning is enabled, but the allocator provides an unpoisoned
|
||||
|
|
@ -530,7 +568,7 @@ struct Allocator {
|
|||
|
||||
if (delete_size && flags()->new_delete_type_mismatch &&
|
||||
delete_size != m->UsedSize()) {
|
||||
ReportNewDeleteSizeMismatch(p, m->UsedSize(), delete_size, stack);
|
||||
ReportNewDeleteSizeMismatch(p, delete_size, stack);
|
||||
}
|
||||
|
||||
QuarantineChunk(m, ptr, stack, alloc_type);
|
||||
|
|
@ -563,7 +601,7 @@ struct Allocator {
|
|||
|
||||
void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
|
||||
if (CallocShouldReturnNullDueToOverflow(size, nmemb))
|
||||
return allocator.ReturnNullOrDie();
|
||||
return allocator.ReturnNullOrDieOnBadRequest();
|
||||
void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
|
||||
// If the memory comes from the secondary allocator no need to clear it
|
||||
// as it comes directly from mmap.
|
||||
|
|
@ -673,6 +711,9 @@ uptr AsanChunkView::End() { return Beg() + UsedSize(); }
|
|||
uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
|
||||
uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
|
||||
uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
|
||||
AllocType AsanChunkView::GetAllocType() {
|
||||
return (AllocType)chunk_->alloc_type;
|
||||
}
|
||||
|
||||
static StackTrace GetStackTraceFromId(u32 id) {
|
||||
CHECK(id);
|
||||
|
|
@ -707,6 +748,9 @@ void GetAllocatorOptions(AllocatorOptions *options) {
|
|||
AsanChunkView FindHeapChunkByAddress(uptr addr) {
|
||||
return instance.FindHeapChunkByAddress(addr);
|
||||
}
|
||||
AsanChunkView FindHeapChunkByAllocBeg(uptr addr) {
|
||||
return AsanChunkView(instance.GetAsanChunk(reinterpret_cast<void*>(addr)));
|
||||
}
|
||||
|
||||
void AsanThreadLocalMallocStorage::CommitBack() {
|
||||
instance.CommitBack(this);
|
||||
|
|
|
|||
|
|
@ -33,10 +33,12 @@ struct AsanChunk;
|
|||
|
||||
struct AllocatorOptions {
|
||||
u32 quarantine_size_mb;
|
||||
u32 thread_local_quarantine_size_kb;
|
||||
u16 min_redzone;
|
||||
u16 max_redzone;
|
||||
u8 may_return_null;
|
||||
u8 alloc_dealloc_mismatch;
|
||||
s32 release_to_os_interval_ms;
|
||||
|
||||
void SetFrom(const Flags *f, const CommonFlags *cf);
|
||||
void CopyTo(Flags *f, CommonFlags *cf);
|
||||
|
|
@ -62,6 +64,7 @@ class AsanChunkView {
|
|||
u32 GetFreeStackId();
|
||||
StackTrace GetAllocStack();
|
||||
StackTrace GetFreeStack();
|
||||
AllocType GetAllocType();
|
||||
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
|
||||
if (addr >= Beg() && (addr + access_size) <= End()) {
|
||||
*offset = addr - Beg();
|
||||
|
|
@ -90,6 +93,7 @@ class AsanChunkView {
|
|||
};
|
||||
|
||||
AsanChunkView FindHeapChunkByAddress(uptr address);
|
||||
AsanChunkView FindHeapChunkByAllocBeg(uptr address);
|
||||
|
||||
// List of AsanChunks with total size.
|
||||
class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
|
||||
|
|
@ -117,18 +121,36 @@ struct AsanMapUnmapCallback {
|
|||
# if defined(__powerpc64__)
|
||||
const uptr kAllocatorSpace = 0xa0000000000ULL;
|
||||
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
|
||||
typedef DefaultSizeClassMap SizeClassMap;
|
||||
# elif defined(__aarch64__) && SANITIZER_ANDROID
|
||||
const uptr kAllocatorSpace = 0x3000000000ULL;
|
||||
const uptr kAllocatorSize = 0x2000000000ULL; // 128G.
|
||||
typedef VeryCompactSizeClassMap SizeClassMap;
|
||||
# elif defined(__aarch64__)
|
||||
// AArch64/SANITIZIER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
|
||||
// AArch64/SANITIZER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
|
||||
// so no need to different values for different VMA.
|
||||
const uptr kAllocatorSpace = 0x10000000000ULL;
|
||||
const uptr kAllocatorSize = 0x10000000000ULL; // 3T.
|
||||
typedef DefaultSizeClassMap SizeClassMap;
|
||||
# elif SANITIZER_WINDOWS
|
||||
const uptr kAllocatorSpace = ~(uptr)0;
|
||||
const uptr kAllocatorSize = 0x8000000000ULL; // 500G
|
||||
typedef DefaultSizeClassMap SizeClassMap;
|
||||
# else
|
||||
const uptr kAllocatorSpace = 0x600000000000ULL;
|
||||
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
|
||||
# endif
|
||||
typedef DefaultSizeClassMap SizeClassMap;
|
||||
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
|
||||
SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
|
||||
# endif
|
||||
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
|
||||
static const uptr kSpaceBeg = kAllocatorSpace;
|
||||
static const uptr kSpaceSize = kAllocatorSize;
|
||||
static const uptr kMetadataSize = 0;
|
||||
typedef __asan::SizeClassMap SizeClassMap;
|
||||
typedef AsanMapUnmapCallback MapUnmapCallback;
|
||||
static const uptr kFlags = 0;
|
||||
};
|
||||
|
||||
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
|
||||
#else // Fallback to SizeClassAllocator32.
|
||||
static const uptr kRegionSizeLog = 20;
|
||||
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
|
||||
|
|
|
|||
|
|
@ -14,74 +14,39 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_descriptions.h"
|
||||
#include "asan_flags.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_thread.h"
|
||||
|
||||
namespace __asan {
|
||||
namespace {
|
||||
using namespace __asan;
|
||||
|
||||
void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
|
||||
descr->name[0] = 0;
|
||||
descr->region_address = 0;
|
||||
descr->region_size = 0;
|
||||
descr->region_kind = "stack";
|
||||
|
||||
AsanThread::StackFrameAccess access;
|
||||
if (!t->GetStackFrameAccessByAddr(addr, &access))
|
||||
return;
|
||||
static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset,
|
||||
char *name, uptr name_size,
|
||||
uptr ®ion_address, uptr ®ion_size) {
|
||||
InternalMmapVector<StackVarDescr> vars(16);
|
||||
if (!ParseFrameDescription(access.frame_descr, &vars)) {
|
||||
if (!ParseFrameDescription(frame_descr, &vars)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uptr i = 0; i < vars.size(); i++) {
|
||||
if (access.offset <= vars[i].beg + vars[i].size) {
|
||||
internal_strncat(descr->name, vars[i].name_pos,
|
||||
Min(descr->name_size, vars[i].name_len));
|
||||
descr->region_address = addr - (access.offset - vars[i].beg);
|
||||
descr->region_size = vars[i].size;
|
||||
if (offset <= vars[i].beg + vars[i].size) {
|
||||
// We use name_len + 1 because strlcpy will guarantee a \0 at the end, so
|
||||
// if we're limiting the copy due to name_len, we add 1 to ensure we copy
|
||||
// the whole name and then terminate with '\0'.
|
||||
internal_strlcpy(name, vars[i].name_pos,
|
||||
Min(name_size, vars[i].name_len + 1));
|
||||
region_address = addr - (offset - vars[i].beg);
|
||||
region_size = vars[i].size;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
|
||||
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
|
||||
descr->name[0] = 0;
|
||||
descr->region_address = 0;
|
||||
descr->region_size = 0;
|
||||
|
||||
if (!chunk.IsValid()) {
|
||||
descr->region_kind = "heap-invalid";
|
||||
return;
|
||||
}
|
||||
|
||||
descr->region_address = chunk.Beg();
|
||||
descr->region_size = chunk.UsedSize();
|
||||
descr->region_kind = "heap";
|
||||
}
|
||||
|
||||
void AsanLocateAddress(uptr addr, AddressDescription *descr) {
|
||||
if (DescribeAddressIfShadow(addr, descr, /* print */ false)) {
|
||||
return;
|
||||
}
|
||||
if (GetInfoForAddressIfGlobal(addr, descr)) {
|
||||
return;
|
||||
}
|
||||
asanThreadRegistry().Lock();
|
||||
AsanThread *thread = FindThreadByStackAddress(addr);
|
||||
asanThreadRegistry().Unlock();
|
||||
if (thread) {
|
||||
GetInfoForStackVar(addr, descr, thread);
|
||||
return;
|
||||
}
|
||||
GetInfoForHeapAddress(addr, descr);
|
||||
}
|
||||
|
||||
static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
|
||||
uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
|
||||
bool alloc_stack) {
|
||||
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
if (!chunk.IsValid()) return 0;
|
||||
|
|
@ -108,18 +73,58 @@ static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
|
|||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
using namespace __asan;
|
||||
} // namespace
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
|
||||
uptr *region_address, uptr *region_size) {
|
||||
AddressDescription descr = { name, name_size, 0, 0, nullptr };
|
||||
AsanLocateAddress(addr, &descr);
|
||||
if (region_address) *region_address = descr.region_address;
|
||||
if (region_size) *region_size = descr.region_size;
|
||||
return descr.region_kind;
|
||||
uptr *region_address_ptr,
|
||||
uptr *region_size_ptr) {
|
||||
AddressDescription descr(addr);
|
||||
uptr region_address = 0;
|
||||
uptr region_size = 0;
|
||||
const char *region_kind = nullptr;
|
||||
if (name && name_size > 0) name[0] = 0;
|
||||
|
||||
if (auto shadow = descr.AsShadow()) {
|
||||
// region_{address,size} are already 0
|
||||
switch (shadow->kind) {
|
||||
case kShadowKindLow:
|
||||
region_kind = "low shadow";
|
||||
break;
|
||||
case kShadowKindGap:
|
||||
region_kind = "shadow gap";
|
||||
break;
|
||||
case kShadowKindHigh:
|
||||
region_kind = "high shadow";
|
||||
break;
|
||||
}
|
||||
} else if (auto heap = descr.AsHeap()) {
|
||||
region_kind = "heap";
|
||||
region_address = heap->chunk_access.chunk_begin;
|
||||
region_size = heap->chunk_access.chunk_size;
|
||||
} else if (auto stack = descr.AsStack()) {
|
||||
region_kind = "stack";
|
||||
if (!stack->frame_descr) {
|
||||
// region_{address,size} are already 0
|
||||
} else {
|
||||
FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name,
|
||||
name_size, region_address, region_size);
|
||||
}
|
||||
} else if (auto global = descr.AsGlobal()) {
|
||||
region_kind = "global";
|
||||
auto &g = global->globals[0];
|
||||
internal_strlcpy(name, g.name, name_size);
|
||||
region_address = g.beg;
|
||||
region_size = g.size;
|
||||
} else {
|
||||
// region_{address,size} are already 0
|
||||
region_kind = "heap-invalid";
|
||||
}
|
||||
|
||||
CHECK(region_kind);
|
||||
if (region_address_ptr) *region_address_ptr = region_address;
|
||||
if (region_size_ptr) *region_size_ptr = region_size;
|
||||
return region_kind;
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
|
|
|
|||
486
lib/asan/asan_descriptions.cc
Normal file
486
lib/asan/asan_descriptions.cc
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
//===-- asan_descriptions.cc ------------------------------------*- 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 AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// ASan functions for getting information about an address and/or printing it.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_descriptions.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// Return " (thread_name) " or an empty string if the name is empty.
|
||||
const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
|
||||
uptr buff_len) {
|
||||
const char *name = t->name;
|
||||
if (name[0] == '\0') return "";
|
||||
buff[0] = 0;
|
||||
internal_strncat(buff, " (", 3);
|
||||
internal_strncat(buff, name, buff_len - 4);
|
||||
internal_strncat(buff, ")", 2);
|
||||
return buff;
|
||||
}
|
||||
|
||||
const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len) {
|
||||
if (tid == kInvalidTid) return "";
|
||||
asanThreadRegistry().CheckLocked();
|
||||
AsanThreadContext *t = GetThreadContextByTidLocked(tid);
|
||||
return ThreadNameWithParenthesis(t, buff, buff_len);
|
||||
}
|
||||
|
||||
void DescribeThread(AsanThreadContext *context) {
|
||||
CHECK(context);
|
||||
asanThreadRegistry().CheckLocked();
|
||||
// No need to announce the main thread.
|
||||
if (context->tid == 0 || context->announced) {
|
||||
return;
|
||||
}
|
||||
context->announced = true;
|
||||
char tname[128];
|
||||
InternalScopedString str(1024);
|
||||
str.append("Thread T%d%s", context->tid,
|
||||
ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
|
||||
if (context->parent_tid == kInvalidTid) {
|
||||
str.append(" created by unknown thread\n");
|
||||
Printf("%s", str.data());
|
||||
return;
|
||||
}
|
||||
str.append(
|
||||
" created by T%d%s here:\n", context->parent_tid,
|
||||
ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
|
||||
Printf("%s", str.data());
|
||||
StackDepotGet(context->stack_id).Print();
|
||||
// Recursively described parent thread if needed.
|
||||
if (flags()->print_full_thread_history) {
|
||||
AsanThreadContext *parent_context =
|
||||
GetThreadContextByTidLocked(context->parent_tid);
|
||||
DescribeThread(parent_context);
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow descriptions
|
||||
static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {
|
||||
CHECK(!AddrIsInMem(addr));
|
||||
if (AddrIsInShadowGap(addr)) {
|
||||
*shadow_kind = kShadowKindGap;
|
||||
} else if (AddrIsInHighShadow(addr)) {
|
||||
*shadow_kind = kShadowKindHigh;
|
||||
} else if (AddrIsInLowShadow(addr)) {
|
||||
*shadow_kind = kShadowKindLow;
|
||||
} else {
|
||||
CHECK(0 && "Address is not in memory and not in shadow?");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DescribeAddressIfShadow(uptr addr) {
|
||||
ShadowAddressDescription descr;
|
||||
if (!GetShadowAddressInformation(addr, &descr)) return false;
|
||||
descr.Print();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) {
|
||||
if (AddrIsInMem(addr)) return false;
|
||||
ShadowKind shadow_kind;
|
||||
if (!GetShadowKind(addr, &shadow_kind)) return false;
|
||||
if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr;
|
||||
descr->addr = addr;
|
||||
descr->kind = shadow_kind;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Heap descriptions
|
||||
static void GetAccessToHeapChunkInformation(ChunkAccess *descr,
|
||||
AsanChunkView chunk, uptr addr,
|
||||
uptr access_size) {
|
||||
descr->bad_addr = addr;
|
||||
if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {
|
||||
descr->access_type = kAccessTypeLeft;
|
||||
} else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {
|
||||
descr->access_type = kAccessTypeRight;
|
||||
if (descr->offset < 0) {
|
||||
descr->bad_addr -= descr->offset;
|
||||
descr->offset = 0;
|
||||
}
|
||||
} else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {
|
||||
descr->access_type = kAccessTypeInside;
|
||||
} else {
|
||||
descr->access_type = kAccessTypeUnknown;
|
||||
}
|
||||
descr->chunk_begin = chunk.Beg();
|
||||
descr->chunk_size = chunk.UsedSize();
|
||||
descr->alloc_type = chunk.GetAllocType();
|
||||
}
|
||||
|
||||
static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {
|
||||
Decorator d;
|
||||
InternalScopedString str(4096);
|
||||
str.append("%s", d.Location());
|
||||
switch (descr.access_type) {
|
||||
case kAccessTypeLeft:
|
||||
str.append("%p is located %zd bytes to the left of",
|
||||
(void *)descr.bad_addr, descr.offset);
|
||||
break;
|
||||
case kAccessTypeRight:
|
||||
str.append("%p is located %zd bytes to the right of",
|
||||
(void *)descr.bad_addr, descr.offset);
|
||||
break;
|
||||
case kAccessTypeInside:
|
||||
str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr,
|
||||
descr.offset);
|
||||
break;
|
||||
case kAccessTypeUnknown:
|
||||
str.append(
|
||||
"%p is located somewhere around (this is AddressSanitizer bug!)",
|
||||
(void *)descr.bad_addr);
|
||||
}
|
||||
str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size,
|
||||
(void *)descr.chunk_begin,
|
||||
(void *)(descr.chunk_begin + descr.chunk_size));
|
||||
str.append("%s", d.EndLocation());
|
||||
Printf("%s", str.data());
|
||||
}
|
||||
|
||||
bool GetHeapAddressInformation(uptr addr, uptr access_size,
|
||||
HeapAddressDescription *descr) {
|
||||
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
if (!chunk.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
descr->addr = addr;
|
||||
GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,
|
||||
access_size);
|
||||
CHECK_NE(chunk.AllocTid(), kInvalidTid);
|
||||
descr->alloc_tid = chunk.AllocTid();
|
||||
descr->alloc_stack_id = chunk.GetAllocStackId();
|
||||
descr->free_tid = chunk.FreeTid();
|
||||
if (descr->free_tid != kInvalidTid)
|
||||
descr->free_stack_id = chunk.GetFreeStackId();
|
||||
return true;
|
||||
}
|
||||
|
||||
static StackTrace GetStackTraceFromId(u32 id) {
|
||||
CHECK(id);
|
||||
StackTrace res = StackDepotGet(id);
|
||||
CHECK(res.trace);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DescribeAddressIfHeap(uptr addr, uptr access_size) {
|
||||
HeapAddressDescription descr;
|
||||
if (!GetHeapAddressInformation(addr, access_size, &descr)) {
|
||||
Printf(
|
||||
"AddressSanitizer can not describe address in more detail "
|
||||
"(wild memory access suspected).\n");
|
||||
return false;
|
||||
}
|
||||
descr.Print();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stack descriptions
|
||||
bool GetStackAddressInformation(uptr addr, uptr access_size,
|
||||
StackAddressDescription *descr) {
|
||||
AsanThread *t = FindThreadByStackAddress(addr);
|
||||
if (!t) return false;
|
||||
|
||||
descr->addr = addr;
|
||||
descr->tid = t->tid();
|
||||
// Try to fetch precise stack frame for this access.
|
||||
AsanThread::StackFrameAccess access;
|
||||
if (!t->GetStackFrameAccessByAddr(addr, &access)) {
|
||||
descr->frame_descr = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
descr->offset = access.offset;
|
||||
descr->access_size = access_size;
|
||||
descr->frame_pc = access.frame_pc;
|
||||
descr->frame_descr = access.frame_descr;
|
||||
|
||||
#if SANITIZER_PPC64V1
|
||||
// On PowerPC64 ELFv1, the address of a function actually points to a
|
||||
// three-doubleword data structure with the first field containing
|
||||
// the address of the function's code.
|
||||
descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);
|
||||
#endif
|
||||
descr->frame_pc += 16;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
|
||||
uptr access_size, uptr prev_var_end,
|
||||
uptr next_var_beg) {
|
||||
uptr var_end = var.beg + var.size;
|
||||
uptr addr_end = addr + access_size;
|
||||
const char *pos_descr = nullptr;
|
||||
// If the variable [var.beg, var_end) is the nearest variable to the
|
||||
// current memory access, indicate it in the log.
|
||||
if (addr >= var.beg) {
|
||||
if (addr_end <= var_end)
|
||||
pos_descr = "is inside"; // May happen if this is a use-after-return.
|
||||
else if (addr < var_end)
|
||||
pos_descr = "partially overflows";
|
||||
else if (addr_end <= next_var_beg &&
|
||||
next_var_beg - addr_end >= addr - var_end)
|
||||
pos_descr = "overflows";
|
||||
} else {
|
||||
if (addr_end > var.beg)
|
||||
pos_descr = "partially underflows";
|
||||
else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)
|
||||
pos_descr = "underflows";
|
||||
}
|
||||
InternalScopedString str(1024);
|
||||
str.append(" [%zd, %zd)", var.beg, var_end);
|
||||
// Render variable name.
|
||||
str.append(" '");
|
||||
for (uptr i = 0; i < var.name_len; ++i) {
|
||||
str.append("%c", var.name_pos[i]);
|
||||
}
|
||||
str.append("'");
|
||||
if (pos_descr) {
|
||||
Decorator d;
|
||||
// FIXME: we may want to also print the size of the access here,
|
||||
// but in case of accesses generated by memset it may be confusing.
|
||||
str.append("%s <== Memory access at offset %zd %s this variable%s\n",
|
||||
d.Location(), addr, pos_descr, d.EndLocation());
|
||||
} else {
|
||||
str.append("\n");
|
||||
}
|
||||
Printf("%s", str.data());
|
||||
}
|
||||
|
||||
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||
StackAddressDescription descr;
|
||||
if (!GetStackAddressInformation(addr, access_size, &descr)) return false;
|
||||
descr.Print();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Global descriptions
|
||||
static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
|
||||
const __asan_global &g) {
|
||||
InternalScopedString str(4096);
|
||||
Decorator d;
|
||||
str.append("%s", d.Location());
|
||||
if (addr < g.beg) {
|
||||
str.append("%p is located %zd bytes to the left", (void *)addr,
|
||||
g.beg - addr);
|
||||
} else if (addr + access_size > g.beg + g.size) {
|
||||
if (addr < g.beg + g.size) addr = g.beg + g.size;
|
||||
str.append("%p is located %zd bytes to the right", (void *)addr,
|
||||
addr - (g.beg + g.size));
|
||||
} else {
|
||||
// Can it happen?
|
||||
str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
|
||||
}
|
||||
str.append(" of global variable '%s' defined in '",
|
||||
MaybeDemangleGlobalName(g.name));
|
||||
PrintGlobalLocation(&str, g);
|
||||
str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
|
||||
str.append("%s", d.EndLocation());
|
||||
PrintGlobalNameIfASCII(&str, g);
|
||||
Printf("%s", str.data());
|
||||
}
|
||||
|
||||
bool GetGlobalAddressInformation(uptr addr, uptr access_size,
|
||||
GlobalAddressDescription *descr) {
|
||||
descr->addr = addr;
|
||||
int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,
|
||||
ARRAY_SIZE(descr->globals));
|
||||
descr->size = globals_num;
|
||||
descr->access_size = access_size;
|
||||
return globals_num != 0;
|
||||
}
|
||||
|
||||
bool DescribeAddressIfGlobal(uptr addr, uptr access_size,
|
||||
const char *bug_type) {
|
||||
GlobalAddressDescription descr;
|
||||
if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;
|
||||
|
||||
descr.Print(bug_type);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShadowAddressDescription::Print() const {
|
||||
Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]);
|
||||
}
|
||||
|
||||
void GlobalAddressDescription::Print(const char *bug_type) const {
|
||||
for (int i = 0; i < size; i++) {
|
||||
DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);
|
||||
if (bug_type &&
|
||||
0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
|
||||
reg_sites[i]) {
|
||||
Printf(" registered at:\n");
|
||||
StackDepotGet(reg_sites[i]).Print();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StackAddressDescription::Print() const {
|
||||
Decorator d;
|
||||
char tname[128];
|
||||
Printf("%s", d.Location());
|
||||
Printf("Address %p is located in stack of thread T%d%s", addr, tid,
|
||||
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
|
||||
|
||||
if (!frame_descr) {
|
||||
Printf("%s\n", d.EndLocation());
|
||||
return;
|
||||
}
|
||||
Printf(" at offset %zu in frame%s\n", offset, d.EndLocation());
|
||||
|
||||
// Now we print the frame where the alloca has happened.
|
||||
// We print this frame as a stack trace with one element.
|
||||
// The symbolizer may print more than one frame if inlining was involved.
|
||||
// The frame numbers may be different than those in the stack trace printed
|
||||
// previously. That's unfortunate, but I have no better solution,
|
||||
// especially given that the alloca may be from entirely different place
|
||||
// (e.g. use-after-scope, or different thread's stack).
|
||||
Printf("%s", d.EndLocation());
|
||||
StackTrace alloca_stack(&frame_pc, 1);
|
||||
alloca_stack.Print();
|
||||
|
||||
InternalMmapVector<StackVarDescr> vars(16);
|
||||
if (!ParseFrameDescription(frame_descr, &vars)) {
|
||||
Printf(
|
||||
"AddressSanitizer can't parse the stack frame "
|
||||
"descriptor: |%s|\n",
|
||||
frame_descr);
|
||||
// 'addr' is a stack address, so return true even if we can't parse frame
|
||||
return;
|
||||
}
|
||||
uptr n_objects = vars.size();
|
||||
// Report the number of stack objects.
|
||||
Printf(" This frame has %zu object(s):\n", n_objects);
|
||||
|
||||
// Report all objects in this frame.
|
||||
for (uptr i = 0; i < n_objects; i++) {
|
||||
uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
|
||||
uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
|
||||
PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,
|
||||
next_var_beg);
|
||||
}
|
||||
Printf(
|
||||
"HINT: this may be a false positive if your program uses "
|
||||
"some custom stack unwind mechanism or swapcontext\n");
|
||||
if (SANITIZER_WINDOWS)
|
||||
Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
|
||||
else
|
||||
Printf(" (longjmp and C++ exceptions *are* supported)\n");
|
||||
|
||||
DescribeThread(GetThreadContextByTidLocked(tid));
|
||||
}
|
||||
|
||||
void HeapAddressDescription::Print() const {
|
||||
PrintHeapChunkAccess(addr, chunk_access);
|
||||
|
||||
asanThreadRegistry().CheckLocked();
|
||||
AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
|
||||
StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
|
||||
|
||||
char tname[128];
|
||||
Decorator d;
|
||||
AsanThreadContext *free_thread = nullptr;
|
||||
if (free_tid != kInvalidTid) {
|
||||
free_thread = GetThreadContextByTidLocked(free_tid);
|
||||
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
|
||||
free_thread->tid,
|
||||
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
StackTrace free_stack = GetStackTraceFromId(free_stack_id);
|
||||
free_stack.Print();
|
||||
Printf("%spreviously allocated by thread T%d%s here:%s\n", d.Allocation(),
|
||||
alloc_thread->tid,
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
} else {
|
||||
Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
|
||||
alloc_thread->tid,
|
||||
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
||||
d.EndAllocation());
|
||||
}
|
||||
alloc_stack.Print();
|
||||
DescribeThread(GetCurrentThread());
|
||||
if (free_thread) DescribeThread(free_thread);
|
||||
DescribeThread(alloc_thread);
|
||||
}
|
||||
|
||||
AddressDescription::AddressDescription(uptr addr, uptr access_size,
|
||||
bool shouldLockThreadRegistry) {
|
||||
if (GetShadowAddressInformation(addr, &data.shadow)) {
|
||||
data.kind = kAddressKindShadow;
|
||||
return;
|
||||
}
|
||||
if (GetHeapAddressInformation(addr, access_size, &data.heap)) {
|
||||
data.kind = kAddressKindHeap;
|
||||
return;
|
||||
}
|
||||
|
||||
bool isStackMemory = false;
|
||||
if (shouldLockThreadRegistry) {
|
||||
ThreadRegistryLock l(&asanThreadRegistry());
|
||||
isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
|
||||
} else {
|
||||
isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
|
||||
}
|
||||
if (isStackMemory) {
|
||||
data.kind = kAddressKindStack;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetGlobalAddressInformation(addr, access_size, &data.global)) {
|
||||
data.kind = kAddressKindGlobal;
|
||||
return;
|
||||
}
|
||||
data.kind = kAddressKindWild;
|
||||
addr = 0;
|
||||
}
|
||||
|
||||
void PrintAddressDescription(uptr addr, uptr access_size,
|
||||
const char *bug_type) {
|
||||
ShadowAddressDescription shadow_descr;
|
||||
if (GetShadowAddressInformation(addr, &shadow_descr)) {
|
||||
shadow_descr.Print();
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalAddressDescription global_descr;
|
||||
if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {
|
||||
global_descr.Print(bug_type);
|
||||
return;
|
||||
}
|
||||
|
||||
StackAddressDescription stack_descr;
|
||||
if (GetStackAddressInformation(addr, access_size, &stack_descr)) {
|
||||
stack_descr.Print();
|
||||
return;
|
||||
}
|
||||
|
||||
HeapAddressDescription heap_descr;
|
||||
if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {
|
||||
heap_descr.Print();
|
||||
return;
|
||||
}
|
||||
|
||||
// We exhausted our possibilities. Bail out.
|
||||
Printf(
|
||||
"AddressSanitizer can not describe address in more detail "
|
||||
"(wild memory access suspected).\n");
|
||||
}
|
||||
} // namespace __asan
|
||||
253
lib/asan/asan_descriptions.h
Normal file
253
lib/asan/asan_descriptions.h
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
//===-- asan_descriptions.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 AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// ASan-private header for asan_descriptions.cc.
|
||||
// TODO(filcab): Most struct definitions should move to the interface headers.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef ASAN_DESCRIPTIONS_H
|
||||
#define ASAN_DESCRIPTIONS_H
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_report_decorator.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void DescribeThread(AsanThreadContext *context);
|
||||
static inline void DescribeThread(AsanThread *t) {
|
||||
if (t) DescribeThread(t->context());
|
||||
}
|
||||
const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
|
||||
uptr buff_len);
|
||||
const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len);
|
||||
|
||||
class Decorator : public __sanitizer::SanitizerCommonDecorator {
|
||||
public:
|
||||
Decorator() : SanitizerCommonDecorator() {}
|
||||
const char *Access() { return Blue(); }
|
||||
const char *EndAccess() { return Default(); }
|
||||
const char *Location() { return Green(); }
|
||||
const char *EndLocation() { return Default(); }
|
||||
const char *Allocation() { return Magenta(); }
|
||||
const char *EndAllocation() { return Default(); }
|
||||
|
||||
const char *ShadowByte(u8 byte) {
|
||||
switch (byte) {
|
||||
case kAsanHeapLeftRedzoneMagic:
|
||||
case kAsanArrayCookieMagic:
|
||||
return Red();
|
||||
case kAsanHeapFreeMagic:
|
||||
return Magenta();
|
||||
case kAsanStackLeftRedzoneMagic:
|
||||
case kAsanStackMidRedzoneMagic:
|
||||
case kAsanStackRightRedzoneMagic:
|
||||
return Red();
|
||||
case kAsanStackAfterReturnMagic:
|
||||
return Magenta();
|
||||
case kAsanInitializationOrderMagic:
|
||||
return Cyan();
|
||||
case kAsanUserPoisonedMemoryMagic:
|
||||
case kAsanContiguousContainerOOBMagic:
|
||||
case kAsanAllocaLeftMagic:
|
||||
case kAsanAllocaRightMagic:
|
||||
return Blue();
|
||||
case kAsanStackUseAfterScopeMagic:
|
||||
return Magenta();
|
||||
case kAsanGlobalRedzoneMagic:
|
||||
return Red();
|
||||
case kAsanInternalHeapMagic:
|
||||
return Yellow();
|
||||
case kAsanIntraObjectRedzone:
|
||||
return Yellow();
|
||||
default:
|
||||
return Default();
|
||||
}
|
||||
}
|
||||
const char *EndShadowByte() { return Default(); }
|
||||
const char *MemoryByte() { return Magenta(); }
|
||||
const char *EndMemoryByte() { return Default(); }
|
||||
};
|
||||
|
||||
enum ShadowKind : u8 {
|
||||
kShadowKindLow,
|
||||
kShadowKindGap,
|
||||
kShadowKindHigh,
|
||||
};
|
||||
static const char *const ShadowNames[] = {"low shadow", "shadow gap",
|
||||
"high shadow"};
|
||||
|
||||
struct ShadowAddressDescription {
|
||||
uptr addr;
|
||||
ShadowKind kind;
|
||||
u8 shadow_byte;
|
||||
|
||||
void Print() const;
|
||||
};
|
||||
|
||||
bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr);
|
||||
bool DescribeAddressIfShadow(uptr addr);
|
||||
|
||||
enum AccessType {
|
||||
kAccessTypeLeft,
|
||||
kAccessTypeRight,
|
||||
kAccessTypeInside,
|
||||
kAccessTypeUnknown, // This means we have an AddressSanitizer bug!
|
||||
};
|
||||
|
||||
struct ChunkAccess {
|
||||
uptr bad_addr;
|
||||
sptr offset;
|
||||
uptr chunk_begin;
|
||||
uptr chunk_size;
|
||||
u32 access_type : 2;
|
||||
u32 alloc_type : 2;
|
||||
};
|
||||
|
||||
struct HeapAddressDescription {
|
||||
uptr addr;
|
||||
uptr alloc_tid;
|
||||
uptr free_tid;
|
||||
u32 alloc_stack_id;
|
||||
u32 free_stack_id;
|
||||
ChunkAccess chunk_access;
|
||||
|
||||
void Print() const;
|
||||
};
|
||||
|
||||
bool GetHeapAddressInformation(uptr addr, uptr access_size,
|
||||
HeapAddressDescription *descr);
|
||||
bool DescribeAddressIfHeap(uptr addr, uptr access_size = 1);
|
||||
|
||||
struct StackAddressDescription {
|
||||
uptr addr;
|
||||
uptr tid;
|
||||
uptr offset;
|
||||
uptr frame_pc;
|
||||
uptr access_size;
|
||||
const char *frame_descr;
|
||||
|
||||
void Print() const;
|
||||
};
|
||||
|
||||
bool GetStackAddressInformation(uptr addr, uptr access_size,
|
||||
StackAddressDescription *descr);
|
||||
|
||||
struct GlobalAddressDescription {
|
||||
uptr addr;
|
||||
// Assume address is close to at most four globals.
|
||||
static const int kMaxGlobals = 4;
|
||||
__asan_global globals[kMaxGlobals];
|
||||
u32 reg_sites[kMaxGlobals];
|
||||
uptr access_size;
|
||||
u8 size;
|
||||
|
||||
void Print(const char *bug_type = "") const;
|
||||
};
|
||||
|
||||
bool GetGlobalAddressInformation(uptr addr, uptr access_size,
|
||||
GlobalAddressDescription *descr);
|
||||
bool DescribeAddressIfGlobal(uptr addr, uptr access_size, const char *bug_type);
|
||||
|
||||
// General function to describe an address. Will try to describe the address as
|
||||
// a shadow, global (variable), stack, or heap address.
|
||||
// bug_type is optional and is used for checking if we're reporting an
|
||||
// initialization-order-fiasco
|
||||
// The proper access_size should be passed for stack, global, and heap
|
||||
// addresses. Defaults to 1.
|
||||
// Each of the *AddressDescription functions has its own Print() member, which
|
||||
// may take access_size and bug_type parameters if needed.
|
||||
void PrintAddressDescription(uptr addr, uptr access_size = 1,
|
||||
const char *bug_type = "");
|
||||
|
||||
enum AddressKind {
|
||||
kAddressKindWild,
|
||||
kAddressKindShadow,
|
||||
kAddressKindHeap,
|
||||
kAddressKindStack,
|
||||
kAddressKindGlobal,
|
||||
};
|
||||
|
||||
class AddressDescription {
|
||||
struct AddressDescriptionData {
|
||||
AddressKind kind;
|
||||
union {
|
||||
ShadowAddressDescription shadow;
|
||||
HeapAddressDescription heap;
|
||||
StackAddressDescription stack;
|
||||
GlobalAddressDescription global;
|
||||
uptr addr;
|
||||
};
|
||||
};
|
||||
|
||||
AddressDescriptionData data;
|
||||
|
||||
public:
|
||||
AddressDescription() = default;
|
||||
// shouldLockThreadRegistry allows us to skip locking if we're sure we already
|
||||
// have done it.
|
||||
AddressDescription(uptr addr, bool shouldLockThreadRegistry = true)
|
||||
: AddressDescription(addr, 1, shouldLockThreadRegistry) {}
|
||||
AddressDescription(uptr addr, uptr access_size,
|
||||
bool shouldLockThreadRegistry = true);
|
||||
|
||||
uptr Address() const {
|
||||
switch (data.kind) {
|
||||
case kAddressKindWild:
|
||||
return data.addr;
|
||||
case kAddressKindShadow:
|
||||
return data.shadow.addr;
|
||||
case kAddressKindHeap:
|
||||
return data.heap.addr;
|
||||
case kAddressKindStack:
|
||||
return data.stack.addr;
|
||||
case kAddressKindGlobal:
|
||||
return data.global.addr;
|
||||
}
|
||||
UNREACHABLE("AddressInformation kind is invalid");
|
||||
}
|
||||
void Print(const char *bug_descr = nullptr) const {
|
||||
switch (data.kind) {
|
||||
case kAddressKindWild:
|
||||
Printf("Address %p is a wild pointer.\n", data.addr);
|
||||
return;
|
||||
case kAddressKindShadow:
|
||||
return data.shadow.Print();
|
||||
case kAddressKindHeap:
|
||||
return data.heap.Print();
|
||||
case kAddressKindStack:
|
||||
return data.stack.Print();
|
||||
case kAddressKindGlobal:
|
||||
// initialization-order-fiasco has a special Print()
|
||||
return data.global.Print(bug_descr);
|
||||
}
|
||||
UNREACHABLE("AddressInformation kind is invalid");
|
||||
}
|
||||
|
||||
void StoreTo(AddressDescriptionData *dst) const { *dst = data; }
|
||||
|
||||
const ShadowAddressDescription *AsShadow() const {
|
||||
return data.kind == kAddressKindShadow ? &data.shadow : nullptr;
|
||||
}
|
||||
const HeapAddressDescription *AsHeap() const {
|
||||
return data.kind == kAddressKindHeap ? &data.heap : nullptr;
|
||||
}
|
||||
const StackAddressDescription *AsStack() const {
|
||||
return data.kind == kAddressKindStack ? &data.stack : nullptr;
|
||||
}
|
||||
const GlobalAddressDescription *AsGlobal() const {
|
||||
return data.kind == kAddressKindGlobal ? &data.global : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_DESCRIPTIONS_H
|
||||
503
lib/asan/asan_errors.cc
Normal file
503
lib/asan/asan_errors.cc
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
//===-- asan_errors.cc ------------------------------------------*- 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 AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// ASan implementation for error structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_errors.h"
|
||||
#include <signal.h>
|
||||
#include "asan_descriptions.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void ErrorStackOverflow::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: %s on address %p"
|
||||
" (pc %p bp %p sp %p T%d)\n", scariness.GetDescription(),
|
||||
(void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
|
||||
Printf("%s", d.EndWarning());
|
||||
scariness.Print();
|
||||
BufferedStackTrace stack;
|
||||
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
|
||||
common_flags()->fast_unwind_on_fatal);
|
||||
stack.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), &stack);
|
||||
}
|
||||
|
||||
static void MaybeDumpInstructionBytes(uptr pc) {
|
||||
if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return;
|
||||
InternalScopedString str(1024);
|
||||
str.append("First 16 instruction bytes at pc: ");
|
||||
if (IsAccessibleMemoryRange(pc, 16)) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/ false, " ");
|
||||
}
|
||||
str.append("\n");
|
||||
} else {
|
||||
str.append("unaccessible\n");
|
||||
}
|
||||
Report("%s", str.data());
|
||||
}
|
||||
|
||||
static void MaybeDumpRegisters(void *context) {
|
||||
if (!flags()->dump_registers) return;
|
||||
SignalContext::DumpAllRegisters(context);
|
||||
}
|
||||
|
||||
void ErrorDeadlySignal::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
const char *description = DescribeSignalOrException(signo);
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: %s on unknown address %p (pc %p bp %p sp %p "
|
||||
"T%d)\n",
|
||||
description, (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
|
||||
Printf("%s", d.EndWarning());
|
||||
if (pc < GetPageSizeCached()) Report("Hint: pc points to the zero page.\n");
|
||||
if (is_memory_access) {
|
||||
const char *access_type =
|
||||
write_flag == SignalContext::WRITE
|
||||
? "WRITE"
|
||||
: (write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
|
||||
Report("The signal is caused by a %s memory access.\n", access_type);
|
||||
if (addr < GetPageSizeCached())
|
||||
Report("Hint: address points to the zero page.\n");
|
||||
}
|
||||
scariness.Print();
|
||||
BufferedStackTrace stack;
|
||||
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
|
||||
common_flags()->fast_unwind_on_fatal);
|
||||
stack.Print();
|
||||
MaybeDumpInstructionBytes(pc);
|
||||
MaybeDumpRegisters(context);
|
||||
Printf("AddressSanitizer can not provide additional info.\n");
|
||||
ReportErrorSummary(description, &stack);
|
||||
}
|
||||
|
||||
void ErrorDoubleFree::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
char tname[128];
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: attempting %s on %p in "
|
||||
"thread T%d%s:\n",
|
||||
scariness.GetDescription(), addr_description.addr, tid,
|
||||
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
|
||||
Printf("%s", d.EndWarning());
|
||||
scariness.Print();
|
||||
GET_STACK_TRACE_FATAL(second_free_stack->trace[0],
|
||||
second_free_stack->top_frame_bp);
|
||||
stack.Print();
|
||||
addr_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), &stack);
|
||||
}
|
||||
|
||||
void ErrorNewDeleteSizeMismatch::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
char tname[128];
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: %s on %p in thread "
|
||||
"T%d%s:\n",
|
||||
scariness.GetDescription(), addr_description.addr, tid,
|
||||
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
|
||||
Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
|
||||
Printf(
|
||||
" size of the allocated type: %zd bytes;\n"
|
||||
" size of the deallocated type: %zd bytes.\n",
|
||||
addr_description.chunk_access.chunk_size, delete_size);
|
||||
CHECK_GT(free_stack->size, 0);
|
||||
scariness.Print();
|
||||
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
||||
stack.Print();
|
||||
addr_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), &stack);
|
||||
Report(
|
||||
"HINT: if you don't care about these errors you may set "
|
||||
"ASAN_OPTIONS=new_delete_type_mismatch=0\n");
|
||||
}
|
||||
|
||||
void ErrorFreeNotMalloced::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
char tname[128];
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: attempting free on address "
|
||||
"which was not malloc()-ed: %p in thread T%d%s\n",
|
||||
addr_description.Address(), tid,
|
||||
ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
|
||||
Printf("%s", d.EndWarning());
|
||||
CHECK_GT(free_stack->size, 0);
|
||||
scariness.Print();
|
||||
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
||||
stack.Print();
|
||||
addr_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), &stack);
|
||||
}
|
||||
|
||||
void ErrorAllocTypeMismatch::Print() {
|
||||
static const char *alloc_names[] = {"INVALID", "malloc", "operator new",
|
||||
"operator new []"};
|
||||
static const char *dealloc_names[] = {"INVALID", "free", "operator delete",
|
||||
"operator delete []"};
|
||||
CHECK_NE(alloc_type, dealloc_type);
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n",
|
||||
scariness.GetDescription(),
|
||||
alloc_names[alloc_type], dealloc_names[dealloc_type],
|
||||
addr_description.addr);
|
||||
Printf("%s", d.EndWarning());
|
||||
CHECK_GT(dealloc_stack->size, 0);
|
||||
scariness.Print();
|
||||
GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp);
|
||||
stack.Print();
|
||||
addr_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), &stack);
|
||||
Report(
|
||||
"HINT: if you don't care about these errors you may set "
|
||||
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
|
||||
}
|
||||
|
||||
void ErrorMallocUsableSizeNotOwned::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: attempting to call malloc_usable_size() for "
|
||||
"pointer which is not owned: %p\n",
|
||||
addr_description.Address());
|
||||
Printf("%s", d.EndWarning());
|
||||
stack->Print();
|
||||
addr_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), stack);
|
||||
}
|
||||
|
||||
void ErrorSanitizerGetAllocatedSizeNotOwned::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: attempting to call "
|
||||
"__sanitizer_get_allocated_size() for pointer which is not owned: %p\n",
|
||||
addr_description.Address());
|
||||
Printf("%s", d.EndWarning());
|
||||
stack->Print();
|
||||
addr_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), stack);
|
||||
}
|
||||
|
||||
void ErrorStringFunctionMemoryRangesOverlap::Print() {
|
||||
Decorator d;
|
||||
char bug_type[100];
|
||||
internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
|
||||
Printf("%s", d.Warning());
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) "
|
||||
"overlap\n",
|
||||
bug_type, addr1_description.Address(),
|
||||
addr1_description.Address() + length1, addr2_description.Address(),
|
||||
addr2_description.Address() + length2);
|
||||
Printf("%s", d.EndWarning());
|
||||
scariness.Print();
|
||||
stack->Print();
|
||||
addr1_description.Print();
|
||||
addr2_description.Print();
|
||||
ReportErrorSummary(bug_type, stack);
|
||||
}
|
||||
|
||||
void ErrorStringFunctionSizeOverflow::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("ERROR: AddressSanitizer: %s: (size=%zd)\n",
|
||||
scariness.GetDescription(), size);
|
||||
Printf("%s", d.EndWarning());
|
||||
scariness.Print();
|
||||
stack->Print();
|
||||
addr_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), stack);
|
||||
}
|
||||
|
||||
void ErrorBadParamsToAnnotateContiguousContainer::Print() {
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: bad parameters to "
|
||||
"__sanitizer_annotate_contiguous_container:\n"
|
||||
" beg : %p\n"
|
||||
" end : %p\n"
|
||||
" old_mid : %p\n"
|
||||
" new_mid : %p\n",
|
||||
beg, end, old_mid, new_mid);
|
||||
uptr granularity = SHADOW_GRANULARITY;
|
||||
if (!IsAligned(beg, granularity))
|
||||
Report("ERROR: beg is not aligned by %d\n", granularity);
|
||||
stack->Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), stack);
|
||||
}
|
||||
|
||||
void ErrorODRViolation::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(),
|
||||
global1.beg);
|
||||
Printf("%s", d.EndWarning());
|
||||
InternalScopedString g1_loc(256), g2_loc(256);
|
||||
PrintGlobalLocation(&g1_loc, global1);
|
||||
PrintGlobalLocation(&g2_loc, global2);
|
||||
Printf(" [1] size=%zd '%s' %s\n", global1.size,
|
||||
MaybeDemangleGlobalName(global1.name), g1_loc.data());
|
||||
Printf(" [2] size=%zd '%s' %s\n", global2.size,
|
||||
MaybeDemangleGlobalName(global2.name), g2_loc.data());
|
||||
if (stack_id1 && stack_id2) {
|
||||
Printf("These globals were registered at these points:\n");
|
||||
Printf(" [1]:\n");
|
||||
StackDepotGet(stack_id1).Print();
|
||||
Printf(" [2]:\n");
|
||||
StackDepotGet(stack_id2).Print();
|
||||
}
|
||||
Report(
|
||||
"HINT: if you don't care about these errors you may set "
|
||||
"ASAN_OPTIONS=detect_odr_violation=0\n");
|
||||
InternalScopedString error_msg(256);
|
||||
error_msg.append("%s: global '%s' at %s", scariness.GetDescription(),
|
||||
MaybeDemangleGlobalName(global1.name), g1_loc.data());
|
||||
ReportErrorSummary(error_msg.data());
|
||||
}
|
||||
|
||||
void ErrorInvalidPointerPair::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("ERROR: AddressSanitizer: %s: %p %p\n", scariness.GetDescription(),
|
||||
addr1_description.Address(), addr2_description.Address());
|
||||
Printf("%s", d.EndWarning());
|
||||
GET_STACK_TRACE_FATAL(pc, bp);
|
||||
stack.Print();
|
||||
addr1_description.Print();
|
||||
addr2_description.Print();
|
||||
ReportErrorSummary(scariness.GetDescription(), &stack);
|
||||
}
|
||||
|
||||
static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
|
||||
return s[-1] > 127 && s[1] > 127;
|
||||
}
|
||||
|
||||
ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
|
||||
bool is_write_, uptr access_size_)
|
||||
: ErrorBase(tid),
|
||||
addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false),
|
||||
pc(pc_),
|
||||
bp(bp_),
|
||||
sp(sp_),
|
||||
access_size(access_size_),
|
||||
is_write(is_write_),
|
||||
shadow_val(0) {
|
||||
scariness.Clear();
|
||||
if (access_size) {
|
||||
if (access_size <= 9) {
|
||||
char desr[] = "?-byte";
|
||||
desr[0] = '0' + access_size;
|
||||
scariness.Scare(access_size + access_size / 2, desr);
|
||||
} else if (access_size >= 10) {
|
||||
scariness.Scare(15, "multi-byte");
|
||||
}
|
||||
is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read");
|
||||
|
||||
// Determine the error type.
|
||||
bug_descr = "unknown-crash";
|
||||
if (AddrIsInMem(addr)) {
|
||||
u8 *shadow_addr = (u8 *)MemToShadow(addr);
|
||||
// If we are accessing 16 bytes, look at the second shadow byte.
|
||||
if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++;
|
||||
// If we are in the partial right redzone, look at the next shadow byte.
|
||||
if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
|
||||
bool far_from_bounds = false;
|
||||
shadow_val = *shadow_addr;
|
||||
int bug_type_score = 0;
|
||||
// For use-after-frees reads are almost as bad as writes.
|
||||
int read_after_free_bonus = 0;
|
||||
switch (shadow_val) {
|
||||
case kAsanHeapLeftRedzoneMagic:
|
||||
case kAsanArrayCookieMagic:
|
||||
bug_descr = "heap-buffer-overflow";
|
||||
bug_type_score = 10;
|
||||
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
||||
break;
|
||||
case kAsanHeapFreeMagic:
|
||||
bug_descr = "heap-use-after-free";
|
||||
bug_type_score = 20;
|
||||
if (!is_write) read_after_free_bonus = 18;
|
||||
break;
|
||||
case kAsanStackLeftRedzoneMagic:
|
||||
bug_descr = "stack-buffer-underflow";
|
||||
bug_type_score = 25;
|
||||
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
||||
break;
|
||||
case kAsanInitializationOrderMagic:
|
||||
bug_descr = "initialization-order-fiasco";
|
||||
bug_type_score = 1;
|
||||
break;
|
||||
case kAsanStackMidRedzoneMagic:
|
||||
case kAsanStackRightRedzoneMagic:
|
||||
bug_descr = "stack-buffer-overflow";
|
||||
bug_type_score = 25;
|
||||
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
||||
break;
|
||||
case kAsanStackAfterReturnMagic:
|
||||
bug_descr = "stack-use-after-return";
|
||||
bug_type_score = 30;
|
||||
if (!is_write) read_after_free_bonus = 18;
|
||||
break;
|
||||
case kAsanUserPoisonedMemoryMagic:
|
||||
bug_descr = "use-after-poison";
|
||||
bug_type_score = 20;
|
||||
break;
|
||||
case kAsanContiguousContainerOOBMagic:
|
||||
bug_descr = "container-overflow";
|
||||
bug_type_score = 10;
|
||||
break;
|
||||
case kAsanStackUseAfterScopeMagic:
|
||||
bug_descr = "stack-use-after-scope";
|
||||
bug_type_score = 10;
|
||||
break;
|
||||
case kAsanGlobalRedzoneMagic:
|
||||
bug_descr = "global-buffer-overflow";
|
||||
bug_type_score = 10;
|
||||
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
||||
break;
|
||||
case kAsanIntraObjectRedzone:
|
||||
bug_descr = "intra-object-overflow";
|
||||
bug_type_score = 10;
|
||||
break;
|
||||
case kAsanAllocaLeftMagic:
|
||||
case kAsanAllocaRightMagic:
|
||||
bug_descr = "dynamic-stack-buffer-overflow";
|
||||
bug_type_score = 25;
|
||||
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
||||
break;
|
||||
}
|
||||
scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr);
|
||||
if (far_from_bounds) scariness.Scare(10, "far-from-bounds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintContainerOverflowHint() {
|
||||
Printf("HINT: if you don't care about these errors you may set "
|
||||
"ASAN_OPTIONS=detect_container_overflow=0.\n"
|
||||
"If you suspect a false positive see also: "
|
||||
"https://github.com/google/sanitizers/wiki/"
|
||||
"AddressSanitizerContainerOverflow.\n");
|
||||
}
|
||||
|
||||
static void PrintShadowByte(InternalScopedString *str, const char *before,
|
||||
u8 byte, const char *after = "\n") {
|
||||
PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
|
||||
}
|
||||
|
||||
static void PrintLegend(InternalScopedString *str) {
|
||||
str->append(
|
||||
"Shadow byte legend (one shadow byte represents %d "
|
||||
"application bytes):\n",
|
||||
(int)SHADOW_GRANULARITY);
|
||||
PrintShadowByte(str, " Addressable: ", 0);
|
||||
str->append(" Partially addressable: ");
|
||||
for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
|
||||
str->append("\n");
|
||||
PrintShadowByte(str, " Heap left redzone: ",
|
||||
kAsanHeapLeftRedzoneMagic);
|
||||
PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
|
||||
PrintShadowByte(str, " Stack left redzone: ",
|
||||
kAsanStackLeftRedzoneMagic);
|
||||
PrintShadowByte(str, " Stack mid redzone: ",
|
||||
kAsanStackMidRedzoneMagic);
|
||||
PrintShadowByte(str, " Stack right redzone: ",
|
||||
kAsanStackRightRedzoneMagic);
|
||||
PrintShadowByte(str, " Stack after return: ",
|
||||
kAsanStackAfterReturnMagic);
|
||||
PrintShadowByte(str, " Stack use after scope: ",
|
||||
kAsanStackUseAfterScopeMagic);
|
||||
PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
|
||||
PrintShadowByte(str, " Global init order: ",
|
||||
kAsanInitializationOrderMagic);
|
||||
PrintShadowByte(str, " Poisoned by user: ",
|
||||
kAsanUserPoisonedMemoryMagic);
|
||||
PrintShadowByte(str, " Container overflow: ",
|
||||
kAsanContiguousContainerOOBMagic);
|
||||
PrintShadowByte(str, " Array cookie: ",
|
||||
kAsanArrayCookieMagic);
|
||||
PrintShadowByte(str, " Intra object redzone: ",
|
||||
kAsanIntraObjectRedzone);
|
||||
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
|
||||
PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
|
||||
PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
|
||||
}
|
||||
|
||||
static void PrintShadowBytes(InternalScopedString *str, const char *before,
|
||||
u8 *bytes, u8 *guilty, uptr n) {
|
||||
Decorator d;
|
||||
if (before) str->append("%s%p:", before, bytes);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
u8 *p = bytes + i;
|
||||
const char *before =
|
||||
p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
|
||||
const char *after = p == guilty ? "]" : "";
|
||||
PrintShadowByte(str, before, *p, after);
|
||||
}
|
||||
str->append("\n");
|
||||
}
|
||||
|
||||
static void PrintShadowMemoryForAddress(uptr addr) {
|
||||
if (!AddrIsInMem(addr)) return;
|
||||
uptr shadow_addr = MemToShadow(addr);
|
||||
const uptr n_bytes_per_row = 16;
|
||||
uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
|
||||
InternalScopedString str(4096 * 8);
|
||||
str.append("Shadow bytes around the buggy address:\n");
|
||||
for (int i = -5; i <= 5; i++) {
|
||||
const char *prefix = (i == 0) ? "=>" : " ";
|
||||
PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
|
||||
(u8 *)shadow_addr, n_bytes_per_row);
|
||||
}
|
||||
if (flags()->print_legend) PrintLegend(&str);
|
||||
Printf("%s", str.data());
|
||||
}
|
||||
|
||||
void ErrorGeneric::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
uptr addr = addr_description.Address();
|
||||
Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n",
|
||||
bug_descr, (void *)addr, pc, bp, sp);
|
||||
Printf("%s", d.EndWarning());
|
||||
|
||||
char tname[128];
|
||||
Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(),
|
||||
access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size,
|
||||
(void *)addr, tid,
|
||||
ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess());
|
||||
|
||||
scariness.Print();
|
||||
GET_STACK_TRACE_FATAL(pc, bp);
|
||||
stack.Print();
|
||||
|
||||
// Pass bug_descr because we have a special case for
|
||||
// initialization-order-fiasco
|
||||
addr_description.Print(bug_descr);
|
||||
if (shadow_val == kAsanContiguousContainerOOBMagic)
|
||||
PrintContainerOverflowHint();
|
||||
ReportErrorSummary(bug_descr, &stack);
|
||||
PrintShadowMemoryForAddress(addr);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
390
lib/asan/asan_errors.h
Normal file
390
lib/asan/asan_errors.h
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
//===-- asan_errors.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 AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// ASan-private header for error structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef ASAN_ERRORS_H
|
||||
#define ASAN_ERRORS_H
|
||||
|
||||
#include "asan_descriptions.h"
|
||||
#include "asan_scariness_score.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
struct ErrorBase {
|
||||
ErrorBase() = default;
|
||||
explicit ErrorBase(u32 tid_) : tid(tid_) {}
|
||||
ScarinessScoreBase scariness;
|
||||
u32 tid;
|
||||
};
|
||||
|
||||
struct ErrorStackOverflow : ErrorBase {
|
||||
uptr addr, pc, bp, sp;
|
||||
// ErrorStackOverflow never owns the context.
|
||||
void *context;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorStackOverflow() = default;
|
||||
ErrorStackOverflow(u32 tid, const SignalContext &sig)
|
||||
: ErrorBase(tid),
|
||||
addr(sig.addr),
|
||||
pc(sig.pc),
|
||||
bp(sig.bp),
|
||||
sp(sig.sp),
|
||||
context(sig.context) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "stack-overflow");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorDeadlySignal : ErrorBase {
|
||||
uptr addr, pc, bp, sp;
|
||||
// ErrorDeadlySignal never owns the context.
|
||||
void *context;
|
||||
int signo;
|
||||
SignalContext::WriteFlag write_flag;
|
||||
bool is_memory_access;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorDeadlySignal() = default;
|
||||
ErrorDeadlySignal(u32 tid, const SignalContext &sig, int signo_)
|
||||
: ErrorBase(tid),
|
||||
addr(sig.addr),
|
||||
pc(sig.pc),
|
||||
bp(sig.bp),
|
||||
sp(sig.sp),
|
||||
context(sig.context),
|
||||
signo(signo_),
|
||||
write_flag(sig.write_flag),
|
||||
is_memory_access(sig.is_memory_access) {
|
||||
scariness.Clear();
|
||||
if (is_memory_access) {
|
||||
if (addr < GetPageSizeCached()) {
|
||||
scariness.Scare(10, "null-deref");
|
||||
} else if (addr == pc) {
|
||||
scariness.Scare(60, "wild-jump");
|
||||
} else if (write_flag == SignalContext::WRITE) {
|
||||
scariness.Scare(30, "wild-addr-write");
|
||||
} else if (write_flag == SignalContext::READ) {
|
||||
scariness.Scare(20, "wild-addr-read");
|
||||
} else {
|
||||
scariness.Scare(25, "wild-addr");
|
||||
}
|
||||
} else {
|
||||
scariness.Scare(10, "signal");
|
||||
}
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorDoubleFree : ErrorBase {
|
||||
// ErrorDoubleFree doesn't own the stack trace.
|
||||
const BufferedStackTrace *second_free_stack;
|
||||
HeapAddressDescription addr_description;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorDoubleFree() = default;
|
||||
ErrorDoubleFree(u32 tid, BufferedStackTrace *stack, uptr addr)
|
||||
: ErrorBase(tid), second_free_stack(stack) {
|
||||
CHECK_GT(second_free_stack->size, 0);
|
||||
GetHeapAddressInformation(addr, 1, &addr_description);
|
||||
scariness.Clear();
|
||||
scariness.Scare(42, "double-free");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorNewDeleteSizeMismatch : ErrorBase {
|
||||
// ErrorNewDeleteSizeMismatch doesn't own the stack trace.
|
||||
const BufferedStackTrace *free_stack;
|
||||
HeapAddressDescription addr_description;
|
||||
uptr delete_size;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorNewDeleteSizeMismatch() = default;
|
||||
ErrorNewDeleteSizeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
|
||||
uptr delete_size_)
|
||||
: ErrorBase(tid), free_stack(stack), delete_size(delete_size_) {
|
||||
GetHeapAddressInformation(addr, 1, &addr_description);
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "new-delete-type-mismatch");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorFreeNotMalloced : ErrorBase {
|
||||
// ErrorFreeNotMalloced doesn't own the stack trace.
|
||||
const BufferedStackTrace *free_stack;
|
||||
AddressDescription addr_description;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorFreeNotMalloced() = default;
|
||||
ErrorFreeNotMalloced(u32 tid, BufferedStackTrace *stack, uptr addr)
|
||||
: ErrorBase(tid),
|
||||
free_stack(stack),
|
||||
addr_description(addr, /*shouldLockThreadRegistry=*/false) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(40, "bad-free");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorAllocTypeMismatch : ErrorBase {
|
||||
// ErrorAllocTypeMismatch doesn't own the stack trace.
|
||||
const BufferedStackTrace *dealloc_stack;
|
||||
HeapAddressDescription addr_description;
|
||||
AllocType alloc_type, dealloc_type;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorAllocTypeMismatch() = default;
|
||||
ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
|
||||
AllocType alloc_type_, AllocType dealloc_type_)
|
||||
: ErrorBase(tid),
|
||||
dealloc_stack(stack),
|
||||
alloc_type(alloc_type_),
|
||||
dealloc_type(dealloc_type_) {
|
||||
GetHeapAddressInformation(addr, 1, &addr_description);
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "alloc-dealloc-mismatch");
|
||||
};
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorMallocUsableSizeNotOwned : ErrorBase {
|
||||
// ErrorMallocUsableSizeNotOwned doesn't own the stack trace.
|
||||
const BufferedStackTrace *stack;
|
||||
AddressDescription addr_description;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorMallocUsableSizeNotOwned() = default;
|
||||
ErrorMallocUsableSizeNotOwned(u32 tid, BufferedStackTrace *stack_, uptr addr)
|
||||
: ErrorBase(tid),
|
||||
stack(stack_),
|
||||
addr_description(addr, /*shouldLockThreadRegistry=*/false) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "bad-malloc_usable_size");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorSanitizerGetAllocatedSizeNotOwned : ErrorBase {
|
||||
// ErrorSanitizerGetAllocatedSizeNotOwned doesn't own the stack trace.
|
||||
const BufferedStackTrace *stack;
|
||||
AddressDescription addr_description;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorSanitizerGetAllocatedSizeNotOwned() = default;
|
||||
ErrorSanitizerGetAllocatedSizeNotOwned(u32 tid, BufferedStackTrace *stack_,
|
||||
uptr addr)
|
||||
: ErrorBase(tid),
|
||||
stack(stack_),
|
||||
addr_description(addr, /*shouldLockThreadRegistry=*/false) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "bad-__sanitizer_get_allocated_size");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorStringFunctionMemoryRangesOverlap : ErrorBase {
|
||||
// ErrorStringFunctionMemoryRangesOverlap doesn't own the stack trace.
|
||||
const BufferedStackTrace *stack;
|
||||
uptr length1, length2;
|
||||
AddressDescription addr1_description;
|
||||
AddressDescription addr2_description;
|
||||
const char *function;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorStringFunctionMemoryRangesOverlap() = default;
|
||||
ErrorStringFunctionMemoryRangesOverlap(u32 tid, BufferedStackTrace *stack_,
|
||||
uptr addr1, uptr length1_, uptr addr2,
|
||||
uptr length2_, const char *function_)
|
||||
: ErrorBase(tid),
|
||||
stack(stack_),
|
||||
length1(length1_),
|
||||
length2(length2_),
|
||||
addr1_description(addr1, length1, /*shouldLockThreadRegistry=*/false),
|
||||
addr2_description(addr2, length2, /*shouldLockThreadRegistry=*/false),
|
||||
function(function_) {
|
||||
char bug_type[100];
|
||||
internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, bug_type);
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorStringFunctionSizeOverflow : ErrorBase {
|
||||
// ErrorStringFunctionSizeOverflow doesn't own the stack trace.
|
||||
const BufferedStackTrace *stack;
|
||||
AddressDescription addr_description;
|
||||
uptr size;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorStringFunctionSizeOverflow() = default;
|
||||
ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace *stack_,
|
||||
uptr addr, uptr size_)
|
||||
: ErrorBase(tid),
|
||||
stack(stack_),
|
||||
addr_description(addr, /*shouldLockThreadRegistry=*/false),
|
||||
size(size_) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "negative-size-param");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorBadParamsToAnnotateContiguousContainer : ErrorBase {
|
||||
// ErrorBadParamsToAnnotateContiguousContainer doesn't own the stack trace.
|
||||
const BufferedStackTrace *stack;
|
||||
uptr beg, end, old_mid, new_mid;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorBadParamsToAnnotateContiguousContainer() = default;
|
||||
// PS4: Do we want an AddressDescription for beg?
|
||||
ErrorBadParamsToAnnotateContiguousContainer(u32 tid,
|
||||
BufferedStackTrace *stack_,
|
||||
uptr beg_, uptr end_,
|
||||
uptr old_mid_, uptr new_mid_)
|
||||
: ErrorBase(tid),
|
||||
stack(stack_),
|
||||
beg(beg_),
|
||||
end(end_),
|
||||
old_mid(old_mid_),
|
||||
new_mid(new_mid_) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "bad-__sanitizer_annotate_contiguous_container");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorODRViolation : ErrorBase {
|
||||
__asan_global global1, global2;
|
||||
u32 stack_id1, stack_id2;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorODRViolation() = default;
|
||||
ErrorODRViolation(u32 tid, const __asan_global *g1, u32 stack_id1_,
|
||||
const __asan_global *g2, u32 stack_id2_)
|
||||
: ErrorBase(tid),
|
||||
global1(*g1),
|
||||
global2(*g2),
|
||||
stack_id1(stack_id1_),
|
||||
stack_id2(stack_id2_) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "odr-violation");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorInvalidPointerPair : ErrorBase {
|
||||
uptr pc, bp, sp;
|
||||
AddressDescription addr1_description;
|
||||
AddressDescription addr2_description;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorInvalidPointerPair() = default;
|
||||
ErrorInvalidPointerPair(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr p1,
|
||||
uptr p2)
|
||||
: ErrorBase(tid),
|
||||
pc(pc_),
|
||||
bp(bp_),
|
||||
sp(sp_),
|
||||
addr1_description(p1, 1, /*shouldLockThreadRegistry=*/false),
|
||||
addr2_description(p2, 1, /*shouldLockThreadRegistry=*/false) {
|
||||
scariness.Clear();
|
||||
scariness.Scare(10, "invalid-pointer-pair");
|
||||
}
|
||||
void Print();
|
||||
};
|
||||
|
||||
struct ErrorGeneric : ErrorBase {
|
||||
AddressDescription addr_description;
|
||||
uptr pc, bp, sp;
|
||||
uptr access_size;
|
||||
const char *bug_descr;
|
||||
bool is_write;
|
||||
u8 shadow_val;
|
||||
// VS2013 doesn't implement unrestricted unions, so we need a trivial default
|
||||
// constructor
|
||||
ErrorGeneric() = default;
|
||||
ErrorGeneric(u32 tid, uptr addr, uptr pc_, uptr bp_, uptr sp_, bool is_write_,
|
||||
uptr access_size_);
|
||||
void Print();
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
#define ASAN_FOR_EACH_ERROR_KIND(macro) \
|
||||
macro(StackOverflow) \
|
||||
macro(DeadlySignal) \
|
||||
macro(DoubleFree) \
|
||||
macro(NewDeleteSizeMismatch) \
|
||||
macro(FreeNotMalloced) \
|
||||
macro(AllocTypeMismatch) \
|
||||
macro(MallocUsableSizeNotOwned) \
|
||||
macro(SanitizerGetAllocatedSizeNotOwned) \
|
||||
macro(StringFunctionMemoryRangesOverlap) \
|
||||
macro(StringFunctionSizeOverflow) \
|
||||
macro(BadParamsToAnnotateContiguousContainer) \
|
||||
macro(ODRViolation) \
|
||||
macro(InvalidPointerPair) \
|
||||
macro(Generic)
|
||||
// clang-format on
|
||||
|
||||
#define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name,
|
||||
#define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name;
|
||||
#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \
|
||||
ErrorDescription(Error##name const &e) : kind(kErrorKind##name), name(e) {}
|
||||
#define ASAN_ERROR_DESCRIPTION_PRINT(name) \
|
||||
case kErrorKind##name: \
|
||||
return name.Print();
|
||||
|
||||
enum ErrorKind {
|
||||
kErrorKindInvalid = 0,
|
||||
ASAN_FOR_EACH_ERROR_KIND(ASAN_DEFINE_ERROR_KIND)
|
||||
};
|
||||
|
||||
struct ErrorDescription {
|
||||
ErrorKind kind;
|
||||
// We're using a tagged union because it allows us to have a trivially
|
||||
// copiable type and use the same structures as the public interface.
|
||||
//
|
||||
// We can add a wrapper around it to make it "more c++-like", but that would
|
||||
// add a lot of code and the benefit wouldn't be that big.
|
||||
union {
|
||||
ErrorBase Base;
|
||||
ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_MEMBER)
|
||||
};
|
||||
|
||||
ErrorDescription() { internal_memset(this, 0, sizeof(*this)); }
|
||||
ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_CONSTRUCTOR)
|
||||
|
||||
bool IsValid() { return kind != kErrorKindInvalid; }
|
||||
void Print() {
|
||||
switch (kind) {
|
||||
ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_PRINT)
|
||||
case kErrorKindInvalid:
|
||||
CHECK(0);
|
||||
}
|
||||
CHECK(0);
|
||||
}
|
||||
};
|
||||
|
||||
#undef ASAN_FOR_EACH_ERROR_KIND
|
||||
#undef ASAN_DEFINE_ERROR_KIND
|
||||
#undef ASAN_ERROR_DESCRIPTION_MEMBER
|
||||
#undef ASAN_ERROR_DESCRIPTION_CONSTRUCTOR
|
||||
#undef ASAN_ERROR_DESCRIPTION_PRINT
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_ERRORS_H
|
||||
|
|
@ -100,7 +100,7 @@ FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
|
|||
// if the signal arrives between checking and setting flags[pos], the
|
||||
// signal handler's fake stack will start from a different hint_position
|
||||
// and so will not touch this particular byte. So, it is safe to do this
|
||||
// with regular non-atimic load and store (at least I was not able to make
|
||||
// with regular non-atomic load and store (at least I was not able to make
|
||||
// this code crash).
|
||||
if (flags[pos]) continue;
|
||||
flags[pos] = 1;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ struct FakeFrame {
|
|||
// Allocate() flips the appropriate allocation flag atomically, thus achieving
|
||||
// async-signal safety.
|
||||
// This allocator does not have quarantine per se, but it tries to allocate the
|
||||
// frames in round robin fasion to maximize the delay between a deallocation
|
||||
// frames in round robin fashion to maximize the delay between a deallocation
|
||||
// and the next allocation.
|
||||
class FakeStack {
|
||||
static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B.
|
||||
|
|
@ -99,12 +99,12 @@ class FakeStack {
|
|||
return ((uptr)1) << (stack_size_log - kMinStackFrameSizeLog - class_id);
|
||||
}
|
||||
|
||||
// Divide n by the numbe of frames in size class.
|
||||
// Divide n by the number of frames in size class.
|
||||
static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) {
|
||||
return n & (NumberOfFrames(stack_size_log, class_id) - 1);
|
||||
}
|
||||
|
||||
// The the pointer to the flags of the given class_id.
|
||||
// The pointer to the flags of the given class_id.
|
||||
u8 *GetFlags(uptr stack_size_log, uptr class_id) {
|
||||
return reinterpret_cast<u8 *>(this) + kFlagsOffset +
|
||||
FlagsOffset(stack_size_log, class_id);
|
||||
|
|
|
|||
|
|
@ -156,9 +156,19 @@ void InitializeFlags() {
|
|||
f->quarantine_size_mb = f->quarantine_size >> 20;
|
||||
if (f->quarantine_size_mb < 0) {
|
||||
const int kDefaultQuarantineSizeMb =
|
||||
(ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8;
|
||||
(ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8;
|
||||
f->quarantine_size_mb = kDefaultQuarantineSizeMb;
|
||||
}
|
||||
if (f->thread_local_quarantine_size_kb < 0) {
|
||||
const u32 kDefaultThreadLocalQuarantineSizeKb =
|
||||
// It is not advised to go lower than 64Kb, otherwise quarantine batches
|
||||
// pushed from thread local quarantine to global one will create too
|
||||
// much overhead. One quarantine batch size is 8Kb and it holds up to
|
||||
// 1021 chunk, which amounts to 1/8 memory overhead per batch when
|
||||
// thread local quarantine is set to 64Kb.
|
||||
(ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10);
|
||||
f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb;
|
||||
}
|
||||
if (!f->replace_str && common_flags()->intercept_strlen) {
|
||||
Report("WARNING: strlen interceptor is enabled even though replace_str=0. "
|
||||
"Use intercept_strlen=0 to disable it.");
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ ASAN_FLAG(int, quarantine_size_mb, -1,
|
|||
"Size (in Mb) of quarantine used to detect use-after-free "
|
||||
"errors. Lower value may reduce memory usage but increase the "
|
||||
"chance of false negatives.")
|
||||
ASAN_FLAG(int, thread_local_quarantine_size_kb, -1,
|
||||
"Size (in Kb) of thread local quarantine used to detect "
|
||||
"use-after-free errors. Lower value may reduce memory usage but "
|
||||
"increase the chance of false negatives. It is not advised to go "
|
||||
"lower than 64Kb, otherwise frequent transfers to global quarantine "
|
||||
"might affect performance.")
|
||||
ASAN_FLAG(int, redzone, 16,
|
||||
"Minimal size (in bytes) of redzones around heap objects. "
|
||||
"Requirement: redzone >= 16, is a power of two.")
|
||||
|
|
@ -102,7 +108,7 @@ ASAN_FLAG(bool, poison_array_cookie, true,
|
|||
// https://github.com/google/sanitizers/issues/309
|
||||
// TODO(glider,timurrrr): Fix known issues and enable this back.
|
||||
ASAN_FLAG(bool, alloc_dealloc_mismatch,
|
||||
(SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0),
|
||||
!SANITIZER_MAC && !SANITIZER_WINDOWS && !SANITIZER_ANDROID,
|
||||
"Report errors on malloc/delete, new/free, new/delete[], etc.")
|
||||
|
||||
ASAN_FLAG(bool, new_delete_type_mismatch, true,
|
||||
|
|
@ -133,6 +139,9 @@ ASAN_FLAG(int, detect_odr_violation, 2,
|
|||
"have different sizes")
|
||||
ASAN_FLAG(bool, dump_instruction_bytes, false,
|
||||
"If true, dump 16 bytes starting at the instruction that caused SEGV")
|
||||
ASAN_FLAG(bool, dump_registers, true,
|
||||
"If true, dump values of CPU registers when SEGV happens. Only "
|
||||
"available on OS X for now.")
|
||||
ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
|
||||
ASAN_FLAG(bool, halt_on_error, true,
|
||||
"Crash the program after printing the first error report "
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
|
|
@ -123,18 +124,6 @@ int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
|
|||
return res;
|
||||
}
|
||||
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
|
||||
Global g = {};
|
||||
if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
|
||||
internal_strncpy(descr->name, g.name, descr->name_size);
|
||||
descr->region_address = g.beg;
|
||||
descr->region_size = g.size;
|
||||
descr->region_kind = "global";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum GlobalSymbolState {
|
||||
UNREGISTERED = 0,
|
||||
REGISTERED = 1
|
||||
|
|
@ -279,6 +268,46 @@ void StopInitOrderChecking() {
|
|||
}
|
||||
}
|
||||
|
||||
static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
|
||||
|
||||
const char *MaybeDemangleGlobalName(const char *name) {
|
||||
// We can spoil names of globals with C linkage, so use an heuristic
|
||||
// approach to check if the name should be demangled.
|
||||
bool should_demangle = false;
|
||||
if (name[0] == '_' && name[1] == 'Z')
|
||||
should_demangle = true;
|
||||
else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
|
||||
should_demangle = true;
|
||||
|
||||
return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
|
||||
}
|
||||
|
||||
// Check if the global is a zero-terminated ASCII string. If so, print it.
|
||||
void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) {
|
||||
for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
|
||||
unsigned char c = *(unsigned char *)p;
|
||||
if (c == '\0' || !IsASCII(c)) return;
|
||||
}
|
||||
if (*(char *)(g.beg + g.size - 1) != '\0') return;
|
||||
str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
|
||||
(char *)g.beg);
|
||||
}
|
||||
|
||||
static const char *GlobalFilename(const __asan_global &g) {
|
||||
const char *res = g.module_name;
|
||||
// Prefer the filename from source location, if is available.
|
||||
if (g.location) res = g.location->filename;
|
||||
CHECK(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) {
|
||||
str->append("%s", GlobalFilename(g));
|
||||
if (!g.location) return;
|
||||
if (g.location->line_no) str->append(":%d", g.location->line_no);
|
||||
if (g.location->column_no) str->append(":%d", g.location->column_no);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
|
|
@ -319,6 +348,20 @@ void __asan_register_globals(__asan_global *globals, uptr n) {
|
|||
Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
|
||||
}
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (SANITIZER_WINDOWS && globals[i].beg == 0) {
|
||||
// The MSVC incremental linker may pad globals out to 256 bytes. As long
|
||||
// as __asan_global is less than 256 bytes large and its size is a power
|
||||
// of two, we can skip over the padding.
|
||||
static_assert(
|
||||
sizeof(__asan_global) < 256 &&
|
||||
(sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0,
|
||||
"sizeof(__asan_global) incompatible with incremental linker padding");
|
||||
// If these are padding bytes, the rest of the global should be zero.
|
||||
CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 &&
|
||||
globals[i].name == nullptr && globals[i].module_name == nullptr &&
|
||||
globals[i].odr_indicator == 0);
|
||||
continue;
|
||||
}
|
||||
RegisterGlobal(&globals[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -329,6 +372,11 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
|
|||
if (!flags()->report_globals) return;
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (SANITIZER_WINDOWS && globals[i].beg == 0) {
|
||||
// Skip globals that look like padding from the MSVC incremental linker.
|
||||
// See comment in __asan_register_globals.
|
||||
continue;
|
||||
}
|
||||
UnregisterGlobal(&globals[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -339,10 +387,10 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
|
|||
// initializer can only touch global variables in the same TU.
|
||||
void __asan_before_dynamic_init(const char *module_name) {
|
||||
if (!flags()->check_initialization_order ||
|
||||
!CanPoisonMemory())
|
||||
!CanPoisonMemory() ||
|
||||
!dynamic_init_globals)
|
||||
return;
|
||||
bool strict_init_order = flags()->strict_init_order;
|
||||
CHECK(dynamic_init_globals);
|
||||
CHECK(module_name);
|
||||
CHECK(asan_inited);
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
|
|
@ -365,7 +413,8 @@ void __asan_before_dynamic_init(const char *module_name) {
|
|||
// TU are poisoned. It simply unpoisons all dynamically initialized globals.
|
||||
void __asan_after_dynamic_init() {
|
||||
if (!flags()->check_initialization_order ||
|
||||
!CanPoisonMemory())
|
||||
!CanPoisonMemory() ||
|
||||
!dynamic_init_globals)
|
||||
return;
|
||||
CHECK(asan_inited);
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
|
|
|
|||
62
lib/asan/asan_globals_win.cc
Normal file
62
lib/asan/asan_globals_win.cc
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
//===-- asan_globals_win.cc -----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Global registration code that is linked into every Windows DLL and EXE.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_interface_internal.h"
|
||||
#if SANITIZER_WINDOWS
|
||||
|
||||
namespace __asan {
|
||||
|
||||
#pragma section(".ASAN$GA", read, write) // NOLINT
|
||||
#pragma section(".ASAN$GZ", read, write) // NOLINT
|
||||
extern "C" __declspec(allocate(".ASAN$GA"))
|
||||
__asan_global __asan_globals_start = {};
|
||||
extern "C" __declspec(allocate(".ASAN$GZ"))
|
||||
__asan_global __asan_globals_end = {};
|
||||
#pragma comment(linker, "/merge:.ASAN=.data")
|
||||
|
||||
static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
|
||||
__asan_global *start = &__asan_globals_start + 1;
|
||||
__asan_global *end = &__asan_globals_end;
|
||||
uptr bytediff = (uptr)end - (uptr)start;
|
||||
if (bytediff % sizeof(__asan_global) != 0) {
|
||||
#ifdef ASAN_DLL_THUNK
|
||||
__debugbreak();
|
||||
#else
|
||||
CHECK("corrupt asan global array");
|
||||
#endif
|
||||
}
|
||||
// We know end >= start because the linker sorts the portion after the dollar
|
||||
// sign alphabetically.
|
||||
uptr n = end - start;
|
||||
hook(start, n);
|
||||
}
|
||||
|
||||
static void register_dso_globals() {
|
||||
call_on_globals(&__asan_register_globals);
|
||||
}
|
||||
|
||||
static void unregister_dso_globals() {
|
||||
call_on_globals(&__asan_unregister_globals);
|
||||
}
|
||||
|
||||
// Register globals
|
||||
#pragma section(".CRT$XCU", long, read) // NOLINT
|
||||
#pragma section(".CRT$XTX", long, read) // NOLINT
|
||||
extern "C" __declspec(allocate(".CRT$XCU"))
|
||||
void (*const __asan_dso_reg_hook)() = ®ister_dso_globals;
|
||||
extern "C" __declspec(allocate(".CRT$XTX"))
|
||||
void (*const __asan_dso_unreg_hook)() = &unregister_dso_globals;
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_WINDOWS
|
||||
34
lib/asan/asan_globals_win.h
Normal file
34
lib/asan/asan_globals_win.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//===-- asan_globals_win.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Interface to the Windows-specific global management code. Separated into a
|
||||
// standalone header to allow inclusion from asan_win_dynamic_runtime_thunk,
|
||||
// which defines symbols that clash with other sanitizer headers.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_GLOBALS_WIN_H
|
||||
#define ASAN_GLOBALS_WIN_H
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
#error "this file is Windows-only, and uses MSVC pragmas"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
#define SANITIZER_SYM_PREFIX
|
||||
#else
|
||||
#define SANITIZER_SYM_PREFIX "_"
|
||||
#endif
|
||||
|
||||
// Use this macro to force linking asan_globals_win.cc into the DSO.
|
||||
#define ASAN_LINK_GLOBALS_WIN() \
|
||||
__pragma( \
|
||||
comment(linker, "/include:" SANITIZER_SYM_PREFIX "__asan_dso_reg_hook"))
|
||||
|
||||
#endif // ASAN_GLOBALS_WIN_H
|
||||
|
|
@ -81,6 +81,51 @@ struct AsanInterceptorContext {
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
// memcpy is called during __asan_init() from the internals of printf(...).
|
||||
// We do not treat memcpy with to==from as a bug.
|
||||
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
|
||||
#define ASAN_MEMCPY_IMPL(ctx, to, from, size) \
|
||||
do { \
|
||||
if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \
|
||||
if (asan_init_is_running) { \
|
||||
return REAL(memcpy)(to, from, size); \
|
||||
} \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
if (flags()->replace_intrin) { \
|
||||
if (to != from) { \
|
||||
CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \
|
||||
} \
|
||||
ASAN_READ_RANGE(ctx, from, size); \
|
||||
ASAN_WRITE_RANGE(ctx, to, size); \
|
||||
} \
|
||||
return REAL(memcpy)(to, from, size); \
|
||||
} while (0)
|
||||
|
||||
// memset is called inside Printf.
|
||||
#define ASAN_MEMSET_IMPL(ctx, block, c, size) \
|
||||
do { \
|
||||
if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \
|
||||
if (asan_init_is_running) { \
|
||||
return REAL(memset)(block, c, size); \
|
||||
} \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
if (flags()->replace_intrin) { \
|
||||
ASAN_WRITE_RANGE(ctx, block, size); \
|
||||
} \
|
||||
return REAL(memset)(block, c, size); \
|
||||
} while (0)
|
||||
|
||||
#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) \
|
||||
do { \
|
||||
if (UNLIKELY(!asan_inited)) return internal_memmove(to, from, size); \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
if (flags()->replace_intrin) { \
|
||||
ASAN_READ_RANGE(ctx, from, size); \
|
||||
ASAN_WRITE_RANGE(ctx, to, size); \
|
||||
} \
|
||||
return internal_memmove(to, from, size); \
|
||||
} while (0)
|
||||
|
||||
#define ASAN_READ_RANGE(ctx, offset, size) \
|
||||
ACCESS_MEMORY_RANGE(ctx, offset, size, false)
|
||||
#define ASAN_WRITE_RANGE(ctx, offset, size) \
|
||||
|
|
@ -198,10 +243,25 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
|
|||
} else { \
|
||||
*begin = *end = 0; \
|
||||
}
|
||||
// Asan needs custom handling of these:
|
||||
#undef SANITIZER_INTERCEPT_MEMSET
|
||||
#undef SANITIZER_INTERCEPT_MEMMOVE
|
||||
#undef SANITIZER_INTERCEPT_MEMCPY
|
||||
|
||||
#define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \
|
||||
do { \
|
||||
ASAN_INTERCEPTOR_ENTER(ctx, memmove); \
|
||||
ASAN_MEMMOVE_IMPL(ctx, to, from, size); \
|
||||
} while (false)
|
||||
|
||||
#define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \
|
||||
do { \
|
||||
ASAN_INTERCEPTOR_ENTER(ctx, memcpy); \
|
||||
ASAN_MEMCPY_IMPL(ctx, to, from, size); \
|
||||
} while (false)
|
||||
|
||||
#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \
|
||||
do { \
|
||||
ASAN_INTERCEPTOR_ENTER(ctx, memset); \
|
||||
ASAN_MEMSET_IMPL(ctx, block, c, size); \
|
||||
} while (false)
|
||||
|
||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||
|
||||
// Syscall interceptors don't have contexts, we don't support suppressions
|
||||
|
|
@ -389,90 +449,18 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// memcpy is called during __asan_init() from the internals of printf(...).
|
||||
// We do not treat memcpy with to==from as a bug.
|
||||
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
|
||||
#define ASAN_MEMCPY_IMPL(ctx, to, from, size) do { \
|
||||
if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \
|
||||
if (asan_init_is_running) { \
|
||||
return REAL(memcpy)(to, from, size); \
|
||||
} \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
if (flags()->replace_intrin) { \
|
||||
if (to != from) { \
|
||||
CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \
|
||||
} \
|
||||
ASAN_READ_RANGE(ctx, from, size); \
|
||||
ASAN_WRITE_RANGE(ctx, to, size); \
|
||||
} \
|
||||
return REAL(memcpy)(to, from, size); \
|
||||
} while (0)
|
||||
|
||||
|
||||
void *__asan_memcpy(void *to, const void *from, uptr size) {
|
||||
ASAN_MEMCPY_IMPL(nullptr, to, from, size);
|
||||
}
|
||||
|
||||
// memset is called inside Printf.
|
||||
#define ASAN_MEMSET_IMPL(ctx, block, c, size) do { \
|
||||
if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \
|
||||
if (asan_init_is_running) { \
|
||||
return REAL(memset)(block, c, size); \
|
||||
} \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
if (flags()->replace_intrin) { \
|
||||
ASAN_WRITE_RANGE(ctx, block, size); \
|
||||
} \
|
||||
return REAL(memset)(block, c, size); \
|
||||
} while (0)
|
||||
|
||||
void *__asan_memset(void *block, int c, uptr size) {
|
||||
ASAN_MEMSET_IMPL(nullptr, block, c, size);
|
||||
}
|
||||
|
||||
#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) do { \
|
||||
if (UNLIKELY(!asan_inited)) \
|
||||
return internal_memmove(to, from, size); \
|
||||
ENSURE_ASAN_INITED(); \
|
||||
if (flags()->replace_intrin) { \
|
||||
ASAN_READ_RANGE(ctx, from, size); \
|
||||
ASAN_WRITE_RANGE(ctx, to, size); \
|
||||
} \
|
||||
return internal_memmove(to, from, size); \
|
||||
} while (0)
|
||||
|
||||
void *__asan_memmove(void *to, const void *from, uptr size) {
|
||||
ASAN_MEMMOVE_IMPL(nullptr, to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
|
||||
void *ctx;
|
||||
ASAN_INTERCEPTOR_ENTER(ctx, memmove);
|
||||
ASAN_MEMMOVE_IMPL(ctx, to, from, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
|
||||
void *ctx;
|
||||
ASAN_INTERCEPTOR_ENTER(ctx, memcpy);
|
||||
#if !SANITIZER_MAC
|
||||
ASAN_MEMCPY_IMPL(ctx, to, from, size);
|
||||
#else
|
||||
// At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced
|
||||
// with WRAP(memcpy). As a result, false positives are reported for memmove()
|
||||
// calls. If we just disable error reporting with
|
||||
// ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with
|
||||
// internal_memcpy(), which may lead to crashes, see
|
||||
// http://llvm.org/bugs/show_bug.cgi?id=16362.
|
||||
ASAN_MEMMOVE_IMPL(ctx, to, from, size);
|
||||
#endif // !SANITIZER_MAC
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
|
||||
void *ctx;
|
||||
ASAN_INTERCEPTOR_ENTER(ctx, memset);
|
||||
ASAN_MEMSET_IMPL(ctx, block, c, size);
|
||||
}
|
||||
|
||||
#if ASAN_INTERCEPT_INDEX
|
||||
# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
|
||||
INTERCEPTOR(char*, index, const char *string, int c)
|
||||
|
|
@ -720,19 +708,10 @@ INTERCEPTOR(int, fork, void) {
|
|||
namespace __asan {
|
||||
void InitializeAsanInterceptors() {
|
||||
static bool was_called_once;
|
||||
CHECK(was_called_once == false);
|
||||
CHECK(!was_called_once);
|
||||
was_called_once = true;
|
||||
InitializeCommonInterceptors();
|
||||
|
||||
// Intercept mem* functions.
|
||||
ASAN_INTERCEPT_FUNC(memcpy);
|
||||
ASAN_INTERCEPT_FUNC(memset);
|
||||
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
|
||||
// In asan, REAL(memmove) is not used, but it is used in msan.
|
||||
ASAN_INTERCEPT_FUNC(memmove);
|
||||
}
|
||||
CHECK(REAL(memcpy));
|
||||
|
||||
// Intercept str* functions.
|
||||
ASAN_INTERCEPT_FUNC(strcat); // NOLINT
|
||||
ASAN_INTERCEPT_FUNC(strcpy); // NOLINT
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#include "asan_init_version.h"
|
||||
|
||||
using __sanitizer::uptr;
|
||||
using __sanitizer::u64;
|
||||
using __sanitizer::u32;
|
||||
|
||||
extern "C" {
|
||||
// This function should be called at the very beginning of the process,
|
||||
|
|
@ -79,6 +81,20 @@ extern "C" {
|
|||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_after_dynamic_init();
|
||||
|
||||
// Sets bytes of the given range of the shadow memory into specific value.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_shadow_00(uptr addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_shadow_f1(uptr addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_shadow_f2(uptr addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_shadow_f3(uptr addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_shadow_f5(uptr addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_shadow_f8(uptr addr, uptr size);
|
||||
|
||||
// These two functions are used by instrumented code in the
|
||||
// use-after-scope mode. They mark memory for local variables as
|
||||
// unaddressable when they leave scope and addressable before the
|
||||
|
|
@ -156,6 +172,9 @@ extern "C" {
|
|||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ const char* __asan_default_options();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
extern uptr __asan_shadow_memory_dynamic_address;
|
||||
|
||||
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
extern int __asan_option_detect_stack_use_after_return;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
// If set, values like allocator chunk size, as well as defaults for some flags
|
||||
// will be changed towards less memory overhead.
|
||||
#ifndef ASAN_LOW_MEMORY
|
||||
# if SANITIZER_IOS || (SANITIZER_WORDSIZE == 32)
|
||||
# if SANITIZER_IOS || SANITIZER_ANDROID
|
||||
# define ASAN_LOW_MEMORY 1
|
||||
# else
|
||||
# define ASAN_LOW_MEMORY 0
|
||||
|
|
@ -65,6 +65,9 @@ void AsanInitFromRtl();
|
|||
// asan_win.cc
|
||||
void InitializePlatformExceptionHandlers();
|
||||
|
||||
// asan_win.cc / asan_posix.cc
|
||||
const char *DescribeSignalOrException(int signo);
|
||||
|
||||
// asan_rtl.cc
|
||||
void NORETURN ShowStatsAndAbort();
|
||||
|
||||
|
|
@ -100,17 +103,6 @@ void *AsanDlSymNext(const char *sym);
|
|||
|
||||
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
|
||||
|
||||
// Platform-specific options.
|
||||
#if SANITIZER_MAC
|
||||
bool PlatformHasDifferentMemcpyAndMemmove();
|
||||
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \
|
||||
(PlatformHasDifferentMemcpyAndMemmove())
|
||||
#elif SANITIZER_WINDOWS64
|
||||
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false
|
||||
#else
|
||||
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
|
||||
#endif // SANITIZER_MAC
|
||||
|
||||
// Add convenient macro for interface functions that may be represented as
|
||||
// weak hooks.
|
||||
#define ASAN_MALLOC_HOOK(ptr, size) \
|
||||
|
|
@ -132,12 +124,10 @@ extern bool asan_init_is_running;
|
|||
extern void (*death_callback)(void);
|
||||
// These magic values are written to shadow for better error reporting.
|
||||
const int kAsanHeapLeftRedzoneMagic = 0xfa;
|
||||
const int kAsanHeapRightRedzoneMagic = 0xfb;
|
||||
const int kAsanHeapFreeMagic = 0xfd;
|
||||
const int kAsanStackLeftRedzoneMagic = 0xf1;
|
||||
const int kAsanStackMidRedzoneMagic = 0xf2;
|
||||
const int kAsanStackRightRedzoneMagic = 0xf3;
|
||||
const int kAsanStackPartialRedzoneMagic = 0xf4;
|
||||
const int kAsanStackAfterReturnMagic = 0xf5;
|
||||
const int kAsanInitializationOrderMagic = 0xf6;
|
||||
const int kAsanUserPoisonedMemoryMagic = 0xf7;
|
||||
|
|
|
|||
|
|
@ -49,15 +49,6 @@ namespace __asan {
|
|||
void InitializePlatformInterceptors() {}
|
||||
void InitializePlatformExceptionHandlers() {}
|
||||
|
||||
bool PlatformHasDifferentMemcpyAndMemmove() {
|
||||
// On OS X 10.7 memcpy() and memmove() are both resolved
|
||||
// into memmove$VARIANT$sse42.
|
||||
// See also https://github.com/google/sanitizers/issues/34.
|
||||
// TODO(glider): need to check dynamically that memcpy() and memmove() are
|
||||
// actually the same function.
|
||||
return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
|
||||
}
|
||||
|
||||
// No-op. Mac does not support static linkage anyway.
|
||||
void *AsanDoesNotSupportStaticLinkage() {
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,13 @@ INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
|
|||
if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
|
||||
uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
|
||||
uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
|
||||
void *new_ptr = asan_malloc(size, &stack);
|
||||
void *new_ptr;
|
||||
if (UNLIKELY(!asan_inited)) {
|
||||
new_ptr = AllocateFromLocalPool(size);
|
||||
} else {
|
||||
copy_size = size;
|
||||
new_ptr = asan_malloc(copy_size, &stack);
|
||||
}
|
||||
internal_memcpy(new_ptr, ptr, copy_size);
|
||||
return new_ptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,11 @@ void *_recalloc(void *p, size_t n, size_t elem_size) {
|
|||
return realloc(p, size);
|
||||
}
|
||||
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_recalloc_base(void *p, size_t n, size_t elem_size) {
|
||||
return _recalloc(p, n, elem_size);
|
||||
}
|
||||
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
size_t _msize(const void *ptr) {
|
||||
GET_CURRENT_PC_BP_SP;
|
||||
|
|
@ -223,6 +228,7 @@ void ReplaceSystemMalloc() {
|
|||
TryToOverrideFunction("_realloc_base", (uptr)realloc);
|
||||
TryToOverrideFunction("_realloc_crt", (uptr)realloc);
|
||||
TryToOverrideFunction("_recalloc", (uptr)_recalloc);
|
||||
TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
|
||||
TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
|
||||
TryToOverrideFunction("_msize", (uptr)_msize);
|
||||
TryToOverrideFunction("_expand", (uptr)_expand);
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@
|
|||
// || `[0x00000000, 0x2fffffff]` || LowMem ||
|
||||
|
||||
static const u64 kDefaultShadowScale = 3;
|
||||
static const u64 kDefaultShadowSentinel = ~(uptr)0;
|
||||
static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000
|
||||
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
|
||||
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
|
||||
|
|
@ -140,7 +141,6 @@ static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52;
|
|||
static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
|
||||
static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
|
||||
static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
static const u64 kWindowsShadowOffset64 = 1ULL << 45; // 32TB
|
||||
|
||||
#define SHADOW_SCALE kDefaultShadowScale
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ static const u64 kWindowsShadowOffset64 = 1ULL << 45; // 32TB
|
|||
# if SANITIZER_IOSSIM
|
||||
# define SHADOW_OFFSET kIosSimShadowOffset64
|
||||
# else
|
||||
# define SHADOW_OFFSET kIosShadowOffset64
|
||||
# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
|
||||
# endif
|
||||
# elif defined(__aarch64__)
|
||||
# define SHADOW_OFFSET kAArch64_ShadowOffset64
|
||||
|
|
@ -183,7 +183,7 @@ static const u64 kWindowsShadowOffset64 = 1ULL << 45; // 32TB
|
|||
# elif defined(__mips64)
|
||||
# define SHADOW_OFFSET kMIPS64_ShadowOffset64
|
||||
# elif SANITIZER_WINDOWS64
|
||||
# define SHADOW_OFFSET kWindowsShadowOffset64
|
||||
# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
|
||||
# else
|
||||
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
|
||||
# endif
|
||||
|
|
@ -269,9 +269,25 @@ static inline bool AddrIsInMidMem(uptr a) {
|
|||
return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInShadowGap(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
if (kMidMemBeg) {
|
||||
if (a <= kShadowGapEnd)
|
||||
return SHADOW_OFFSET == 0 || a >= kShadowGapBeg;
|
||||
return (a >= kShadowGap2Beg && a <= kShadowGap2End) ||
|
||||
(a >= kShadowGap3Beg && a <= kShadowGap3End);
|
||||
}
|
||||
// In zero-based shadow mode we treat addresses near zero as addresses
|
||||
// in shadow gap as well.
|
||||
if (SHADOW_OFFSET == 0)
|
||||
return a <= kShadowGapEnd;
|
||||
return a >= kShadowGapBeg && a <= kShadowGapEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInMem(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a);
|
||||
return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a) ||
|
||||
(flags()->protect_shadow_gap == 0 && AddrIsInShadowGap(a));
|
||||
}
|
||||
|
||||
static inline uptr MemToShadow(uptr p) {
|
||||
|
|
@ -295,21 +311,6 @@ static inline bool AddrIsInShadow(uptr a) {
|
|||
return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a);
|
||||
}
|
||||
|
||||
static inline bool AddrIsInShadowGap(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
if (kMidMemBeg) {
|
||||
if (a <= kShadowGapEnd)
|
||||
return SHADOW_OFFSET == 0 || a >= kShadowGapBeg;
|
||||
return (a >= kShadowGap2Beg && a <= kShadowGap2End) ||
|
||||
(a >= kShadowGap3Beg && a <= kShadowGap3End);
|
||||
}
|
||||
// In zero-based shadow mode we treat addresses near zero as addresses
|
||||
// in shadow gap as well.
|
||||
if (SHADOW_OFFSET == 0)
|
||||
return a <= kShadowGapEnd;
|
||||
return a >= kShadowGapBeg && a <= kShadowGapEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsAlignedByGranularity(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
return (a & (SHADOW_GRANULARITY - 1)) == 0;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class HeapProfile {
|
|||
|
||||
static void ChunkCallback(uptr chunk, void *arg) {
|
||||
HeapProfile *hp = reinterpret_cast<HeapProfile*>(arg);
|
||||
AsanChunkView cv = FindHeapChunkByAddress(chunk);
|
||||
AsanChunkView cv = FindHeapChunkByAllocBeg(chunk);
|
||||
if (!cv.IsAllocated()) return;
|
||||
u32 id = cv.GetAllocStackId();
|
||||
if (!id) return;
|
||||
|
|
|
|||
|
|
@ -45,26 +45,6 @@
|
|||
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
// This code has issues on OSX.
|
||||
// See https://github.com/google/sanitizers/issues/131.
|
||||
|
||||
// Fake std::nothrow_t to avoid including <new>.
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
} // namespace std
|
||||
|
||||
#define OPERATOR_NEW_BODY(type) \
|
||||
GET_STACK_TRACE_MALLOC;\
|
||||
return asan_memalign(0, size, &stack, type);
|
||||
|
||||
// On OS X it's not enough to just provide our own 'operator new' and
|
||||
// 'operator delete' implementations, because they're going to be in the
|
||||
// runtime dylib, and the main executable will depend on both the runtime
|
||||
// dylib and libstdc++, each of those'll have its implementation of new and
|
||||
// delete.
|
||||
// To make sure that C++ allocation/deallocation operators are overridden on
|
||||
// OS X we need to intercept them using their mangled names.
|
||||
#if !SANITIZER_MAC
|
||||
// FreeBSD prior v9.2 have wrong definition of 'size_t'.
|
||||
// http://svnweb.freebsd.org/base?view=revision&revision=232261
|
||||
#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
|
||||
|
|
@ -74,6 +54,30 @@ struct nothrow_t {};
|
|||
#endif // __FreeBSD_version
|
||||
#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
|
||||
|
||||
// This code has issues on OSX.
|
||||
// See https://github.com/google/sanitizers/issues/131.
|
||||
|
||||
// Fake std::nothrow_t and std::align_val_t to avoid including <new>.
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
enum class align_val_t: size_t {};
|
||||
} // namespace std
|
||||
|
||||
#define OPERATOR_NEW_BODY(type) \
|
||||
GET_STACK_TRACE_MALLOC;\
|
||||
return asan_memalign(0, size, &stack, type);
|
||||
#define OPERATOR_NEW_BODY_ALIGN(type) \
|
||||
GET_STACK_TRACE_MALLOC;\
|
||||
return asan_memalign((uptr)align, size, &stack, type);
|
||||
|
||||
// On OS X it's not enough to just provide our own 'operator new' and
|
||||
// 'operator delete' implementations, because they're going to be in the
|
||||
// runtime dylib, and the main executable will depend on both the runtime
|
||||
// dylib and libstdc++, each of those'll have its implementation of new and
|
||||
// delete.
|
||||
// To make sure that C++ allocation/deallocation operators are overridden on
|
||||
// OS X we need to intercept them using their mangled names.
|
||||
#if !SANITIZER_MAC
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
|
|
@ -84,6 +88,18 @@ void *operator new(size_t size, std::nothrow_t const&)
|
|||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new[](size_t size, std::nothrow_t const&)
|
||||
{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new(size_t size, std::align_val_t align)
|
||||
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); }
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new[](size_t size, std::align_val_t align)
|
||||
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); }
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&)
|
||||
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); }
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&)
|
||||
{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); }
|
||||
|
||||
#else // SANITIZER_MAC
|
||||
INTERCEPTOR(void *, _Znwm, size_t size) {
|
||||
|
|
@ -131,6 +147,32 @@ void operator delete[](void *ptr, size_t size) NOEXCEPT {
|
|||
GET_STACK_TRACE_FREE;
|
||||
asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete(void *ptr, std::align_val_t) NOEXCEPT {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete[](void *ptr, std::align_val_t) NOEXCEPT {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete(void *ptr, std::align_val_t, std::nothrow_t const&) {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete[](void *ptr, std::align_val_t, std::nothrow_t const&) {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete(void *ptr, size_t size, std::align_val_t) NOEXCEPT {
|
||||
GET_STACK_TRACE_FREE;
|
||||
asan_sized_free(ptr, size, &stack, FROM_NEW);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete[](void *ptr, size_t size, std::align_val_t) NOEXCEPT {
|
||||
GET_STACK_TRACE_FREE;
|
||||
asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
|
||||
}
|
||||
|
||||
#else // SANITIZER_MAC
|
||||
INTERCEPTOR(void, _ZdlPv, void *ptr) {
|
||||
|
|
|
|||
|
|
@ -64,12 +64,9 @@ struct ShadowSegmentEndpoint {
|
|||
};
|
||||
|
||||
void FlushUnneededASanShadowMemory(uptr p, uptr size) {
|
||||
// Since asan's mapping is compacting, the shadow chunk may be
|
||||
// not page-aligned, so we only flush the page-aligned portion.
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
|
||||
uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
|
||||
FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
|
||||
// Since asan's mapping is compacting, the shadow chunk may be
|
||||
// not page-aligned, so we only flush the page-aligned portion.
|
||||
ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
|
||||
}
|
||||
|
||||
void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
|
||||
|
|
@ -117,9 +114,9 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
|
|||
ShadowSegmentEndpoint beg(beg_addr);
|
||||
ShadowSegmentEndpoint end(end_addr);
|
||||
if (beg.chunk == end.chunk) {
|
||||
CHECK(beg.offset < end.offset);
|
||||
CHECK_LT(beg.offset, end.offset);
|
||||
s8 value = beg.value;
|
||||
CHECK(value == end.value);
|
||||
CHECK_EQ(value, end.value);
|
||||
// We can only poison memory if the byte in end.offset is unaddressable.
|
||||
// No need to re-poison memory if it is poisoned already.
|
||||
if (value > 0 && value <= end.offset) {
|
||||
|
|
@ -131,7 +128,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
CHECK(beg.chunk < end.chunk);
|
||||
CHECK_LT(beg.chunk, end.chunk);
|
||||
if (beg.offset > 0) {
|
||||
// Mark bytes from beg.offset as unaddressable.
|
||||
if (beg.value == 0) {
|
||||
|
|
@ -157,9 +154,9 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
|
|||
ShadowSegmentEndpoint beg(beg_addr);
|
||||
ShadowSegmentEndpoint end(end_addr);
|
||||
if (beg.chunk == end.chunk) {
|
||||
CHECK(beg.offset < end.offset);
|
||||
CHECK_LT(beg.offset, end.offset);
|
||||
s8 value = beg.value;
|
||||
CHECK(value == end.value);
|
||||
CHECK_EQ(value, end.value);
|
||||
// We unpoison memory bytes up to enbytes up to end.offset if it is not
|
||||
// unpoisoned already.
|
||||
if (value != 0) {
|
||||
|
|
@ -167,7 +164,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
CHECK(beg.chunk < end.chunk);
|
||||
CHECK_LT(beg.chunk, end.chunk);
|
||||
if (beg.offset > 0) {
|
||||
*beg.chunk = 0;
|
||||
beg.chunk++;
|
||||
|
|
@ -314,6 +311,30 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
|
|||
}
|
||||
}
|
||||
|
||||
void __asan_set_shadow_00(uptr addr, uptr size) {
|
||||
REAL(memset)((void *)addr, 0, size);
|
||||
}
|
||||
|
||||
void __asan_set_shadow_f1(uptr addr, uptr size) {
|
||||
REAL(memset)((void *)addr, 0xf1, size);
|
||||
}
|
||||
|
||||
void __asan_set_shadow_f2(uptr addr, uptr size) {
|
||||
REAL(memset)((void *)addr, 0xf2, size);
|
||||
}
|
||||
|
||||
void __asan_set_shadow_f3(uptr addr, uptr size) {
|
||||
REAL(memset)((void *)addr, 0xf3, size);
|
||||
}
|
||||
|
||||
void __asan_set_shadow_f5(uptr addr, uptr size) {
|
||||
REAL(memset)((void *)addr, 0xf5, size);
|
||||
}
|
||||
|
||||
void __asan_set_shadow_f8(uptr addr, uptr size) {
|
||||
REAL(memset)((void *)addr, 0xf8, size);
|
||||
}
|
||||
|
||||
void __asan_poison_stack_memory(uptr addr, uptr size) {
|
||||
VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
|
||||
PoisonAlignedStackMemory(addr, size, true);
|
||||
|
|
@ -388,7 +409,7 @@ const void *__sanitizer_contiguous_container_find_bad_address(
|
|||
// ending with end.
|
||||
uptr kMaxRangeToCheck = 32;
|
||||
uptr r1_beg = beg;
|
||||
uptr r1_end = Min(end + kMaxRangeToCheck, mid);
|
||||
uptr r1_end = Min(beg + kMaxRangeToCheck, mid);
|
||||
uptr r2_beg = Max(beg, mid - kMaxRangeToCheck);
|
||||
uptr r2_end = Min(end, mid + kMaxRangeToCheck);
|
||||
uptr r3_beg = Max(end - kMaxRangeToCheck, mid);
|
||||
|
|
|
|||
|
|
@ -86,8 +86,8 @@ ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
|
|||
}
|
||||
}
|
||||
|
||||
// Calls __sanitizer::FlushUnneededShadowMemory() on
|
||||
// [MemToShadow(p), MemToShadow(p+size)] with proper rounding.
|
||||
// Calls __sanitizer::ReleaseMemoryPagesToOS() on
|
||||
// [MemToShadow(p), MemToShadow(p+size)].
|
||||
void FlushUnneededASanShadowMemory(uptr p, uptr size);
|
||||
|
||||
} // namespace __asan
|
||||
|
|
|
|||
|
|
@ -33,6 +33,19 @@
|
|||
|
||||
namespace __asan {
|
||||
|
||||
const char *DescribeSignalOrException(int signo) {
|
||||
switch (signo) {
|
||||
case SIGFPE:
|
||||
return "FPE";
|
||||
case SIGILL:
|
||||
return "ILL";
|
||||
case SIGABRT:
|
||||
return "ABRT";
|
||||
default:
|
||||
return "SEGV";
|
||||
}
|
||||
}
|
||||
|
||||
void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
|
||||
ScopedDeadlySignal signal_scope(GetCurrentThread());
|
||||
int code = (int)((siginfo_t*)siginfo)->si_code;
|
||||
|
|
@ -84,12 +97,8 @@ void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
|
|||
// unaligned memory access.
|
||||
if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
|
||||
ReportStackOverflow(sig);
|
||||
else if (signo == SIGFPE)
|
||||
ReportDeadlySignal("FPE", sig);
|
||||
else if (signo == SIGILL)
|
||||
ReportDeadlySignal("ILL", sig);
|
||||
else
|
||||
ReportDeadlySignal("SEGV", sig);
|
||||
ReportDeadlySignal(signo, sig);
|
||||
}
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -25,35 +25,29 @@ struct StackVarDescr {
|
|||
uptr name_len;
|
||||
};
|
||||
|
||||
struct AddressDescription {
|
||||
char *name;
|
||||
uptr name_size;
|
||||
uptr region_address;
|
||||
uptr region_size;
|
||||
const char *region_kind;
|
||||
};
|
||||
|
||||
// Returns the number of globals close to the provided address and copies
|
||||
// them to "globals" array.
|
||||
int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites,
|
||||
int max_globals);
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
|
||||
|
||||
const char *MaybeDemangleGlobalName(const char *name);
|
||||
void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g);
|
||||
void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g);
|
||||
|
||||
void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
|
||||
bool in_shadow, const char *after = "\n");
|
||||
|
||||
// The following functions prints address description depending
|
||||
// on the memory type (shadow/heap/stack/global).
|
||||
void DescribeHeapAddress(uptr addr, uptr access_size);
|
||||
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
|
||||
bool print = true);
|
||||
bool ParseFrameDescription(const char *frame_descr,
|
||||
InternalMmapVector<StackVarDescr> *vars);
|
||||
bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
||||
void DescribeThread(AsanThreadContext *context);
|
||||
|
||||
// Different kinds of error reports.
|
||||
void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
|
||||
uptr access_size, u32 exp, bool fatal);
|
||||
void ReportStackOverflow(const SignalContext &sig);
|
||||
void ReportDeadlySignal(const char *description, const SignalContext &sig);
|
||||
void ReportNewDeleteSizeMismatch(uptr addr, uptr alloc_size, uptr delete_size,
|
||||
void ReportDeadlySignal(int signo, const SignalContext &sig);
|
||||
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
|
||||
BufferedStackTrace *free_stack);
|
||||
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
|
||||
void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "ubsan/ubsan_init.h"
|
||||
#include "ubsan/ubsan_platform.h"
|
||||
|
||||
uptr __asan_shadow_memory_dynamic_address; // Global interface symbol.
|
||||
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
|
||||
uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
|
||||
|
||||
|
|
@ -263,6 +264,7 @@ static NOINLINE void force_interface_symbols() {
|
|||
volatile int fake_condition = 0; // prevent dead condition elimination.
|
||||
// __asan_report_* functions are noreturn, so we need a switch to prevent
|
||||
// the compiler from removing any of them.
|
||||
// clang-format off
|
||||
switch (fake_condition) {
|
||||
case 1: __asan_report_load1(0); break;
|
||||
case 2: __asan_report_load2(0); break;
|
||||
|
|
@ -302,7 +304,14 @@ static NOINLINE void force_interface_symbols() {
|
|||
case 37: __asan_unpoison_stack_memory(0, 0); break;
|
||||
case 38: __asan_region_is_poisoned(0, 0); break;
|
||||
case 39: __asan_describe_address(0); break;
|
||||
case 40: __asan_set_shadow_00(0, 0); break;
|
||||
case 41: __asan_set_shadow_f1(0, 0); break;
|
||||
case 42: __asan_set_shadow_f2(0, 0); break;
|
||||
case 43: __asan_set_shadow_f3(0, 0); break;
|
||||
case 44: __asan_set_shadow_f5(0, 0); break;
|
||||
case 45: __asan_set_shadow_f8(0, 0); break;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static void asan_atexit() {
|
||||
|
|
@ -326,8 +335,21 @@ static void InitializeHighMemEnd() {
|
|||
}
|
||||
|
||||
static void ProtectGap(uptr addr, uptr size) {
|
||||
if (!flags()->protect_shadow_gap)
|
||||
if (!flags()->protect_shadow_gap) {
|
||||
// The shadow gap is unprotected, so there is a chance that someone
|
||||
// is actually using this memory. Which means it needs a shadow...
|
||||
uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached());
|
||||
uptr GapShadowEnd =
|
||||
RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1;
|
||||
if (Verbosity())
|
||||
Printf("protect_shadow_gap=0:"
|
||||
" not protecting shadow gap, allocating gap's shadow\n"
|
||||
"|| `[%p, %p]` || ShadowGap's shadow ||\n", GapShadowBeg,
|
||||
GapShadowEnd);
|
||||
ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd,
|
||||
"unprotected gap shadow");
|
||||
return;
|
||||
}
|
||||
void *res = MmapFixedNoAccess(addr, size, "shadow gap");
|
||||
if (addr == (uptr)res)
|
||||
return;
|
||||
|
|
@ -388,6 +410,8 @@ static void PrintAddressSpaceLayout() {
|
|||
Printf("redzone=%zu\n", (uptr)flags()->redzone);
|
||||
Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone);
|
||||
Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb);
|
||||
Printf("thread_local_quarantine_size_kb=%zuK\n",
|
||||
(uptr)flags()->thread_local_quarantine_size_kb);
|
||||
Printf("malloc_context_size=%zu\n",
|
||||
(uptr)common_flags()->malloc_context_size);
|
||||
|
||||
|
|
@ -401,6 +425,79 @@ static void PrintAddressSpaceLayout() {
|
|||
kHighShadowBeg > kMidMemEnd);
|
||||
}
|
||||
|
||||
static void InitializeShadowMemory() {
|
||||
// Set the shadow memory address to uninitialized.
|
||||
__asan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
|
||||
|
||||
uptr shadow_start = kLowShadowBeg;
|
||||
// Detect if a dynamic shadow address must used and find a available location
|
||||
// when necessary. When dynamic address is used, the macro |kLowShadowBeg|
|
||||
// expands to |__asan_shadow_memory_dynamic_address| which is
|
||||
// |kDefaultShadowSentinel|.
|
||||
if (shadow_start == kDefaultShadowSentinel) {
|
||||
__asan_shadow_memory_dynamic_address = 0;
|
||||
CHECK_EQ(0, kLowShadowBeg);
|
||||
|
||||
uptr granularity = GetMmapGranularity();
|
||||
uptr alignment = 8 * granularity;
|
||||
uptr left_padding = granularity;
|
||||
uptr space_size = kHighShadowEnd + left_padding;
|
||||
|
||||
shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity);
|
||||
CHECK_NE((uptr)0, shadow_start);
|
||||
CHECK(IsAligned(shadow_start, alignment));
|
||||
}
|
||||
// Update the shadow memory address (potentially) used by instrumentation.
|
||||
__asan_shadow_memory_dynamic_address = shadow_start;
|
||||
|
||||
if (kLowShadowBeg)
|
||||
shadow_start -= GetMmapGranularity();
|
||||
bool full_shadow_is_available =
|
||||
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
|
||||
|
||||
#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
|
||||
!ASAN_FIXED_MAPPING
|
||||
if (!full_shadow_is_available) {
|
||||
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
|
||||
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (Verbosity()) PrintAddressSpaceLayout();
|
||||
|
||||
if (full_shadow_is_available) {
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
if (kLowShadowBeg)
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
|
||||
// mmap the high shadow.
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
|
||||
// protect the gap.
|
||||
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
|
||||
CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
|
||||
} else if (kMidMemBeg &&
|
||||
MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
|
||||
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
|
||||
CHECK(kLowShadowBeg != kLowShadowEnd);
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
|
||||
// mmap the mid shadow.
|
||||
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
|
||||
// mmap the high shadow.
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
|
||||
// protect the gaps.
|
||||
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
|
||||
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
|
||||
ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
|
||||
} else {
|
||||
Report("Shadow memory range interleaves with an existing memory mapping. "
|
||||
"ASan cannot proceed correctly. ABORTING.\n");
|
||||
Report("ASan shadow was supposed to be located in the [%p-%p] range.\n",
|
||||
shadow_start, kHighShadowEnd);
|
||||
DumpProcessMap();
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
static void AsanInitInternal() {
|
||||
if (LIKELY(asan_inited)) return;
|
||||
SanitizerToolName = "AddressSanitizer";
|
||||
|
|
@ -434,7 +531,6 @@ static void AsanInitInternal() {
|
|||
|
||||
__sanitizer_set_report_path(common_flags()->log_path);
|
||||
|
||||
// Enable UAR detection, if required.
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
flags()->detect_stack_use_after_return;
|
||||
|
||||
|
|
@ -453,61 +549,9 @@ static void AsanInitInternal() {
|
|||
|
||||
ReplaceSystemMalloc();
|
||||
|
||||
uptr shadow_start = kLowShadowBeg;
|
||||
if (kLowShadowBeg)
|
||||
shadow_start -= GetMmapGranularity();
|
||||
bool full_shadow_is_available =
|
||||
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
|
||||
|
||||
#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
|
||||
!ASAN_FIXED_MAPPING
|
||||
if (!full_shadow_is_available) {
|
||||
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
|
||||
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
|
||||
}
|
||||
#elif SANITIZER_WINDOWS64
|
||||
// Disable the "mid mem" shadow layout.
|
||||
if (!full_shadow_is_available) {
|
||||
kMidMemBeg = 0;
|
||||
kMidMemEnd = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (Verbosity()) PrintAddressSpaceLayout();
|
||||
|
||||
DisableCoreDumperIfNecessary();
|
||||
|
||||
if (full_shadow_is_available) {
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
if (kLowShadowBeg)
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
|
||||
// mmap the high shadow.
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
|
||||
// protect the gap.
|
||||
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
|
||||
CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
|
||||
} else if (kMidMemBeg &&
|
||||
MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
|
||||
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
|
||||
CHECK(kLowShadowBeg != kLowShadowEnd);
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
|
||||
// mmap the mid shadow.
|
||||
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
|
||||
// mmap the high shadow.
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
|
||||
// protect the gaps.
|
||||
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
|
||||
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
|
||||
ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
|
||||
} else {
|
||||
Report("Shadow memory range interleaves with an existing memory mapping. "
|
||||
"ASan cannot proceed correctly. ABORTING.\n");
|
||||
Report("ASan shadow was supposed to be located in the [%p-%p] range.\n",
|
||||
shadow_start, kHighShadowEnd);
|
||||
DumpProcessMap();
|
||||
Die();
|
||||
}
|
||||
InitializeShadowMemory();
|
||||
|
||||
AsanTSDInit(PlatformTSDDtor);
|
||||
InstallDeadlySignalHandlers(AsanOnDeadlySignal);
|
||||
|
|
@ -599,6 +643,9 @@ static AsanInitializer asan_initializer;
|
|||
using namespace __asan; // NOLINT
|
||||
|
||||
void NOINLINE __asan_handle_no_return() {
|
||||
if (asan_init_is_running)
|
||||
return;
|
||||
|
||||
int local_stack;
|
||||
AsanThread *curr_thread = GetCurrentThread();
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@
|
|||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
namespace __asan {
|
||||
class ScarinessScore {
|
||||
public:
|
||||
ScarinessScore() {
|
||||
struct ScarinessScoreBase {
|
||||
void Clear() {
|
||||
descr[0] = 0;
|
||||
score = 0;
|
||||
}
|
||||
void Scare(int add_to_score, const char *reason) {
|
||||
if (descr[0])
|
||||
|
|
@ -52,16 +52,23 @@ class ScarinessScore {
|
|||
Printf("SCARINESS: %d (%s)\n", score, descr);
|
||||
}
|
||||
static void PrintSimple(int score, const char *descr) {
|
||||
ScarinessScore SS;
|
||||
SS.Scare(score, descr);
|
||||
SS.Print();
|
||||
ScarinessScoreBase SSB;
|
||||
SSB.Clear();
|
||||
SSB.Scare(score, descr);
|
||||
SSB.Print();
|
||||
}
|
||||
|
||||
private:
|
||||
int score = 0;
|
||||
int score;
|
||||
char descr[1024];
|
||||
};
|
||||
|
||||
struct ScarinessScore : ScarinessScoreBase {
|
||||
ScarinessScore() {
|
||||
Clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_SCARINESS_SCORE_H
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@ void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom,
|
|||
current_fake_stack->Destroy(this->tid());
|
||||
}
|
||||
|
||||
void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save) {
|
||||
void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save,
|
||||
uptr *bottom_old,
|
||||
uptr *size_old) {
|
||||
if (!atomic_load(&stack_switching_, memory_order_relaxed)) {
|
||||
Report("ERROR: finishing a fiber switch that has not started\n");
|
||||
Die();
|
||||
|
|
@ -152,6 +154,10 @@ void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save) {
|
|||
fake_stack_ = fake_stack_save;
|
||||
}
|
||||
|
||||
if (bottom_old)
|
||||
*bottom_old = stack_bottom_;
|
||||
if (size_old)
|
||||
*size_old = stack_top_ - stack_bottom_;
|
||||
stack_bottom_ = next_stack_bottom_;
|
||||
stack_top_ = next_stack_top_;
|
||||
atomic_store(&stack_switching_, 0, memory_order_release);
|
||||
|
|
@ -345,7 +351,7 @@ AsanThread *GetCurrentThread() {
|
|||
// limits, so only do this magic on Android, and only if the found thread
|
||||
// is the main thread.
|
||||
AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
|
||||
if (ThreadStackContainsAddress(tctx, &context)) {
|
||||
if (tctx && ThreadStackContainsAddress(tctx, &context)) {
|
||||
SetCurrentThread(tctx->thread);
|
||||
return tctx->thread;
|
||||
}
|
||||
|
|
@ -447,12 +453,16 @@ void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom,
|
|||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_finish_switch_fiber(void* fakestack) {
|
||||
void __sanitizer_finish_switch_fiber(void* fakestack,
|
||||
const void **bottom_old,
|
||||
uptr *size_old) {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
if (!t) {
|
||||
VReport(1, "__asan_finish_switch_fiber called from unknown thread\n");
|
||||
return;
|
||||
}
|
||||
t->FinishSwitchFiber((FakeStack*)fakestack);
|
||||
t->FinishSwitchFiber((FakeStack*)fakestack,
|
||||
(uptr*)bottom_old,
|
||||
(uptr*)size_old);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,8 @@ class AsanThread {
|
|||
}
|
||||
|
||||
void StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, uptr size);
|
||||
void FinishSwitchFiber(FakeStack *fake_stack_save);
|
||||
void FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old,
|
||||
uptr *size_old);
|
||||
|
||||
bool has_fake_stack() {
|
||||
return !atomic_load(&stack_switching_, memory_order_relaxed) &&
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asan_globals_win.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_report.h"
|
||||
|
|
@ -37,7 +38,13 @@ int __asan_should_detect_stack_use_after_return() {
|
|||
return __asan_option_detect_stack_use_after_return;
|
||||
}
|
||||
|
||||
// -------------------- A workaround for the abscence of weak symbols ----- {{{
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_shadow_memory_dynamic_address() {
|
||||
__asan_init();
|
||||
return __asan_shadow_memory_dynamic_address;
|
||||
}
|
||||
|
||||
// -------------------- A workaround for the absence of weak symbols ----- {{{
|
||||
// We don't have a direct equivalent of weak symbols when using MSVC, but we can
|
||||
// use the /alternatename directive to tell the linker to default a specific
|
||||
// symbol to a specific value, which works nicely for allocator hooks and
|
||||
|
|
@ -64,14 +71,22 @@ void __asan_default_on_error() {}
|
|||
// }}}
|
||||
} // extern "C"
|
||||
|
||||
// ---------------------- Windows-specific inteceptors ---------------- {{{
|
||||
// ---------------------- Windows-specific interceptors ---------------- {{{
|
||||
INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
|
||||
CHECK(REAL(RtlRaiseException));
|
||||
// This is a noreturn function, unless it's one of the exceptions raised to
|
||||
// communicate with the debugger, such as the one from OutputDebugString.
|
||||
if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
|
||||
__asan_handle_no_return();
|
||||
REAL(RtlRaiseException)(ExceptionRecord);
|
||||
}
|
||||
|
||||
INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(RaiseException));
|
||||
__asan_handle_no_return();
|
||||
REAL(RaiseException)(a, b, c, d);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN64
|
||||
|
||||
INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) { // NOLINT
|
||||
|
|
@ -123,44 +138,12 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
|||
asan_thread_start, t, thr_flags, tid);
|
||||
}
|
||||
|
||||
namespace {
|
||||
BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED);
|
||||
|
||||
void EnsureWorkerThreadRegistered() {
|
||||
// FIXME: GetCurrentThread relies on TSD, which might not play well with
|
||||
// system thread pools. We might want to use something like reference
|
||||
// counting to zero out GetCurrentThread() underlying storage when the last
|
||||
// work item finishes? Or can we disable reclaiming of threads in the pool?
|
||||
BlockingMutexLock l(&mu_for_thread_tracking);
|
||||
if (__asan::GetCurrentThread())
|
||||
return;
|
||||
|
||||
AsanThread *t = AsanThread::Create(
|
||||
/* start_routine */ nullptr, /* arg */ nullptr,
|
||||
/* parent_tid */ -1, /* stack */ nullptr, /* detached */ true);
|
||||
t->Init();
|
||||
asanThreadRegistry().StartThread(t->tid(), 0, 0);
|
||||
SetCurrentThread(t);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) {
|
||||
// NtWaitForWorkViaWorkerFactory is called from system worker pool threads to
|
||||
// query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc.
|
||||
// System worker pool threads are created at arbitraty point in time and
|
||||
// without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory
|
||||
// instead and don't register a specific parent_tid/stack.
|
||||
EnsureWorkerThreadRegistered();
|
||||
return REAL(NtWaitForWorkViaWorkerFactory)(a, b);
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void InitializePlatformInterceptors() {
|
||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||
ASAN_INTERCEPT_FUNC(RaiseException);
|
||||
|
||||
#ifdef _WIN64
|
||||
ASAN_INTERCEPT_FUNC(__C_specific_handler);
|
||||
|
|
@ -169,11 +152,15 @@ void InitializePlatformInterceptors() {
|
|||
ASAN_INTERCEPT_FUNC(_except_handler4);
|
||||
#endif
|
||||
|
||||
// NtWaitForWorkViaWorkerFactory is always linked dynamically.
|
||||
CHECK(::__interception::OverrideFunction(
|
||||
"NtWaitForWorkViaWorkerFactory",
|
||||
(uptr)WRAP(NtWaitForWorkViaWorkerFactory),
|
||||
(uptr *)&REAL(NtWaitForWorkViaWorkerFactory)));
|
||||
// Try to intercept kernel32!RaiseException, and if that fails, intercept
|
||||
// ntdll!RtlRaiseException instead.
|
||||
if (!::__interception::OverrideFunction("RaiseException",
|
||||
(uptr)WRAP(RaiseException),
|
||||
(uptr *)&REAL(RaiseException))) {
|
||||
CHECK(::__interception::OverrideFunction("RtlRaiseException",
|
||||
(uptr)WRAP(RtlRaiseException),
|
||||
(uptr *)&REAL(RtlRaiseException)));
|
||||
}
|
||||
}
|
||||
|
||||
void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
|
||||
|
|
@ -229,8 +216,7 @@ void AsanOnDeadlySignal(int, void *siginfo, void *context) {
|
|||
// Exception handler for dealing with shadow memory.
|
||||
static LONG CALLBACK
|
||||
ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
|
||||
static uptr page_size = GetPageSizeCached();
|
||||
static uptr alloc_granularity = GetMmapGranularity();
|
||||
uptr page_size = GetPageSizeCached();
|
||||
// Only handle access violations.
|
||||
if (exception_pointers->ExceptionRecord->ExceptionCode !=
|
||||
EXCEPTION_ACCESS_VIOLATION) {
|
||||
|
|
@ -276,22 +262,57 @@ void InitializePlatformExceptionHandlers() {
|
|||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
|
||||
|
||||
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
|
||||
// Check based on flags if we should report this exception.
|
||||
static bool ShouldReportDeadlyException(unsigned code) {
|
||||
switch (code) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
return common_flags()->handle_segv;
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION: {
|
||||
return common_flags()->handle_sigill;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the textual name for this exception.
|
||||
const char *DescribeSignalOrException(int signo) {
|
||||
unsigned code = signo;
|
||||
// Get the string description of the exception if this is a known deadly
|
||||
// exception.
|
||||
switch (code) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
return "access-violation";
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
return "in-page-error";
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
return "breakpoint";
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
return "illegal-instruction";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) {
|
||||
EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
|
||||
CONTEXT *context = info->ContextRecord;
|
||||
|
||||
if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
|
||||
exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
|
||||
const char *description =
|
||||
(exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
|
||||
? "access-violation"
|
||||
: "in-page-error";
|
||||
SignalContext sig = SignalContext::Create(exception_record, context);
|
||||
ReportDeadlySignal(description, sig);
|
||||
}
|
||||
|
||||
// Continue the search if the signal wasn't deadly.
|
||||
if (!ShouldReportDeadlyException(exception_record->ExceptionCode))
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
// FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
|
||||
|
||||
SignalContext sig = SignalContext::Create(exception_record, context);
|
||||
ReportDeadlySignal(exception_record->ExceptionCode, sig);
|
||||
UNREACHABLE("returned from reporting deadly signal");
|
||||
}
|
||||
|
||||
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
|
||||
__asan_unhandled_exception_filter(info);
|
||||
|
||||
// Bubble out to the default exception filter.
|
||||
return default_seh_handler(info);
|
||||
}
|
||||
|
||||
|
|
@ -331,10 +352,25 @@ int __asan_set_seh_filter() {
|
|||
// immediately after the CRT runs. This way, our exception filter is called
|
||||
// first and we can delegate to their filter if appropriate.
|
||||
#pragma section(".CRT$XCAB", long, read) // NOLINT
|
||||
__declspec(allocate(".CRT$XCAB"))
|
||||
int (*__intercept_seh)() = __asan_set_seh_filter;
|
||||
__declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() =
|
||||
__asan_set_seh_filter;
|
||||
|
||||
// Piggyback on the TLS initialization callback directory to initialize asan as
|
||||
// early as possible. Initializers in .CRT$XL* are called directly by ntdll,
|
||||
// which run before the CRT. Users also add code to .CRT$XLC, so it's important
|
||||
// to run our initializers first.
|
||||
static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) {
|
||||
if (reason == DLL_PROCESS_ATTACH) __asan_init();
|
||||
}
|
||||
|
||||
#pragma section(".CRT$XLAB", long, read) // NOLINT
|
||||
__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
|
||||
unsigned long, void *) = asan_thread_init;
|
||||
#endif
|
||||
|
||||
ASAN_LINK_GLOBALS_WIN()
|
||||
|
||||
// }}}
|
||||
} // namespace __asan
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // SANITIZER_WINDOWS
|
||||
|
|
|
|||
|
|
@ -15,21 +15,30 @@
|
|||
// See https://github.com/google/sanitizers/issues/209 for the details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Only compile this code when buidling asan_dll_thunk.lib
|
||||
// Only compile this code when building asan_dll_thunk.lib
|
||||
// Using #ifdef rather than relying on Makefiles etc.
|
||||
// simplifies the build procedure.
|
||||
#ifdef ASAN_DLL_THUNK
|
||||
#include "asan_init_version.h"
|
||||
#include "asan_globals_win.h"
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_platform_interceptors.h"
|
||||
|
||||
#ifdef _M_IX86
|
||||
#define WINAPI __stdcall
|
||||
#else
|
||||
#define WINAPI
|
||||
#endif
|
||||
|
||||
// ---------- Function interception helper functions and macros ----------- {{{1
|
||||
extern "C" {
|
||||
void *__stdcall GetModuleHandleA(const char *module_name);
|
||||
void *__stdcall GetProcAddress(void *module, const char *proc_name);
|
||||
void *WINAPI GetModuleHandleA(const char *module_name);
|
||||
void *WINAPI GetProcAddress(void *module, const char *proc_name);
|
||||
void abort();
|
||||
}
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
static uptr getRealProcAddressOrDie(const char *name) {
|
||||
uptr ret =
|
||||
__interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
|
||||
|
|
@ -105,7 +114,7 @@ static void InterceptHooks();
|
|||
// ---------- Function wrapping helpers ----------------------------------- {{{1
|
||||
#define WRAP_V_V(name) \
|
||||
extern "C" void name() { \
|
||||
typedef void (*fntype)(); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(); \
|
||||
} \
|
||||
|
|
@ -113,7 +122,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_V_W(name) \
|
||||
extern "C" void name(void *arg) { \
|
||||
typedef void (*fntype)(void *arg); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg); \
|
||||
} \
|
||||
|
|
@ -121,7 +130,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_V_WW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2) { \
|
||||
typedef void (*fntype)(void *, void *); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg1, arg2); \
|
||||
} \
|
||||
|
|
@ -129,7 +138,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_V_WWW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef void *(*fntype)(void *, void *, void *); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg1, arg2, arg3); \
|
||||
} \
|
||||
|
|
@ -137,7 +146,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_W_V(name) \
|
||||
extern "C" void *name() { \
|
||||
typedef void *(*fntype)(); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(); \
|
||||
} \
|
||||
|
|
@ -145,7 +154,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_W_W(name) \
|
||||
extern "C" void *name(void *arg) { \
|
||||
typedef void *(*fntype)(void *arg); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg); \
|
||||
} \
|
||||
|
|
@ -153,7 +162,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_W_WW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2) { \
|
||||
typedef void *(*fntype)(void *, void *); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2); \
|
||||
} \
|
||||
|
|
@ -161,7 +170,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_W_WWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef void *(*fntype)(void *, void *, void *); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3); \
|
||||
} \
|
||||
|
|
@ -169,7 +178,7 @@ static void InterceptHooks();
|
|||
|
||||
#define WRAP_W_WWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
|
||||
typedef void *(*fntype)(void *, void *, void *, void *); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4); \
|
||||
} \
|
||||
|
|
@ -178,7 +187,7 @@ static void InterceptHooks();
|
|||
#define WRAP_W_WWWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
||||
void *arg5) { \
|
||||
typedef void *(*fntype)(void *, void *, void *, void *, void *); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5); \
|
||||
} \
|
||||
|
|
@ -187,7 +196,7 @@ static void InterceptHooks();
|
|||
#define WRAP_W_WWWWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
||||
void *arg5, void *arg6) { \
|
||||
typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \
|
||||
typedef decltype(name) *fntype; \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||
} \
|
||||
|
|
@ -198,9 +207,11 @@ static void InterceptHooks();
|
|||
// Don't use the INTERFACE_FUNCTION machinery for this function as we actually
|
||||
// want to call it in the __asan_init interceptor.
|
||||
WRAP_W_V(__asan_should_detect_stack_use_after_return)
|
||||
WRAP_W_V(__asan_get_shadow_memory_dynamic_address)
|
||||
|
||||
extern "C" {
|
||||
int __asan_option_detect_stack_use_after_return;
|
||||
uptr __asan_shadow_memory_dynamic_address;
|
||||
|
||||
// Manually wrap __asan_init as we need to initialize
|
||||
// __asan_option_detect_stack_use_after_return afterwards.
|
||||
|
|
@ -214,7 +225,8 @@ extern "C" {
|
|||
fn();
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
(__asan_should_detect_stack_use_after_return() != 0);
|
||||
|
||||
__asan_shadow_memory_dynamic_address =
|
||||
(uptr)__asan_get_shadow_memory_dynamic_address();
|
||||
InterceptHooks();
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +236,7 @@ extern "C" void __asan_version_mismatch_check() {
|
|||
}
|
||||
|
||||
INTERFACE_FUNCTION(__asan_handle_no_return)
|
||||
INTERFACE_FUNCTION(__asan_unhandled_exception_filter)
|
||||
|
||||
INTERFACE_FUNCTION(__asan_report_store1)
|
||||
INTERFACE_FUNCTION(__asan_report_store2)
|
||||
|
|
@ -257,6 +270,13 @@ INTERFACE_FUNCTION(__asan_memcpy);
|
|||
INTERFACE_FUNCTION(__asan_memset);
|
||||
INTERFACE_FUNCTION(__asan_memmove);
|
||||
|
||||
INTERFACE_FUNCTION(__asan_set_shadow_00);
|
||||
INTERFACE_FUNCTION(__asan_set_shadow_f1);
|
||||
INTERFACE_FUNCTION(__asan_set_shadow_f2);
|
||||
INTERFACE_FUNCTION(__asan_set_shadow_f3);
|
||||
INTERFACE_FUNCTION(__asan_set_shadow_f5);
|
||||
INTERFACE_FUNCTION(__asan_set_shadow_f8);
|
||||
|
||||
INTERFACE_FUNCTION(__asan_alloca_poison);
|
||||
INTERFACE_FUNCTION(__asan_allocas_unpoison);
|
||||
|
||||
|
|
@ -306,17 +326,18 @@ INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
|
|||
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_dump)
|
||||
INTERFACE_FUNCTION(__sanitizer_dump_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_dump_trace_pc_guard_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_switch)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_pc_guard)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_pc_guard_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_with_check)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_coverage_pc_buffer)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
|
||||
|
|
@ -327,6 +348,8 @@ INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
|
|||
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
|
||||
INTERFACE_FUNCTION(__sanitizer_print_stack_trace)
|
||||
INTERFACE_FUNCTION(__sanitizer_symbolize_pc)
|
||||
INTERFACE_FUNCTION(__sanitizer_symbolize_global)
|
||||
INTERFACE_FUNCTION(__sanitizer_ptr_cmp)
|
||||
INTERFACE_FUNCTION(__sanitizer_ptr_sub)
|
||||
INTERFACE_FUNCTION(__sanitizer_report_error_summary)
|
||||
|
|
@ -347,6 +370,7 @@ INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container)
|
|||
INTERFACE_FUNCTION(__sanitizer_install_malloc_and_free_hooks)
|
||||
INTERFACE_FUNCTION(__sanitizer_start_switch_fiber)
|
||||
INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_module_and_offset_for_pc)
|
||||
|
||||
// TODO(timurrrr): Add more interface functions on the as-needed basis.
|
||||
|
||||
|
|
@ -368,6 +392,7 @@ WRAP_W_WW(realloc)
|
|||
WRAP_W_WW(_realloc_base)
|
||||
WRAP_W_WWW(_realloc_dbg)
|
||||
WRAP_W_WWW(_recalloc)
|
||||
WRAP_W_WWW(_recalloc_base)
|
||||
|
||||
WRAP_W_W(_msize)
|
||||
WRAP_W_W(_expand)
|
||||
|
|
@ -444,4 +469,15 @@ static int call_asan_init() {
|
|||
#pragma section(".CRT$XIB", long, read) // NOLINT
|
||||
__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = call_asan_init;
|
||||
|
||||
static void WINAPI asan_thread_init(void *mod, unsigned long reason,
|
||||
void *reserved) {
|
||||
if (reason == /*DLL_PROCESS_ATTACH=*/1) __asan_init();
|
||||
}
|
||||
|
||||
#pragma section(".CRT$XLAB", long, read) // NOLINT
|
||||
__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *,
|
||||
unsigned long, void *) = asan_thread_init;
|
||||
|
||||
ASAN_LINK_GLOBALS_WIN()
|
||||
|
||||
#endif // ASAN_DLL_THUNK
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//===-- asan_win_uar_thunk.cc ---------------------------------------------===//
|
||||
//===-- asan_win_dynamic_runtime_thunk.cc ---------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
|
@ -16,22 +16,25 @@
|
|||
// This includes:
|
||||
// - forwarding the detect_stack_use_after_return runtime option
|
||||
// - working around deficiencies of the MD runtime
|
||||
// - installing a custom SEH handlerx
|
||||
// - installing a custom SEH handler
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Only compile this code when buidling asan_dynamic_runtime_thunk.lib
|
||||
// Only compile this code when building asan_dynamic_runtime_thunk.lib
|
||||
// Using #ifdef rather than relying on Makefiles etc.
|
||||
// simplifies the build procedure.
|
||||
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
|
||||
#include "asan_globals_win.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
// First, declare CRT sections we'll be using in this file
|
||||
#pragma section(".CRT$XIB", long, read) // NOLINT
|
||||
#pragma section(".CRT$XID", long, read) // NOLINT
|
||||
#pragma section(".CRT$XCAB", long, read) // NOLINT
|
||||
#pragma section(".CRT$XTW", long, read) // NOLINT
|
||||
#pragma section(".CRT$XTY", long, read) // NOLINT
|
||||
#pragma section(".CRT$XLAB", long, read) // NOLINT
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Define a copy of __asan_option_detect_stack_use_after_return that should be
|
||||
|
|
@ -42,14 +45,37 @@
|
|||
// attribute adds __imp_ prefix to the symbol name of a variable.
|
||||
// Since in general we don't know if a given TU is going to be used
|
||||
// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
|
||||
// just to work around this issue, let's clone the a variable that is
|
||||
// constant after initialization anyways.
|
||||
// just to work around this issue, let's clone the variable that is constant
|
||||
// after initialization anyways.
|
||||
extern "C" {
|
||||
__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
|
||||
int __asan_option_detect_stack_use_after_return =
|
||||
__asan_should_detect_stack_use_after_return();
|
||||
int __asan_option_detect_stack_use_after_return;
|
||||
|
||||
__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address();
|
||||
void* __asan_shadow_memory_dynamic_address;
|
||||
}
|
||||
|
||||
static int InitializeClonedVariables() {
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
__asan_should_detect_stack_use_after_return();
|
||||
__asan_shadow_memory_dynamic_address =
|
||||
__asan_get_shadow_memory_dynamic_address();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void NTAPI asan_thread_init(void *mod, unsigned long reason,
|
||||
void *reserved) {
|
||||
if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables();
|
||||
}
|
||||
|
||||
// Our cloned variables must be initialized before C/C++ constructors. If TLS
|
||||
// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB
|
||||
// initializer is needed as a backup.
|
||||
__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() =
|
||||
InitializeClonedVariables;
|
||||
__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
|
||||
unsigned long, void *) = asan_thread_init;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
|
||||
// unload or on exit. ASan relies on LLVM global_dtors to call
|
||||
|
|
@ -73,6 +99,7 @@ void UnregisterGlobals() {
|
|||
int ScheduleUnregisterGlobals() {
|
||||
return atexit(UnregisterGlobals);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
|
||||
// atexit() is initialized (.CRT$XIC). As this is executed before C++
|
||||
|
|
@ -81,8 +108,6 @@ int ScheduleUnregisterGlobals() {
|
|||
__declspec(allocate(".CRT$XID"))
|
||||
int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
|
||||
|
||||
} // namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ASan SEH handling.
|
||||
// We need to set the ASan-specific SEH handler at the end of CRT initialization
|
||||
|
|
@ -97,4 +122,6 @@ __declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() =
|
|||
SetSEHFilter;
|
||||
}
|
||||
|
||||
ASAN_LINK_GLOBALS_WIN()
|
||||
|
||||
#endif // ASAN_DYNAMIC_RUNTIME_THUNK
|
||||
|
|
|
|||
|
|
@ -300,20 +300,22 @@ if [[ -n "$ASAN_RT64" ]]; then
|
|||
cp "$ASAN_RT_PATH/$ASAN_RT64" "$TMPDIR/"
|
||||
fi
|
||||
|
||||
# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup,
|
||||
# which may or may not be a real bug (probably not).
|
||||
ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0,malloc_context_size=0
|
||||
ASAN_OPTIONS=start_deactivated=1,malloc_context_size=0
|
||||
|
||||
function generate_zygote_wrapper { # from, to, asan_rt
|
||||
# The name of a symlink to libclang_rt.asan-$ARCH-android.so used in LD_PRELOAD.
|
||||
# The idea is to have the same name in lib and lib64 to keep it from falling
|
||||
# apart when a 64-bit process spawns a 32-bit one, inheriting the environment.
|
||||
ASAN_RT_SYMLINK=symlink-to-libclang_rt.asan
|
||||
|
||||
function generate_zygote_wrapper { # from, to
|
||||
local _from=$1
|
||||
local _to=$2
|
||||
local _asan_rt=$3
|
||||
if [[ PRE_L -eq 0 ]]; then
|
||||
# LD_PRELOAD parsing is broken in N if it starts with ":". Luckily, it is
|
||||
# unset in the system environment since L.
|
||||
local _ld_preload=$_asan_rt
|
||||
local _ld_preload=$ASAN_RT_SYMLINK
|
||||
else
|
||||
local _ld_preload=\$LD_PRELOAD:$_asan_rt
|
||||
local _ld_preload=\$LD_PRELOAD:$ASAN_RT_SYMLINK
|
||||
fi
|
||||
cat <<EOF >"$TMPDIR/$_from"
|
||||
#!/system/bin/sh-from-zygote
|
||||
|
|
@ -342,18 +344,18 @@ if [[ -f "$TMPDIR/app_process64" ]]; then
|
|||
mv "$TMPDIR/app_process32" "$TMPDIR/app_process32.real"
|
||||
mv "$TMPDIR/app_process64" "$TMPDIR/app_process64.real"
|
||||
fi
|
||||
generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real" "$ASAN_RT"
|
||||
generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real" "$ASAN_RT64"
|
||||
generate_zygote_wrapper "app_process32" "/system/bin/app_process32.real"
|
||||
generate_zygote_wrapper "app_process64" "/system/bin/app_process64.real"
|
||||
else
|
||||
# A 32-bit device.
|
||||
generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32" "$ASAN_RT"
|
||||
generate_zygote_wrapper "app_process.wrap" "/system/bin/app_process32"
|
||||
fi
|
||||
|
||||
# General command-line tool wrapper (use for anything that's not started as
|
||||
# zygote).
|
||||
cat <<EOF >"$TMPDIR/asanwrapper"
|
||||
#!/system/bin/sh
|
||||
LD_PRELOAD=$ASAN_RT \\
|
||||
LD_PRELOAD=$ASAN_RT_SYMLINK \\
|
||||
exec \$@
|
||||
|
||||
EOF
|
||||
|
|
@ -361,7 +363,7 @@ EOF
|
|||
if [[ -n "$ASAN_RT64" ]]; then
|
||||
cat <<EOF >"$TMPDIR/asanwrapper64"
|
||||
#!/system/bin/sh
|
||||
LD_PRELOAD=$ASAN_RT64 \\
|
||||
LD_PRELOAD=$ASAN_RT_SYMLINK \\
|
||||
exec \$@
|
||||
|
||||
EOF
|
||||
|
|
@ -412,12 +414,17 @@ if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
|
|||
install "$TMPDIR/app_process64.real" /system/bin 755 $CTX
|
||||
install "$TMPDIR/asanwrapper" /system/bin 755
|
||||
install "$TMPDIR/asanwrapper64" /system/bin 755
|
||||
|
||||
adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
|
||||
adb_shell ln -s $ASAN_RT64 /system/lib64/$ASAN_RT_SYMLINK
|
||||
else
|
||||
install "$TMPDIR/$ASAN_RT" /system/lib 644
|
||||
install "$TMPDIR/app_process32" /system/bin 755 $CTX
|
||||
install "$TMPDIR/app_process.wrap" /system/bin 755 $CTX
|
||||
install "$TMPDIR/asanwrapper" /system/bin 755 $CTX
|
||||
|
||||
adb_shell ln -s $ASAN_RT /system/lib/$ASAN_RT_SYMLINK
|
||||
|
||||
adb_shell rm /system/bin/app_process
|
||||
adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -106,12 +106,13 @@ set(ASAN_UNITTEST_INSTRUMENTED_LIBS)
|
|||
append_list_if(ANDROID atomic ASAN_UNITTEST_INSTRUMENTED_LIBS)
|
||||
|
||||
set(ASAN_UNITTEST_NOINST_LINKFLAGS ${ASAN_UNITTEST_COMMON_LINKFLAGS})
|
||||
append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBRT -lrt ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread
|
||||
ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS)
|
||||
if(NOT APPLE)
|
||||
append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBRT -lrt ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS)
|
||||
endif()
|
||||
|
||||
# TODO(eugenis): move all -l flags above to _LIBS?
|
||||
set(ASAN_UNITTEST_NOINST_LIBS)
|
||||
|
|
@ -193,6 +194,7 @@ set(ASAN_INST_TEST_SOURCES
|
|||
asan_asm_test.cc
|
||||
asan_globals_test.cc
|
||||
asan_interface_test.cc
|
||||
asan_internal_interface_test.cc
|
||||
asan_test.cc
|
||||
asan_oob_test.cc
|
||||
asan_mem_test.cc
|
||||
|
|
@ -220,6 +222,23 @@ macro(add_asan_tests_for_arch_and_kind arch kind)
|
|||
${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC ${ARGN})
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
# With the MSVC CRT, the choice between static and dynamic CRT is made at
|
||||
# compile time with a macro. Simulate the effect of passing /MD to clang-cl.
|
||||
set(ASAN_INST_DYNAMIC_TEST_OBJECTS)
|
||||
foreach(src ${ASAN_INST_TEST_SOURCES})
|
||||
asan_compile(ASAN_INST_DYNAMIC_TEST_OBJECTS ${src} ${arch} ${kind}
|
||||
${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -D_MT -D_DLL ${ARGN})
|
||||
endforeach()
|
||||
# Clang links the static CRT by default. Override that to use the dynamic
|
||||
# CRT.
|
||||
set(ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS
|
||||
${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS}
|
||||
-Wl,-nodefaultlib:libcmt,-defaultlib:msvcrt,-defaultlib:oldnames)
|
||||
else()
|
||||
set(ASAN_INST_DYNAMIC_TEST_OBJECTS ${ASAN_INST_TEST_OBJECTS})
|
||||
endif()
|
||||
|
||||
# Create the 'default' folder where ASAN tests are produced.
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
foreach(build_mode ${CMAKE_CONFIGURATION_TYPES})
|
||||
|
|
@ -245,7 +264,7 @@ macro(add_asan_tests_for_arch_and_kind arch kind)
|
|||
|
||||
add_asan_test(AsanDynamicUnitTests "Asan-${arch}${kind}-Dynamic-Test"
|
||||
${arch} ${kind} SUBDIR "dynamic"
|
||||
OBJECTS ${ASAN_INST_TEST_OBJECTS}
|
||||
OBJECTS ${ASAN_INST_DYNAMIC_TEST_OBJECTS}
|
||||
LINKFLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS})
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -57,12 +57,13 @@ template<> Type asm_read<Type>(Type *ptr) { \
|
|||
return res; \
|
||||
}
|
||||
|
||||
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
|
||||
template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
|
||||
__asm__("rep " Movs " \n\t" \
|
||||
: \
|
||||
: "D"(dst), "S"(src), "c"(size) \
|
||||
: "rsi", "rdi", "rcx", "memory"); \
|
||||
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
|
||||
template <> \
|
||||
void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
|
||||
__asm__("rep " Movs " \n\t" \
|
||||
: "+D"(dst), "+S"(src), "+c"(size) \
|
||||
: \
|
||||
: "memory"); \
|
||||
}
|
||||
|
||||
DECLARE_ASM_WRITE(U8, "8", "movq", "r");
|
||||
|
|
@ -99,12 +100,13 @@ template<> Type asm_read<Type>(Type *ptr) { \
|
|||
return res; \
|
||||
}
|
||||
|
||||
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
|
||||
template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
|
||||
__asm__("rep " Movs " \n\t" \
|
||||
: \
|
||||
: "D"(dst), "S"(src), "c"(size) \
|
||||
: "esi", "edi", "ecx", "memory"); \
|
||||
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
|
||||
template <> \
|
||||
void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
|
||||
__asm__("rep " Movs " \n\t" \
|
||||
: "+D"(dst), "+S"(src), "+c"(size) \
|
||||
: \
|
||||
: "memory"); \
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ TEST(AddressSanitizerInterface, GetHeapSizeTest) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef __powerpc64__
|
||||
// FIXME: This has not reliably worked on powerpc since r279664. Re-enable
|
||||
// this once the problem is tracked down and fixed.
|
||||
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
|
||||
static const size_t kManyThreadsIterations = 250;
|
||||
static const size_t kManyThreadsNumThreads =
|
||||
|
|
@ -133,6 +136,7 @@ TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
|
|||
// so we can't check for equality here.
|
||||
EXPECT_LT(after_test, before_test + (1UL<<20));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void DoDoubleFree() {
|
||||
int *x = Ident(new int);
|
||||
|
|
|
|||
36
lib/asan/tests/asan_internal_interface_test.cc
Normal file
36
lib/asan/tests/asan_internal_interface_test.cc
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
//===-- asan_internal_interface_test.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 AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_interface_internal.h"
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
TEST(AddressSanitizerInternalInterface, SetShadow) {
|
||||
std::vector<char> buffer(17, 0xff);
|
||||
|
||||
__asan_set_shadow_00((uptr)buffer.data(), buffer.size());
|
||||
EXPECT_EQ(std::vector<char>(buffer.size(), 0x00), buffer);
|
||||
|
||||
__asan_set_shadow_f1((uptr)buffer.data(), buffer.size());
|
||||
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf1), buffer);
|
||||
|
||||
__asan_set_shadow_f2((uptr)buffer.data(), buffer.size());
|
||||
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf2), buffer);
|
||||
|
||||
__asan_set_shadow_f3((uptr)buffer.data(), buffer.size());
|
||||
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf3), buffer);
|
||||
|
||||
__asan_set_shadow_f5((uptr)buffer.data(), buffer.size());
|
||||
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf5), buffer);
|
||||
|
||||
__asan_set_shadow_f8((uptr)buffer.data(), buffer.size());
|
||||
EXPECT_EQ(std::vector<char>(buffer.size(), 0xf8), buffer);
|
||||
}
|
||||
|
|
@ -26,6 +26,8 @@
|
|||
#include <vector>
|
||||
#include <limits>
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
// ATTENTION!
|
||||
// Please don't call intercepted functions (including malloc() and friends)
|
||||
// in this test. The static runtime library is linked explicitly (without
|
||||
|
|
@ -168,6 +170,12 @@ void *ThreadedQuarantineTestWorker(void *unused) {
|
|||
// Check that the thread local allocators are flushed when threads are
|
||||
// destroyed.
|
||||
TEST(AddressSanitizer, ThreadedQuarantineTest) {
|
||||
// Run the routine once to warm up ASAN internal structures to get more
|
||||
// predictable incremental memory changes.
|
||||
pthread_t t;
|
||||
PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0);
|
||||
PTHREAD_JOIN(t, 0);
|
||||
|
||||
const int n_threads = 3000;
|
||||
size_t mmaped1 = __sanitizer_get_heap_size();
|
||||
for (int i = 0; i < n_threads; i++) {
|
||||
|
|
@ -175,6 +183,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
|
|||
PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0);
|
||||
PTHREAD_JOIN(t, 0);
|
||||
size_t mmaped2 = __sanitizer_get_heap_size();
|
||||
// Figure out why this much memory is required.
|
||||
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,15 @@ TEST(AddressSanitizer, StrNLenOOBTest) {
|
|||
}
|
||||
#endif // SANITIZER_TEST_HAS_STRNLEN
|
||||
|
||||
TEST(AddressSanitizer, StrDupOOBTest) {
|
||||
// This test fails with the WinASan dynamic runtime because we fail to intercept
|
||||
// strdup.
|
||||
#if defined(_MSC_VER) && defined(_DLL)
|
||||
#define MAYBE_StrDupOOBTest DISABLED_StrDupOOBTest
|
||||
#else
|
||||
#define MAYBE_StrDupOOBTest StrDupOOBTest
|
||||
#endif
|
||||
|
||||
TEST(AddressSanitizer, MAYBE_StrDupOOBTest) {
|
||||
size_t size = Ident(42);
|
||||
char *str = MallocAndMemsetString(size);
|
||||
char *new_str;
|
||||
|
|
@ -457,11 +465,13 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
|
|||
#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \
|
||||
(MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
|
||||
// Check "memcpy". Use Ident() to avoid inlining.
|
||||
#if PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE
|
||||
memset(str, 'z', size);
|
||||
Ident(memcpy)(str + 1, str + 11, 10);
|
||||
Ident(memcpy)(str, str, 0);
|
||||
EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy"));
|
||||
EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy"));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// We do not treat memcpy with to==from as a bug.
|
||||
|
|
|
|||
|
|
@ -692,7 +692,7 @@ TEST(AddressSanitizer, ThreadStackReuseTest) {
|
|||
PTHREAD_JOIN(t, 0);
|
||||
}
|
||||
|
||||
#if defined(__i686__) || defined(__x86_64__)
|
||||
#if defined(__SSE2__)
|
||||
#include <emmintrin.h>
|
||||
TEST(AddressSanitizer, Store128Test) {
|
||||
char *a = Ident((char*)malloc(Ident(12)));
|
||||
|
|
|
|||
|
|
@ -28,9 +28,18 @@ extern "C" const char* __asan_default_options() {
|
|||
|
||||
namespace __sanitizer {
|
||||
bool ReexecDisabled() {
|
||||
#if __has_feature(address_sanitizer) && SANITIZER_MAC
|
||||
// Allow re-exec in instrumented unit tests on Darwin. Technically, we only
|
||||
// need this for 10.10 and below, where re-exec is required for the
|
||||
// interceptors to work, but to avoid duplicating the version detection logic,
|
||||
// let's just allow re-exec for all Darwin versions. On newer OS versions,
|
||||
// returning 'false' doesn't do anything anyway, because we don't re-exec.
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::GTEST_FLAG(death_test_style) = "threadsafe";
|
||||
|
|
|
|||
|
|
@ -62,7 +62,9 @@ typedef uint64_t U8;
|
|||
|
||||
static const int kPageSize = 4096;
|
||||
|
||||
const size_t kLargeMalloc = 1 << 24;
|
||||
// Big enough to be handled by secondary allocator and small enough to fit into
|
||||
// quarantine for all configurations.
|
||||
const size_t kLargeMalloc = 1 << 22;
|
||||
|
||||
extern void free_aaa(void *p);
|
||||
extern void *malloc_aaa(size_t size);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|||
"${CMAKE_SOURCE_DIR}/../../cmake/Modules")
|
||||
include(base-config-ix)
|
||||
include(CompilerRTUtils)
|
||||
|
||||
load_llvm_config()
|
||||
construct_compiler_rt_default_triple()
|
||||
|
||||
if(APPLE)
|
||||
include(CompilerRTDarwinUtils)
|
||||
endif()
|
||||
|
|
@ -160,7 +164,11 @@ set(GENERIC_SOURCES
|
|||
umodsi3.c
|
||||
umodti3.c)
|
||||
|
||||
if(COMPILER_RT_SUPPORTS_ATOMIC_KEYWORD)
|
||||
option(COMPILER_RT_EXCLUDE_ATOMIC_BUILTIN
|
||||
"Skip the atomic builtin (this may be needed if system headers are unavailable)"
|
||||
Off)
|
||||
|
||||
if(COMPILER_RT_HAS_ATOMIC_KEYWORD AND NOT COMPILER_RT_EXCLUDE_ATOMIC_BUILTIN)
|
||||
set(GENERIC_SOURCES
|
||||
${GENERIC_SOURCES}
|
||||
atomic.c)
|
||||
|
|
@ -261,68 +269,14 @@ else () # MSVC
|
|||
endif () # if (NOT MSVC)
|
||||
|
||||
set(arm_SOURCES
|
||||
arm/adddf3vfp.S
|
||||
arm/addsf3vfp.S
|
||||
arm/aeabi_cdcmp.S
|
||||
arm/aeabi_cdcmpeq_check_nan.c
|
||||
arm/aeabi_cfcmp.S
|
||||
arm/aeabi_cfcmpeq_check_nan.c
|
||||
arm/aeabi_dcmp.S
|
||||
arm/aeabi_div0.c
|
||||
arm/aeabi_drsub.c
|
||||
arm/aeabi_fcmp.S
|
||||
arm/aeabi_frsub.c
|
||||
arm/aeabi_idivmod.S
|
||||
arm/aeabi_ldivmod.S
|
||||
arm/aeabi_memcmp.S
|
||||
arm/aeabi_memcpy.S
|
||||
arm/aeabi_memmove.S
|
||||
arm/aeabi_memset.S
|
||||
arm/aeabi_uidivmod.S
|
||||
arm/aeabi_uldivmod.S
|
||||
arm/bswapdi2.S
|
||||
arm/bswapsi2.S
|
||||
arm/clzdi2.S
|
||||
arm/clzsi2.S
|
||||
arm/comparesf2.S
|
||||
arm/divdf3vfp.S
|
||||
arm/divmodsi4.S
|
||||
arm/divsf3vfp.S
|
||||
arm/divsi3.S
|
||||
arm/eqdf2vfp.S
|
||||
arm/eqsf2vfp.S
|
||||
arm/extendsfdf2vfp.S
|
||||
arm/fixdfsivfp.S
|
||||
arm/fixsfsivfp.S
|
||||
arm/fixunsdfsivfp.S
|
||||
arm/fixunssfsivfp.S
|
||||
arm/floatsidfvfp.S
|
||||
arm/floatsisfvfp.S
|
||||
arm/floatunssidfvfp.S
|
||||
arm/floatunssisfvfp.S
|
||||
arm/gedf2vfp.S
|
||||
arm/gesf2vfp.S
|
||||
arm/gtdf2vfp.S
|
||||
arm/gtsf2vfp.S
|
||||
arm/ledf2vfp.S
|
||||
arm/lesf2vfp.S
|
||||
arm/ltdf2vfp.S
|
||||
arm/ltsf2vfp.S
|
||||
arm/modsi3.S
|
||||
arm/muldf3vfp.S
|
||||
arm/mulsf3vfp.S
|
||||
arm/nedf2vfp.S
|
||||
arm/negdf2vfp.S
|
||||
arm/negsf2vfp.S
|
||||
arm/nesf2vfp.S
|
||||
arm/restore_vfp_d8_d15_regs.S
|
||||
arm/save_vfp_d8_d15_regs.S
|
||||
arm/subdf3vfp.S
|
||||
arm/subsf3vfp.S
|
||||
arm/switch16.S
|
||||
arm/switch32.S
|
||||
arm/switch8.S
|
||||
arm/switchu8.S
|
||||
arm/sync_fetch_and_add_4.S
|
||||
arm/sync_fetch_and_add_8.S
|
||||
arm/sync_fetch_and_and_4.S
|
||||
|
|
@ -343,15 +297,112 @@ set(arm_SOURCES
|
|||
arm/sync_fetch_and_umin_8.S
|
||||
arm/sync_fetch_and_xor_4.S
|
||||
arm/sync_fetch_and_xor_8.S
|
||||
arm/sync_synchronize.S
|
||||
arm/truncdfsf2vfp.S
|
||||
arm/udivmodsi4.S
|
||||
arm/udivsi3.S
|
||||
arm/umodsi3.S
|
||||
arm/unorddf2vfp.S
|
||||
arm/unordsf2vfp.S
|
||||
${GENERIC_SOURCES})
|
||||
|
||||
set(arm_EABI_SOURCES
|
||||
arm/aeabi_cdcmp.S
|
||||
arm/aeabi_cdcmpeq_check_nan.c
|
||||
arm/aeabi_cfcmp.S
|
||||
arm/aeabi_cfcmpeq_check_nan.c
|
||||
arm/aeabi_dcmp.S
|
||||
arm/aeabi_div0.c
|
||||
arm/aeabi_drsub.c
|
||||
arm/aeabi_fcmp.S
|
||||
arm/aeabi_frsub.c
|
||||
arm/aeabi_idivmod.S
|
||||
arm/aeabi_ldivmod.S
|
||||
arm/aeabi_memcmp.S
|
||||
arm/aeabi_memcpy.S
|
||||
arm/aeabi_memmove.S
|
||||
arm/aeabi_memset.S
|
||||
arm/aeabi_uidivmod.S
|
||||
arm/aeabi_uldivmod.S)
|
||||
set(arm_Thumb1_JT_SOURCES
|
||||
arm/switch16.S
|
||||
arm/switch32.S
|
||||
arm/switch8.S
|
||||
arm/switchu8.S)
|
||||
set(arm_Thumb1_SjLj_EH_SOURCES
|
||||
arm/restore_vfp_d8_d15_regs.S
|
||||
arm/save_vfp_d8_d15_regs.S)
|
||||
set(arm_Thumb1_VFPv2_SOURCES
|
||||
arm/adddf3vfp.S
|
||||
arm/addsf3vfp.S
|
||||
arm/divdf3vfp.S
|
||||
arm/divsf3vfp.S
|
||||
arm/eqdf2vfp.S
|
||||
arm/eqsf2vfp.S
|
||||
arm/extendsfdf2vfp.S
|
||||
arm/fixdfsivfp.S
|
||||
arm/fixsfsivfp.S
|
||||
arm/fixunsdfsivfp.S
|
||||
arm/fixunssfsivfp.S
|
||||
arm/floatsidfvfp.S
|
||||
arm/floatsisfvfp.S
|
||||
arm/floatunssidfvfp.S
|
||||
arm/floatunssisfvfp.S
|
||||
arm/gedf2vfp.S
|
||||
arm/gesf2vfp.S
|
||||
arm/gtdf2vfp.S
|
||||
arm/gtsf2vfp.S
|
||||
arm/ledf2vfp.S
|
||||
arm/lesf2vfp.S
|
||||
arm/ltdf2vfp.S
|
||||
arm/ltsf2vfp.S
|
||||
arm/muldf3vfp.S
|
||||
arm/mulsf3vfp.S
|
||||
arm/nedf2vfp.S
|
||||
arm/negdf2vfp.S
|
||||
arm/negsf2vfp.S
|
||||
arm/nesf2vfp.S
|
||||
arm/subdf3vfp.S
|
||||
arm/subsf3vfp.S
|
||||
arm/truncdfsf2vfp.S
|
||||
arm/unorddf2vfp.S
|
||||
arm/unordsf2vfp.S)
|
||||
set(arm_Thumb1_icache_SOURCES
|
||||
arm/sync_synchronize.S)
|
||||
set(arm_Thumb1_SOURCES
|
||||
${arm_Thumb1_JT_SOURCES}
|
||||
${arm_Thumb1_SjLj_EH_SOURCES}
|
||||
${arm_Thumb1_VFPv2_SOURCES}
|
||||
${arm_Thumb1_icache_SOURCES})
|
||||
|
||||
if(MINGW)
|
||||
set(arm_SOURCES
|
||||
arm/aeabi_idivmod.S
|
||||
arm/aeabi_ldivmod.S
|
||||
arm/aeabi_uidivmod.S
|
||||
arm/aeabi_uldivmod.S
|
||||
divmoddi4.c
|
||||
divmodsi4.c
|
||||
divdi3.c
|
||||
divsi3.c
|
||||
fixdfdi.c
|
||||
fixsfdi.c
|
||||
fixunsdfdi.c
|
||||
fixunssfdi.c
|
||||
floatdidf.c
|
||||
floatdisf.c
|
||||
floatundidf.c
|
||||
floatundisf.c
|
||||
mingw_fixfloat.c
|
||||
moddi3.c
|
||||
udivmoddi4.c
|
||||
udivmodsi4.c
|
||||
udivsi3.c
|
||||
umoddi3.c)
|
||||
elseif(NOT WIN32)
|
||||
# TODO the EABI sources should only be added to EABI targets
|
||||
set(arm_SOURCES
|
||||
${arm_SOURCES}
|
||||
${arm_EABI_SOURCES}
|
||||
${arm_Thumb1_SOURCES})
|
||||
endif()
|
||||
|
||||
set(aarch64_SOURCES
|
||||
comparetf2.c
|
||||
extenddftf2.c
|
||||
|
|
@ -398,7 +449,24 @@ if (APPLE)
|
|||
add_subdirectory(macho_embedded)
|
||||
darwin_add_builtin_libraries(${BUILTIN_SUPPORTED_OS})
|
||||
else ()
|
||||
append_string_if(COMPILER_RT_HAS_STD_C99_FLAG -std=gnu99 maybe_stdc99)
|
||||
set(BUILTIN_CFLAGS "")
|
||||
|
||||
append_list_if(COMPILER_RT_HAS_STD_C11_FLAG -std=c11 BUILTIN_CFLAGS)
|
||||
|
||||
# These flags would normally be added to CMAKE_C_FLAGS by the llvm
|
||||
# cmake step. Add them manually if this is a standalone build.
|
||||
if(COMPILER_RT_STANDALONE_BUILD)
|
||||
append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC BUILTIN_CFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_FNO_BUILTIN_FLAG -fno-builtin BUILTIN_CFLAGS)
|
||||
append_list_if(COMPILER_RT_HAS_VISIBILITY_HIDDEN_FLAG -fvisibility=hidden BUILTIN_CFLAGS)
|
||||
if(NOT COMPILER_RT_DEBUG)
|
||||
append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fomit-frame-pointer BUILTIN_CFLAGS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BUILTIN_DEFS "")
|
||||
|
||||
append_list_if(COMPILER_RT_HAS_VISIBILITY_HIDDEN_FLAG VISIBILITY_HIDDEN BUILTIN_DEFS)
|
||||
|
||||
foreach (arch ${BUILTIN_SUPPORTED_ARCH})
|
||||
if (CAN_TARGET_${arch})
|
||||
|
|
@ -413,11 +481,18 @@ else ()
|
|||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# Needed for clear_cache on debug mode, due to r7's usage in inline asm.
|
||||
# Release mode already sets it via -O2/3, Debug mode doesn't.
|
||||
if (${arch} STREQUAL "armhf")
|
||||
list(APPEND BUILTIN_CFLAGS -fomit-frame-pointer)
|
||||
endif()
|
||||
|
||||
add_compiler_rt_runtime(clang_rt.builtins
|
||||
STATIC
|
||||
ARCHS ${arch}
|
||||
SOURCES ${${arch}_SOURCES}
|
||||
CFLAGS ${maybe_stdc99}
|
||||
DEFS ${BUILTIN_DEFS}
|
||||
CFLAGS ${BUILTIN_CFLAGS}
|
||||
PARENT_TARGET builtins)
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
#===- lib/builtins/Makefile.mk -----------------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
|
||||
# Add arch specific optimized implementations.
|
||||
SubDirs += i386 ppc x86_64 arm armv6m
|
||||
|
||||
# Add ARM64 dir.
|
||||
SubDirs += arm64
|
||||
|
||||
# Define the variables for this specific directory.
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o)
|
||||
Implementation := Generic
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#===- lib/builtins/arm/Makefile.mk -------------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := armv5 armv6 armv7 armv7k armv7m armv7em armv7s
|
||||
|
||||
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
|
||||
Implementation := Optimized
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)
|
||||
|
|
@ -15,16 +15,34 @@
|
|||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#define __aeabi_idivmod __rt_sdiv
|
||||
#endif
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod)
|
||||
#if __ARM_ARCH_ISA_THUMB == 1
|
||||
push {r0, r1, lr}
|
||||
bl SYMBOL_NAME(__divsi3)
|
||||
pop {r1, r2, r3} // now r0 = quot, r1 = num, r2 = denom
|
||||
muls r2, r2, r0 // r2 = quot * denom
|
||||
subs r1, r1, r2
|
||||
JMP (r3)
|
||||
#else
|
||||
push { lr }
|
||||
sub sp, sp, #4
|
||||
mov r2, sp
|
||||
#if defined(__MINGW32__)
|
||||
mov r3, r0
|
||||
mov r0, r1
|
||||
mov r1, r3
|
||||
#endif
|
||||
bl SYMBOL_NAME(__divmodsi4)
|
||||
ldr r1, [sp]
|
||||
add sp, sp, #4
|
||||
pop { pc }
|
||||
#endif // __ARM_ARCH_ISA_THUMB == 1
|
||||
END_COMPILERRT_FUNCTION(__aeabi_idivmod)
|
||||
|
||||
NO_EXEC_STACK_DIRECTIVE
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#define __aeabi_ldivmod __rt_sdiv64
|
||||
#endif
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
|
||||
|
|
@ -23,6 +27,14 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
|
|||
sub sp, sp, #16
|
||||
add r12, sp, #8
|
||||
str r12, [sp]
|
||||
#if defined(__MINGW32__)
|
||||
mov r12, r0
|
||||
mov r0, r2
|
||||
mov r2, r12
|
||||
mov r12, r1
|
||||
mov r1, r3
|
||||
mov r3, r12
|
||||
#endif
|
||||
bl SYMBOL_NAME(__divmoddi4)
|
||||
ldr r2, [sp, #8]
|
||||
ldr r3, [sp, #12]
|
||||
|
|
|
|||
|
|
@ -16,16 +16,40 @@
|
|||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#define __aeabi_uidivmod __rt_udiv
|
||||
#endif
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_uidivmod)
|
||||
#if __ARM_ARCH_ISA_THUMB == 1
|
||||
cmp r0, r1
|
||||
bcc LOCAL_LABEL(case_denom_larger)
|
||||
push {r0, r1, lr}
|
||||
bl SYMBOL_NAME(__aeabi_uidiv)
|
||||
pop {r1, r2, r3}
|
||||
muls r2, r2, r0 // r2 = quot * denom
|
||||
subs r1, r1, r2
|
||||
JMP (r3)
|
||||
LOCAL_LABEL(case_denom_larger):
|
||||
movs r1, r0
|
||||
movs r0, #0
|
||||
JMP (lr)
|
||||
#else
|
||||
push { lr }
|
||||
sub sp, sp, #4
|
||||
mov r2, sp
|
||||
#if defined(__MINGW32__)
|
||||
mov r3, r0
|
||||
mov r0, r1
|
||||
mov r1, r3
|
||||
#endif
|
||||
bl SYMBOL_NAME(__udivmodsi4)
|
||||
ldr r1, [sp]
|
||||
add sp, sp, #4
|
||||
pop { pc }
|
||||
#endif
|
||||
END_COMPILERRT_FUNCTION(__aeabi_uidivmod)
|
||||
|
||||
NO_EXEC_STACK_DIRECTIVE
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
// return {quot, rem};
|
||||
// }
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#define __aeabi_uldivmod __rt_udiv64
|
||||
#endif
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod)
|
||||
|
|
@ -23,6 +27,14 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod)
|
|||
sub sp, sp, #16
|
||||
add r12, sp, #8
|
||||
str r12, [sp]
|
||||
#if defined(__MINGW32__)
|
||||
mov r12, r0
|
||||
mov r0, r2
|
||||
mov r2, r12
|
||||
mov r12, r1
|
||||
mov r1, r3
|
||||
mov r3, r12
|
||||
#endif
|
||||
bl SYMBOL_NAME(__udivmoddi4)
|
||||
ldr r2, [sp, #8]
|
||||
ldr r3, [sp, #12]
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@
|
|||
|
||||
#include "../assembly.h"
|
||||
.syntax unified
|
||||
#if __ARM_ARCH_ISA_THUMB == 2
|
||||
.thumb
|
||||
#endif
|
||||
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__eqsf2)
|
||||
|
|
|
|||
|
|
@ -49,17 +49,37 @@ LOCAL_LABEL(divzero):
|
|||
#else
|
||||
ESTABLISH_FRAME
|
||||
// Set aside the sign of the quotient.
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
movs r4, r0
|
||||
eors r4, r1
|
||||
# else
|
||||
eor r4, r0, r1
|
||||
# endif
|
||||
// Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31).
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
asrs r2, r0, #31
|
||||
asrs r3, r1, #31
|
||||
eors r0, r2
|
||||
eors r1, r3
|
||||
subs r0, r0, r2
|
||||
subs r1, r1, r3
|
||||
# else
|
||||
eor r2, r0, r0, asr #31
|
||||
eor r3, r1, r1, asr #31
|
||||
sub r0, r2, r0, asr #31
|
||||
sub r1, r3, r1, asr #31
|
||||
# endif
|
||||
// abs(a) / abs(b)
|
||||
bl SYMBOL_NAME(__udivsi3)
|
||||
// Apply sign of quotient to result and return.
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
asrs r4, #31
|
||||
eors r0, r4
|
||||
subs r0, r0, r4
|
||||
# else
|
||||
eor r0, r0, r4, asr #31
|
||||
sub r0, r0, r4, asr #31
|
||||
# endif
|
||||
CLEAR_FRAME_AND_RETURN
|
||||
#endif
|
||||
END_COMPILERRT_FUNCTION(__divsi3)
|
||||
|
|
|
|||
|
|
@ -40,12 +40,26 @@ DEFINE_COMPILERRT_FUNCTION(__udivsi3)
|
|||
#else
|
||||
cmp r1, #1
|
||||
bcc LOCAL_LABEL(divby0)
|
||||
#if __ARM_ARCH_ISA_THUMB == 1
|
||||
bne LOCAL_LABEL(num_neq_denom)
|
||||
JMP(lr)
|
||||
LOCAL_LABEL(num_neq_denom):
|
||||
#else
|
||||
IT(eq)
|
||||
JMPc(lr, eq)
|
||||
#endif
|
||||
cmp r0, r1
|
||||
#if __ARM_ARCH_ISA_THUMB == 1
|
||||
bhs LOCAL_LABEL(num_ge_denom)
|
||||
movs r0, #0
|
||||
JMP(lr)
|
||||
LOCAL_LABEL(num_ge_denom):
|
||||
#else
|
||||
ITT(cc)
|
||||
movcc r0, #0
|
||||
JMPc(lr, cc)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Implement division using binary long division algorithm.
|
||||
*
|
||||
|
|
@ -62,7 +76,7 @@ DEFINE_COMPILERRT_FUNCTION(__udivsi3)
|
|||
* that (r0 << shift) < 2 * r1. The quotient is stored in r3.
|
||||
*/
|
||||
|
||||
# ifdef __ARM_FEATURE_CLZ
|
||||
# if defined(__ARM_FEATURE_CLZ)
|
||||
clz ip, r0
|
||||
clz r3, r1
|
||||
/* r0 >= r1 implies clz(r0) <= clz(r1), so ip <= r3. */
|
||||
|
|
@ -77,49 +91,128 @@ DEFINE_COMPILERRT_FUNCTION(__udivsi3)
|
|||
sub ip, ip, r3, lsl #3
|
||||
mov r3, #0
|
||||
bx ip
|
||||
# else
|
||||
# else /* No CLZ Feature */
|
||||
# if __ARM_ARCH_ISA_THUMB == 2
|
||||
# error THUMB mode requires CLZ or UDIV
|
||||
# endif
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
# define BLOCK_SIZE 10
|
||||
# else
|
||||
# define BLOCK_SIZE 12
|
||||
# endif
|
||||
|
||||
mov r2, r0
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
mov ip, r0
|
||||
adr r0, LOCAL_LABEL(div0block)
|
||||
adds r0, #1
|
||||
# else
|
||||
adr ip, LOCAL_LABEL(div0block)
|
||||
|
||||
lsr r3, r2, #16
|
||||
# endif
|
||||
lsrs r3, r2, #16
|
||||
cmp r3, r1
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
blo LOCAL_LABEL(skip_16)
|
||||
movs r2, r3
|
||||
subs r0, r0, #(16 * BLOCK_SIZE)
|
||||
LOCAL_LABEL(skip_16):
|
||||
# else
|
||||
movhs r2, r3
|
||||
subhs ip, ip, #(16 * 12)
|
||||
subhs ip, ip, #(16 * BLOCK_SIZE)
|
||||
# endif
|
||||
|
||||
lsr r3, r2, #8
|
||||
lsrs r3, r2, #8
|
||||
cmp r3, r1
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
blo LOCAL_LABEL(skip_8)
|
||||
movs r2, r3
|
||||
subs r0, r0, #(8 * BLOCK_SIZE)
|
||||
LOCAL_LABEL(skip_8):
|
||||
# else
|
||||
movhs r2, r3
|
||||
subhs ip, ip, #(8 * 12)
|
||||
subhs ip, ip, #(8 * BLOCK_SIZE)
|
||||
# endif
|
||||
|
||||
lsr r3, r2, #4
|
||||
lsrs r3, r2, #4
|
||||
cmp r3, r1
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
blo LOCAL_LABEL(skip_4)
|
||||
movs r2, r3
|
||||
subs r0, r0, #(4 * BLOCK_SIZE)
|
||||
LOCAL_LABEL(skip_4):
|
||||
# else
|
||||
movhs r2, r3
|
||||
subhs ip, #(4 * 12)
|
||||
subhs ip, #(4 * BLOCK_SIZE)
|
||||
# endif
|
||||
|
||||
lsr r3, r2, #2
|
||||
lsrs r3, r2, #2
|
||||
cmp r3, r1
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
blo LOCAL_LABEL(skip_2)
|
||||
movs r2, r3
|
||||
subs r0, r0, #(2 * BLOCK_SIZE)
|
||||
LOCAL_LABEL(skip_2):
|
||||
# else
|
||||
movhs r2, r3
|
||||
subhs ip, ip, #(2 * 12)
|
||||
subhs ip, ip, #(2 * BLOCK_SIZE)
|
||||
# endif
|
||||
|
||||
/* Last block, no need to update r2 or r3. */
|
||||
cmp r1, r2, lsr #1
|
||||
subls ip, ip, #(1 * 12)
|
||||
# if __ARM_ARCH_ISA_THUMB == 1
|
||||
lsrs r3, r2, #1
|
||||
cmp r3, r1
|
||||
blo LOCAL_LABEL(skip_1)
|
||||
subs r0, r0, #(1 * BLOCK_SIZE)
|
||||
LOCAL_LABEL(skip_1):
|
||||
movs r2, r0
|
||||
mov r0, ip
|
||||
movs r3, #0
|
||||
JMP (r2)
|
||||
|
||||
mov r3, #0
|
||||
# else
|
||||
cmp r1, r2, lsr #1
|
||||
subls ip, ip, #(1 * BLOCK_SIZE)
|
||||
|
||||
movs r3, #0
|
||||
|
||||
JMP(ip)
|
||||
# endif
|
||||
# endif
|
||||
# endif /* __ARM_FEATURE_CLZ */
|
||||
|
||||
|
||||
#define IMM #
|
||||
/* due to the range limit of branch in Thumb1, we have to place the
|
||||
block closer */
|
||||
LOCAL_LABEL(divby0):
|
||||
movs r0, #0
|
||||
# if defined(__ARM_EABI__)
|
||||
bl __aeabi_idiv0 // due to relocation limit, can't use b.
|
||||
# endif
|
||||
JMP(lr)
|
||||
|
||||
|
||||
#if __ARM_ARCH_ISA_THUMB == 1
|
||||
#define block(shift) \
|
||||
lsls r2, r1, IMM shift; \
|
||||
cmp r0, r2; \
|
||||
blo LOCAL_LABEL(block_skip_##shift); \
|
||||
subs r0, r0, r2; \
|
||||
LOCAL_LABEL(block_skip_##shift) :; \
|
||||
adcs r3, r3 /* same as ((r3 << 1) | Carry). Carry is set if r0 >= r2. */
|
||||
|
||||
/* TODO: if current location counter is not not word aligned, we don't
|
||||
need the .p2align and nop */
|
||||
/* Label div0block must be word-aligned. First align block 31 */
|
||||
.p2align 2
|
||||
nop /* Padding to align div0block as 31 blocks = 310 bytes */
|
||||
|
||||
#else
|
||||
#define block(shift) \
|
||||
cmp r0, r1, lsl IMM shift; \
|
||||
ITT(hs); \
|
||||
WIDE(addhs) r3, r3, IMM (1 << shift); \
|
||||
WIDE(subhs) r0, r0, r1, lsl IMM shift
|
||||
#endif
|
||||
|
||||
block(31)
|
||||
block(30)
|
||||
|
|
@ -159,12 +252,14 @@ LOCAL_LABEL(div0block):
|
|||
JMP(lr)
|
||||
#endif /* __ARM_ARCH_EXT_IDIV__ */
|
||||
|
||||
#if __ARM_ARCH_EXT_IDIV__
|
||||
LOCAL_LABEL(divby0):
|
||||
mov r0, #0
|
||||
#ifdef __ARM_EABI__
|
||||
b __aeabi_idiv0
|
||||
#else
|
||||
JMP(lr)
|
||||
mov r0, #0
|
||||
# ifdef __ARM_EABI__
|
||||
b __aeabi_idiv0
|
||||
# else
|
||||
JMP(lr)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
END_COMPILERRT_FUNCTION(__udivsi3)
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
#===- lib/builtins/arm64/Makefile.mk -----------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := arm64
|
||||
|
||||
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
|
||||
Implementation := Optimized
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#===- lib/builtins/arm/Makefile.mk -------------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := armv6m
|
||||
|
||||
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
|
||||
Implementation := Optimized
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5
|
||||
#define ARM_HAS_BX
|
||||
#endif
|
||||
#if !defined(__ARM_FEATURE_CLZ) && \
|
||||
#if !defined(__ARM_FEATURE_CLZ) && __ARM_ARCH_ISA_THUMB != 1 && \
|
||||
(__ARM_ARCH >= 6 || (__ARM_ARCH == 5 && !defined(__ARM_ARCH_5__)))
|
||||
#define __ARM_FEATURE_CLZ
|
||||
#endif
|
||||
|
|
@ -149,6 +149,7 @@
|
|||
#define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \
|
||||
.globl SYMBOL_NAME(name) SEPARATOR \
|
||||
SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \
|
||||
DECLARE_SYMBOL_VISIBILITY(SYMBOL_NAME(name)) SEPARATOR \
|
||||
.set SYMBOL_NAME(name), SYMBOL_NAME(target) SEPARATOR
|
||||
|
||||
#if defined(__ARM_EABI__)
|
||||
|
|
|
|||
|
|
@ -229,13 +229,20 @@ void __atomic_exchange_c(int size, void *ptr, void *val, void *old, int model) {
|
|||
// Where the size is known at compile time, the compiler may emit calls to
|
||||
// specialised versions of the above functions.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef __SIZEOF_INT128__
|
||||
#define OPTIMISED_CASES\
|
||||
OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t)\
|
||||
OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t)\
|
||||
OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t)\
|
||||
OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t)\
|
||||
/* FIXME: __uint128_t isn't available on 32 bit platforms.
|
||||
OPTIMISED_CASE(16, IS_LOCK_FREE_16, __uint128_t)*/\
|
||||
OPTIMISED_CASE(16, IS_LOCK_FREE_16, __uint128_t)
|
||||
#else
|
||||
#define OPTIMISED_CASES\
|
||||
OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t)\
|
||||
OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t)\
|
||||
OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t)\
|
||||
OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t)
|
||||
#endif
|
||||
|
||||
#define OPTIMISED_CASE(n, lockfree, type)\
|
||||
type __atomic_load_##n(type *src, int model) {\
|
||||
|
|
|
|||
|
|
@ -110,10 +110,12 @@ void __clear_cache(void *start, void *end) {
|
|||
#elif defined(__linux__)
|
||||
register int start_reg __asm("r0") = (int) (intptr_t) start;
|
||||
const register int end_reg __asm("r1") = (int) (intptr_t) end;
|
||||
const register int flags __asm("r2") = 0;
|
||||
const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush;
|
||||
__asm __volatile("svc 0x0"
|
||||
: "=r"(start_reg)
|
||||
: "r"(syscall_nr), "r"(start_reg), "r"(end_reg));
|
||||
: "r"(syscall_nr), "r"(start_reg), "r"(end_reg),
|
||||
"r"(flags));
|
||||
if (start_reg != 0) {
|
||||
compilerrt_abort();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
#===- lib/builtins/i386/Makefile.mk ------------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := i386
|
||||
|
||||
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
|
||||
Implementation := Optimized
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)
|
||||
|
|
@ -91,14 +91,14 @@ COMPILER_RT_ABI tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem);
|
|||
#include <intrin.h>
|
||||
|
||||
uint32_t __inline __builtin_ctz(uint32_t value) {
|
||||
uint32_t trailing_zero = 0;
|
||||
unsigned long trailing_zero = 0;
|
||||
if (_BitScanForward(&trailing_zero, value))
|
||||
return trailing_zero;
|
||||
return 32;
|
||||
}
|
||||
|
||||
uint32_t __inline __builtin_clz(uint32_t value) {
|
||||
uint32_t leading_zero = 0;
|
||||
unsigned long leading_zero = 0;
|
||||
if (_BitScanReverse(&leading_zero, value))
|
||||
return 31 - leading_zero;
|
||||
return 32;
|
||||
|
|
@ -106,7 +106,7 @@ uint32_t __inline __builtin_clz(uint32_t value) {
|
|||
|
||||
#if defined(_M_ARM) || defined(_M_X64)
|
||||
uint32_t __inline __builtin_clzll(uint64_t value) {
|
||||
uint32_t leading_zero = 0;
|
||||
unsigned long leading_zero = 0;
|
||||
if (_BitScanReverse64(&leading_zero, value))
|
||||
return 63 - leading_zero;
|
||||
return 64;
|
||||
|
|
|
|||
36
lib/builtins/mingw_fixfloat.c
Normal file
36
lib/builtins/mingw_fixfloat.c
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* ===-- mingw_fixfloat.c - Wrap int/float conversions for arm/windows -----===
|
||||
*
|
||||
* The LLVM Compiler Infrastructure
|
||||
*
|
||||
* This file is dual licensed under the MIT and the University of Illinois Open
|
||||
* Source Licenses. See LICENSE.TXT for details.
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include "int_lib.h"
|
||||
|
||||
COMPILER_RT_ABI di_int __fixdfdi(double a);
|
||||
COMPILER_RT_ABI di_int __fixsfdi(float a);
|
||||
COMPILER_RT_ABI du_int __fixunsdfdi(double a);
|
||||
COMPILER_RT_ABI du_int __fixunssfdi(float a);
|
||||
COMPILER_RT_ABI double __floatdidf(di_int a);
|
||||
COMPILER_RT_ABI float __floatdisf(di_int a);
|
||||
COMPILER_RT_ABI double __floatundidf(du_int a);
|
||||
COMPILER_RT_ABI float __floatundisf(du_int a);
|
||||
|
||||
COMPILER_RT_ABI di_int __dtoi64(double a) { return __fixdfdi(a); }
|
||||
|
||||
COMPILER_RT_ABI di_int __stoi64(float a) { return __fixsfdi(a); }
|
||||
|
||||
COMPILER_RT_ABI du_int __dtou64(double a) { return __fixunsdfdi(a); }
|
||||
|
||||
COMPILER_RT_ABI du_int __stou64(float a) { return __fixunssfdi(a); }
|
||||
|
||||
COMPILER_RT_ABI double __i64tod(di_int a) { return __floatdidf(a); }
|
||||
|
||||
COMPILER_RT_ABI float __i64tos(di_int a) { return __floatdisf(a); }
|
||||
|
||||
COMPILER_RT_ABI double __u64tod(du_int a) { return __floatundidf(a); }
|
||||
|
||||
COMPILER_RT_ABI float __u64tos(du_int a) { return __floatundisf(a); }
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#===- lib/builtins/ppc/Makefile.mk -------------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := ppc
|
||||
|
||||
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
|
||||
Implementation := Optimized
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#===- lib/builtins/x86_64/Makefile.mk ----------------------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := builtins
|
||||
SubDirs :=
|
||||
OnlyArchs := x86_64 x86_64h
|
||||
|
||||
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o)
|
||||
Implementation := Optimized
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard lib/*.h $(Dir)/*.h)
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
add_custom_target(cfi)
|
||||
set_target_properties(cfi PROPERTIES FOLDER "Compiler-RT Misc")
|
||||
add_compiler_rt_component(cfi)
|
||||
|
||||
set(CFI_SOURCES cfi.cc)
|
||||
|
||||
|
|
@ -36,4 +35,3 @@ foreach(arch ${CFI_SUPPORTED_ARCH})
|
|||
endforeach()
|
||||
|
||||
add_compiler_rt_resource_file(cfi_blacklist cfi_blacklist.txt cfi)
|
||||
add_dependencies(compiler-rt cfi)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ typedef ElfW(Ehdr) Elf_Ehdr;
|
|||
#include "ubsan/ubsan_handlers.h"
|
||||
#endif
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
namespace __cfi {
|
||||
|
||||
#define kCfiShadowLimitsStorageSize 4096 // 1 page
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ append_rtti_flag(OFF DFSAN_COMMON_CFLAGS)
|
|||
append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding DFSAN_COMMON_CFLAGS)
|
||||
|
||||
# Static runtime library.
|
||||
add_custom_target(dfsan)
|
||||
set_target_properties(dfsan PROPERTIES FOLDER "Compiler-RT Misc")
|
||||
add_compiler_rt_component(dfsan)
|
||||
|
||||
foreach(arch ${DFSAN_SUPPORTED_ARCH})
|
||||
set(DFSAN_CFLAGS ${DFSAN_COMMON_CFLAGS})
|
||||
|
|
@ -46,5 +45,3 @@ add_custom_command(OUTPUT ${dfsan_abilist_filename}
|
|||
add_dependencies(dfsan dfsan_abilist)
|
||||
install(FILES ${dfsan_abilist_filename}
|
||||
DESTINATION ${COMPILER_RT_INSTALL_PATH})
|
||||
|
||||
add_dependencies(compiler-rt dfsan)
|
||||
|
|
|
|||
|
|
@ -114,6 +114,26 @@ SANITIZER_INTERFACE_ATTRIBUTE uptr __dfsan_shadow_ptr_mask;
|
|||
// | reserved by kernel |
|
||||
// +--------------------+ 0x0000000000
|
||||
|
||||
// On Linux/AArch64 (48-bit VMA), memory is laid out as follow:
|
||||
//
|
||||
// +--------------------+ 0x1000000000000 (top of memory)
|
||||
// | application memory |
|
||||
// +--------------------+ 0xffff00008000 (kAppAddr)
|
||||
// | unused |
|
||||
// +--------------------+ 0xaaaab0000000 (top of PIE address)
|
||||
// | application PIE |
|
||||
// +--------------------+ 0xaaaaa0000000 (top of PIE address)
|
||||
// | |
|
||||
// | unused |
|
||||
// | |
|
||||
// +--------------------+ 0x1200000000 (kUnusedAddr)
|
||||
// | union table |
|
||||
// +--------------------+ 0x8000000000 (kUnionTableAddr)
|
||||
// | shadow memory |
|
||||
// +--------------------+ 0x0000010000 (kShadowAddr)
|
||||
// | reserved by kernel |
|
||||
// +--------------------+ 0x0000000000
|
||||
|
||||
typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels];
|
||||
|
||||
#ifdef DFSAN_RUNTIME_VMA
|
||||
|
|
@ -372,11 +392,12 @@ static void InitializePlatformEarly() {
|
|||
#ifdef DFSAN_RUNTIME_VMA
|
||||
__dfsan::vmaSize =
|
||||
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
|
||||
if (__dfsan::vmaSize == 39 || __dfsan::vmaSize == 42) {
|
||||
if (__dfsan::vmaSize == 39 || __dfsan::vmaSize == 42 ||
|
||||
__dfsan::vmaSize == 48) {
|
||||
__dfsan_shadow_ptr_mask = ShadowMask();
|
||||
} else {
|
||||
Printf("FATAL: DataFlowSanitizer: unsupported VMA range\n");
|
||||
Printf("FATAL: Found %d - Supported 39 and 42\n", __dfsan::vmaSize);
|
||||
Printf("FATAL: Found %d - Supported 39, 42, and 48\n", __dfsan::vmaSize);
|
||||
Die();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@
|
|||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "dfsan_platform.h"
|
||||
|
||||
using __sanitizer::uptr;
|
||||
using __sanitizer::u16;
|
||||
|
||||
// Copy declarations from public sanitizer/dfsan_interface.h header here.
|
||||
typedef u16 dfsan_label;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
|
||||
int fd, OFF_T offset) {
|
||||
void *res = REAL(mmap)(addr, length, prot, flags, fd, offset);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,13 @@ struct Mapping42 {
|
|||
static const uptr kShadowMask = ~0x3c000000000;
|
||||
};
|
||||
|
||||
struct Mapping48 {
|
||||
static const uptr kShadowAddr = 0x10000;
|
||||
static const uptr kUnionTableAddr = 0x8000000000;
|
||||
static const uptr kAppAddr = 0xffff00008000;
|
||||
static const uptr kShadowMask = ~0xfffff0000000;
|
||||
};
|
||||
|
||||
extern int vmaSize;
|
||||
# define DFSAN_RUNTIME_VMA 1
|
||||
#else
|
||||
|
|
@ -72,11 +79,13 @@ uptr MappingImpl(void) {
|
|||
template<int Type>
|
||||
uptr MappingArchImpl(void) {
|
||||
#ifdef __aarch64__
|
||||
if (vmaSize == 39)
|
||||
return MappingImpl<Mapping39, Type>();
|
||||
else
|
||||
return MappingImpl<Mapping42, Type>();
|
||||
switch (vmaSize) {
|
||||
case 39: return MappingImpl<Mapping39, Type>();
|
||||
case 42: return MappingImpl<Mapping42, Type>();
|
||||
case 48: return MappingImpl<Mapping48, Type>();
|
||||
}
|
||||
DCHECK(0);
|
||||
return 0;
|
||||
#else
|
||||
return MappingImpl<Mapping, Type>();
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -266,6 +266,14 @@ fun:reflect.makeFuncStub=discard
|
|||
# Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp
|
||||
fun:__sanitizer_cov_trace_cmp=custom
|
||||
fun:__sanitizer_cov_trace_cmp=uninstrumented
|
||||
fun:__sanitizer_cov_trace_cmp1=custom
|
||||
fun:__sanitizer_cov_trace_cmp1=uninstrumented
|
||||
fun:__sanitizer_cov_trace_cmp2=custom
|
||||
fun:__sanitizer_cov_trace_cmp2=uninstrumented
|
||||
fun:__sanitizer_cov_trace_cmp4=custom
|
||||
fun:__sanitizer_cov_trace_cmp4=uninstrumented
|
||||
fun:__sanitizer_cov_trace_cmp8=custom
|
||||
fun:__sanitizer_cov_trace_cmp8=uninstrumented
|
||||
# Similar for __sanitizer_cov_trace_switch
|
||||
fun:__sanitizer_cov_trace_switch=custom
|
||||
fun:__sanitizer_cov_trace_switch=uninstrumented
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Build for the EfficiencySanitizer runtime support library.
|
||||
|
||||
add_custom_target(esan)
|
||||
set_target_properties(esan PROPERTIES FOLDER "Compiler-RT Misc")
|
||||
add_compiler_rt_component(esan)
|
||||
|
||||
set(ESAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS})
|
||||
append_rtti_flag(OFF ESAN_RTL_CFLAGS)
|
||||
|
|
@ -36,8 +35,6 @@ foreach (arch ${ESAN_SUPPORTED_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()
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ static void reportStructCounter(StructHashMap::Handle &Handle) {
|
|||
type = "struct";
|
||||
start = &Struct->StructName[7];
|
||||
}
|
||||
// Remove the suffixes with '#' during print.
|
||||
end = strchr(start, '#');
|
||||
// Remove the suffixes with '$' during print.
|
||||
end = strchr(start, '$');
|
||||
CHECK(end != nullptr);
|
||||
Report(" %s %.*s\n", type, end - start, start);
|
||||
Report(" size = %u, count = %llu, ratio = %llu, array access = %llu\n",
|
||||
|
|
|
|||
|
|
@ -141,9 +141,17 @@ static bool verifyShadowScheme() {
|
|||
}
|
||||
#endif
|
||||
|
||||
uptr VmaSize;
|
||||
|
||||
static void initializeShadow() {
|
||||
verifyAddressSpace();
|
||||
|
||||
// This is based on the assumption that the intial stack is always allocated
|
||||
// in the topmost segment of the user address space and the assumption
|
||||
// holds true on all the platforms currently supported.
|
||||
VmaSize =
|
||||
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
|
||||
|
||||
DCHECK(verifyShadowScheme());
|
||||
|
||||
Mapping.initialize(ShadowScale[__esan_which_tool]);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ namespace __esan {
|
|||
|
||||
extern bool EsanIsInitialized;
|
||||
extern bool EsanDuringInit;
|
||||
extern uptr VmaSize;
|
||||
|
||||
void initializeLibrary(ToolType Tool);
|
||||
int finalizeLibrary();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#include "sanitizer_common/sanitizer_flag_parser.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
namespace __esan {
|
||||
|
||||
static const char EsanOptsEnv[] = "ESAN_OPTIONS";
|
||||
|
|
|
|||
381
lib/esan/esan_hashtable.h
Normal file
381
lib/esan/esan_hashtable.h
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
//===-- esan_hashtable.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.
|
||||
//
|
||||
// Generic resizing hashtable.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include <stddef.h>
|
||||
|
||||
namespace __esan {
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Default hash and comparison functions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
template <typename T> struct DefaultHash {
|
||||
size_t operator()(const T &Key) const {
|
||||
return (size_t)Key;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct DefaultEqual {
|
||||
bool operator()(const T &Key1, const T &Key2) const {
|
||||
return Key1 == Key2;
|
||||
}
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// HashTable declaration
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// A simple resizing and mutex-locked hashtable.
|
||||
//
|
||||
// If the default hash functor is used, KeyTy must have an operator size_t().
|
||||
// If the default comparison functor is used, KeyTy must have an operator ==.
|
||||
//
|
||||
// By default all operations are internally-synchronized with a mutex, with no
|
||||
// synchronization for payloads once hashtable functions return. If
|
||||
// ExternalLock is set to true, the caller should call the lock() and unlock()
|
||||
// routines around all hashtable operations and subsequent manipulation of
|
||||
// payloads.
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock = false,
|
||||
typename HashFuncTy = DefaultHash<KeyTy>,
|
||||
typename EqualFuncTy = DefaultEqual<KeyTy> >
|
||||
class HashTable {
|
||||
public:
|
||||
// InitialCapacity must be a power of 2.
|
||||
// ResizeFactor must be between 1 and 99 and indicates the
|
||||
// maximum percentage full that the table should ever be.
|
||||
HashTable(u32 InitialCapacity = 2048, u32 ResizeFactor = 70);
|
||||
~HashTable();
|
||||
bool lookup(const KeyTy &Key, DataTy &Payload); // Const except for Mutex.
|
||||
bool add(const KeyTy &Key, const DataTy &Payload);
|
||||
bool remove(const KeyTy &Key);
|
||||
u32 size(); // Const except for Mutex.
|
||||
// If the table is internally-synchronized, this lock must not be held
|
||||
// while a hashtable function is called as it will deadlock: the lock
|
||||
// is not recursive. This is meant for use with externally-synchronized
|
||||
// tables or with an iterator.
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
struct HashEntry {
|
||||
KeyTy Key;
|
||||
DataTy Payload;
|
||||
HashEntry *Next;
|
||||
};
|
||||
|
||||
public:
|
||||
struct HashPair {
|
||||
HashPair(KeyTy Key, DataTy Data) : Key(Key), Data(Data) {}
|
||||
KeyTy Key;
|
||||
DataTy Data;
|
||||
};
|
||||
|
||||
// This iterator does not perform any synchronization.
|
||||
// It expects the caller to lock the table across the whole iteration.
|
||||
// Calling HashTable functions while using the iterator is not supported.
|
||||
// The iterator returns copies of the keys and data.
|
||||
class iterator {
|
||||
public:
|
||||
iterator(
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table);
|
||||
iterator(const iterator &Src) = default;
|
||||
iterator &operator=(const iterator &Src) = default;
|
||||
HashPair operator*();
|
||||
iterator &operator++();
|
||||
iterator &operator++(int);
|
||||
bool operator==(const iterator &Cmp) const;
|
||||
bool operator!=(const iterator &Cmp) const;
|
||||
|
||||
private:
|
||||
iterator(
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table,
|
||||
int Idx);
|
||||
friend HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>;
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table;
|
||||
int Idx;
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::HashEntry
|
||||
*Entry;
|
||||
};
|
||||
|
||||
// No erase or insert iterator supported
|
||||
iterator begin();
|
||||
iterator end();
|
||||
|
||||
private:
|
||||
void resize();
|
||||
|
||||
HashEntry **Table;
|
||||
u32 Capacity;
|
||||
u32 Entries;
|
||||
const u32 ResizeFactor;
|
||||
BlockingMutex Mutex;
|
||||
const HashFuncTy HashFunc;
|
||||
const EqualFuncTy EqualFunc;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Hashtable implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::HashTable(
|
||||
u32 InitialCapacity, u32 ResizeFactor)
|
||||
: Capacity(InitialCapacity), Entries(0), ResizeFactor(ResizeFactor),
|
||||
HashFunc(HashFuncTy()), EqualFunc(EqualFuncTy()) {
|
||||
CHECK(IsPowerOfTwo(Capacity));
|
||||
CHECK(ResizeFactor >= 1 && ResizeFactor <= 99);
|
||||
Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *));
|
||||
internal_memset(Table, 0, Capacity * sizeof(HashEntry *));
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::~HashTable() {
|
||||
for (u32 i = 0; i < Capacity; ++i) {
|
||||
HashEntry *Entry = Table[i];
|
||||
while (Entry != nullptr) {
|
||||
HashEntry *Next = Entry->Next;
|
||||
Entry->Payload.~DataTy();
|
||||
InternalFree(Entry);
|
||||
Entry = Next;
|
||||
}
|
||||
}
|
||||
InternalFree(Table);
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
u32 HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::size() {
|
||||
u32 Res;
|
||||
if (!ExternalLock)
|
||||
Mutex.Lock();
|
||||
Res = Entries;
|
||||
if (!ExternalLock)
|
||||
Mutex.Unlock();
|
||||
return Res;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::lookup(
|
||||
const KeyTy &Key, DataTy &Payload) {
|
||||
if (!ExternalLock)
|
||||
Mutex.Lock();
|
||||
bool Found = false;
|
||||
size_t Hash = HashFunc(Key) % Capacity;
|
||||
HashEntry *Entry = Table[Hash];
|
||||
for (; Entry != nullptr; Entry = Entry->Next) {
|
||||
if (EqualFunc(Entry->Key, Key)) {
|
||||
Payload = Entry->Payload;
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ExternalLock)
|
||||
Mutex.Unlock();
|
||||
return Found;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::resize() {
|
||||
if (!ExternalLock)
|
||||
Mutex.CheckLocked();
|
||||
size_t OldCapacity = Capacity;
|
||||
HashEntry **OldTable = Table;
|
||||
Capacity *= 2;
|
||||
Table = (HashEntry **)InternalAlloc(Capacity * sizeof(HashEntry *));
|
||||
internal_memset(Table, 0, Capacity * sizeof(HashEntry *));
|
||||
// Re-hash
|
||||
for (u32 i = 0; i < OldCapacity; ++i) {
|
||||
HashEntry *OldEntry = OldTable[i];
|
||||
while (OldEntry != nullptr) {
|
||||
HashEntry *Next = OldEntry->Next;
|
||||
size_t Hash = HashFunc(OldEntry->Key) % Capacity;
|
||||
OldEntry->Next = Table[Hash];
|
||||
Table[Hash] = OldEntry;
|
||||
OldEntry = Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::add(
|
||||
const KeyTy &Key, const DataTy &Payload) {
|
||||
if (!ExternalLock)
|
||||
Mutex.Lock();
|
||||
bool Exists = false;
|
||||
size_t Hash = HashFunc(Key) % Capacity;
|
||||
HashEntry *Entry = Table[Hash];
|
||||
for (; Entry != nullptr; Entry = Entry->Next) {
|
||||
if (EqualFunc(Entry->Key, Key)) {
|
||||
Exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Exists) {
|
||||
Entries++;
|
||||
if (Entries * 100 >= Capacity * ResizeFactor) {
|
||||
resize();
|
||||
Hash = HashFunc(Key) % Capacity;
|
||||
}
|
||||
HashEntry *Add = (HashEntry *)InternalAlloc(sizeof(*Add));
|
||||
Add->Key = Key;
|
||||
Add->Payload = Payload;
|
||||
Add->Next = Table[Hash];
|
||||
Table[Hash] = Add;
|
||||
}
|
||||
if (!ExternalLock)
|
||||
Mutex.Unlock();
|
||||
return !Exists;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::remove(
|
||||
const KeyTy &Key) {
|
||||
if (!ExternalLock)
|
||||
Mutex.Lock();
|
||||
bool Found = false;
|
||||
size_t Hash = HashFunc(Key) % Capacity;
|
||||
HashEntry *Entry = Table[Hash];
|
||||
HashEntry *Prev = nullptr;
|
||||
for (; Entry != nullptr; Prev = Entry, Entry = Entry->Next) {
|
||||
if (EqualFunc(Entry->Key, Key)) {
|
||||
Found = true;
|
||||
Entries--;
|
||||
if (Prev == nullptr)
|
||||
Table[Hash] = Entry->Next;
|
||||
else
|
||||
Prev->Next = Entry->Next;
|
||||
Entry->Payload.~DataTy();
|
||||
InternalFree(Entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ExternalLock)
|
||||
Mutex.Unlock();
|
||||
return Found;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::lock() {
|
||||
Mutex.Lock();
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
void HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::unlock() {
|
||||
Mutex.Unlock();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Iterator implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||
iterator(
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table)
|
||||
: Table(Table), Idx(-1), Entry(nullptr) {
|
||||
operator++();
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||
iterator(
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy> *Table,
|
||||
int Idx)
|
||||
: Table(Table), Idx(Idx), Entry(nullptr) {
|
||||
CHECK(Idx >= (int)Table->Capacity); // Only used to create end().
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||
EqualFuncTy>::HashPair
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||
operator*() {
|
||||
CHECK(Idx >= 0 && Idx < (int)Table->Capacity);
|
||||
CHECK(Entry != nullptr);
|
||||
return HashPair(Entry->Key, Entry->Payload);
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||
EqualFuncTy>::iterator &
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||
operator++() {
|
||||
if (Entry != nullptr)
|
||||
Entry = Entry->Next;
|
||||
while (Entry == nullptr) {
|
||||
++Idx;
|
||||
if (Idx >= (int)Table->Capacity)
|
||||
break; // At end().
|
||||
Entry = Table->Table[Idx];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||
EqualFuncTy>::iterator &
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||
operator++(int) {
|
||||
iterator Temp(*this);
|
||||
operator++();
|
||||
return Temp;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||
operator==(const iterator &Cmp) const {
|
||||
return Cmp.Table == Table && Cmp.Idx == Idx && Cmp.Entry == Entry;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
bool HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::iterator::
|
||||
operator!=(const iterator &Cmp) const {
|
||||
return Cmp.Table != Table || Cmp.Idx != Idx || Cmp.Entry != Entry;
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||
EqualFuncTy>::iterator
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::begin() {
|
||||
return iterator(this);
|
||||
}
|
||||
|
||||
template <typename KeyTy, typename DataTy, bool ExternalLock,
|
||||
typename HashFuncTy, typename EqualFuncTy>
|
||||
typename HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy,
|
||||
EqualFuncTy>::iterator
|
||||
HashTable<KeyTy, DataTy, ExternalLock, HashFuncTy, EqualFuncTy>::end() {
|
||||
return iterator(this, Capacity);
|
||||
}
|
||||
|
||||
} // namespace __esan
|
||||
|
|
@ -461,28 +461,35 @@ INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
|
|||
// Malloc interceptors
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static char early_alloc_buf[128];
|
||||
static bool used_early_alloc_buf;
|
||||
static const uptr early_alloc_buf_size = 4096;
|
||||
static uptr allocated_bytes;
|
||||
static char early_alloc_buf[early_alloc_buf_size];
|
||||
|
||||
static bool isInEarlyAllocBuf(const void *ptr) {
|
||||
return ((uptr)ptr >= (uptr)early_alloc_buf &&
|
||||
((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
|
||||
}
|
||||
|
||||
static void *handleEarlyAlloc(uptr size) {
|
||||
// If esan is initialized during an interceptor (which happens with some
|
||||
// tcmalloc implementations that call pthread_mutex_lock), the call from
|
||||
// dlsym to calloc will deadlock. There is only one such calloc (dlsym
|
||||
// allocates a single pthread key), so we work around it by using a
|
||||
// static buffer for the calloc request. The loader currently needs
|
||||
// 32 bytes but we size at 128 to allow for future changes.
|
||||
// dlsym to calloc will deadlock.
|
||||
// dlsym may also call malloc before REAL(malloc) is retrieved from dlsym.
|
||||
// We work around it by using a static buffer for the early malloc/calloc
|
||||
// requests.
|
||||
// This solution will also allow us to deliberately intercept malloc & family
|
||||
// in the future (to perform tool actions on each allocation, without
|
||||
// replacing the allocator), as it also solves the problem of intercepting
|
||||
// calloc when it will itself be called before its REAL pointer is
|
||||
// initialized.
|
||||
CHECK(!used_early_alloc_buf && size < sizeof(early_alloc_buf));
|
||||
// We do not handle multiple threads here. This only happens at process init
|
||||
// time, and while it's possible for a shared library to create early threads
|
||||
// that race here, we consider that to be a corner case extreme enough that
|
||||
// it's not worth the effort to handle.
|
||||
used_early_alloc_buf = true;
|
||||
return (void *)early_alloc_buf;
|
||||
void *mem = (void *)&early_alloc_buf[allocated_bytes];
|
||||
allocated_bytes += size;
|
||||
CHECK_LT(allocated_bytes, early_alloc_buf_size);
|
||||
return mem;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, calloc, uptr size, uptr n) {
|
||||
|
|
@ -496,14 +503,20 @@ INTERCEPTOR(void*, calloc, uptr size, uptr n) {
|
|||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, malloc, uptr size) {
|
||||
if (EsanDuringInit && REAL(malloc) == nullptr)
|
||||
return handleEarlyAlloc(size);
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, malloc, size);
|
||||
return REAL(malloc)(size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, free, void *p) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, free, p);
|
||||
if (p == (void *)early_alloc_buf) {
|
||||
// We expect just a singleton use but we clear this for cleanliness.
|
||||
used_early_alloc_buf = false;
|
||||
// There are only a few early allocation requests, so we simply skip the free.
|
||||
if (isInEarlyAllocBuf(p))
|
||||
return;
|
||||
}
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, free, p);
|
||||
REAL(free)(p);
|
||||
}
|
||||
|
||||
|
|
@ -534,6 +547,7 @@ void initializeInterceptors() {
|
|||
ESAN_MAYBE_INTERCEPT_PTHREAD_SIGMASK;
|
||||
|
||||
INTERCEPT_FUNCTION(calloc);
|
||||
INTERCEPT_FUNCTION(malloc);
|
||||
INTERCEPT_FUNCTION(free);
|
||||
|
||||
// TODO(bruening): intercept routines that other sanitizers intercept that
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue