mirror of
https://github.com/intel/llvm.git
synced 2026-01-31 07:27:33 +08:00
[compilter-rt] Add unittests for interception library
Summary: This patch is adding unittests for the interception library. Reviewers: rnk Subscribers: majnemer, llvm-commits, wang0109, chrisha, tberghammer, danalbert, srhines Differential Revision: http://reviews.llvm.org/D21980 llvm-svn: 274657
This commit is contained in:
@@ -17,3 +17,7 @@ add_compiler_rt_object_libraries(RTInterception
|
||||
ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH}
|
||||
SOURCES ${INTERCEPTION_SOURCES}
|
||||
CFLAGS ${INTERCEPTION_CFLAGS})
|
||||
|
||||
if(COMPILER_RT_INCLUDE_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -201,6 +201,7 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
|
||||
size_t cursor = 0;
|
||||
while (cursor < size) {
|
||||
switch (code[cursor]) {
|
||||
case '\x50': // push eax
|
||||
case '\x51': // push ecx
|
||||
case '\x52': // push edx
|
||||
case '\x53': // push ebx
|
||||
@@ -341,7 +342,7 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
|
||||
}
|
||||
|
||||
static void **InterestingDLLsAvailable() {
|
||||
const char *InterestingDLLs[] = {
|
||||
static const char *InterestingDLLs[] = {
|
||||
"kernel32.dll",
|
||||
"msvcr110.dll", // VS2012
|
||||
"msvcr120.dll", // VS2013
|
||||
|
||||
147
compiler-rt/lib/interception/tests/CMakeLists.txt
Normal file
147
compiler-rt/lib/interception/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,147 @@
|
||||
include(CompilerRTCompile)
|
||||
|
||||
clang_compiler_add_cxx_check()
|
||||
|
||||
filter_available_targets(INTERCEPTION_UNITTEST_SUPPORTED_ARCH x86_64 i386 mips64 mips64el)
|
||||
|
||||
set(INTERCEPTION_UNITTESTS
|
||||
interception_linux_test.cc
|
||||
interception_test_main.cc
|
||||
interception_win_test.cc
|
||||
)
|
||||
|
||||
set(INTERCEPTION_TEST_HEADERS)
|
||||
|
||||
set(INTERCEPTION_TEST_CFLAGS_COMMON
|
||||
${COMPILER_RT_UNITTEST_CFLAGS}
|
||||
${COMPILER_RT_GTEST_CFLAGS}
|
||||
-I${COMPILER_RT_SOURCE_DIR}/include
|
||||
-I${COMPILER_RT_SOURCE_DIR}/lib
|
||||
-I${COMPILER_RT_SOURCE_DIR}/lib/interception
|
||||
-fno-rtti
|
||||
-O2
|
||||
-Werror=sign-compare
|
||||
-Wno-non-virtual-dtor)
|
||||
|
||||
# -gline-tables-only must be enough for these tests, so use it if possible.
|
||||
if(COMPILER_RT_TEST_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND INTERCEPTION_TEST_CFLAGS_COMMON -gline-tables-only)
|
||||
else()
|
||||
list(APPEND INTERCEPTION_TEST_CFLAGS_COMMON -g)
|
||||
endif()
|
||||
if(MSVC)
|
||||
list(APPEND INTERCEPTION_TEST_CFLAGS_COMMON -gcodeview)
|
||||
endif()
|
||||
list(APPEND INTERCEPTION_TEST_LINK_FLAGS_COMMON -g)
|
||||
|
||||
if(NOT MSVC)
|
||||
list(APPEND INTERCEPTION_TEST_LINK_FLAGS_COMMON --driver-mode=g++)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
list(APPEND INTERCEPTION_TEST_LINK_FLAGS_COMMON -pie)
|
||||
endif()
|
||||
|
||||
set(INTERCEPTION_TEST_LINK_LIBS)
|
||||
append_list_if(COMPILER_RT_HAS_LIBLOG log INTERCEPTION_TEST_LINK_LIBS)
|
||||
# NDK r10 requires -latomic almost always.
|
||||
append_list_if(ANDROID atomic INTERCEPTION_TEST_LINK_LIBS)
|
||||
|
||||
append_list_if(COMPILER_RT_HAS_LIBDL -ldl INTERCEPTION_TEST_LINK_FLAGS_COMMON)
|
||||
append_list_if(COMPILER_RT_HAS_LIBRT -lrt INTERCEPTION_TEST_LINK_FLAGS_COMMON)
|
||||
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread INTERCEPTION_TEST_LINK_FLAGS_COMMON)
|
||||
# x86_64 FreeBSD 9.2 additionally requires libc++ to build the tests. Also,
|
||||
# 'libm' shall be specified explicitly to build i386 tests.
|
||||
if(CMAKE_SYSTEM MATCHES "FreeBSD-9.2-RELEASE")
|
||||
list(APPEND INTERCEPTION_TEST_LINK_FLAGS_COMMON "-lc++ -lm")
|
||||
endif()
|
||||
|
||||
include_directories(..)
|
||||
include_directories(../..)
|
||||
|
||||
# Adds static library which contains interception object file
|
||||
# (universal binary on Mac and arch-specific object files on Linux).
|
||||
macro(add_interceptor_lib library)
|
||||
add_library(${library} STATIC ${ARGN})
|
||||
set_target_properties(${library} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endmacro()
|
||||
|
||||
function(get_interception_lib_for_arch arch lib lib_name)
|
||||
if(APPLE)
|
||||
set(tgt_name "RTInterception.test.osx")
|
||||
else()
|
||||
set(tgt_name "RTInterception.test.${arch}")
|
||||
endif()
|
||||
set(${lib} "${tgt_name}" PARENT_SCOPE)
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
set(configuration_path "${CMAKE_CFG_INTDIR}/")
|
||||
else()
|
||||
set(configuration_path "")
|
||||
endif()
|
||||
if(NOT MSVC)
|
||||
set(${lib_name} "${configuration_path}lib${tgt_name}.a" PARENT_SCOPE)
|
||||
else()
|
||||
set(${lib_name} "${configuration_path}${tgt_name}.lib" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Interception unit tests testsuite.
|
||||
add_custom_target(InterceptionUnitTests)
|
||||
set_target_properties(InterceptionUnitTests PROPERTIES
|
||||
FOLDER "Compiler-RT Tests")
|
||||
|
||||
# Adds interception tests for architecture.
|
||||
macro(add_interception_tests_for_arch arch)
|
||||
get_target_flags_for_arch(${arch} TARGET_FLAGS)
|
||||
set(INTERCEPTION_TEST_SOURCES ${INTERCEPTION_UNITTESTS}
|
||||
${COMPILER_RT_GTEST_SOURCE})
|
||||
set(INTERCEPTION_TEST_COMPILE_DEPS ${INTERCEPTION_TEST_HEADERS})
|
||||
if(NOT COMPILER_RT_STANDALONE_BUILD)
|
||||
list(APPEND INTERCEPTION_TEST_COMPILE_DEPS gtest)
|
||||
endif()
|
||||
set(INTERCEPTION_TEST_OBJECTS)
|
||||
foreach(source ${INTERCEPTION_TEST_SOURCES})
|
||||
get_filename_component(basename ${source} NAME)
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
set(output_obj "${CMAKE_CFG_INTDIR}/${basename}.${arch}.o")
|
||||
else()
|
||||
set(output_obj "${basename}.${arch}.o")
|
||||
endif()
|
||||
clang_compile(${output_obj} ${source}
|
||||
CFLAGS ${INTERCEPTION_TEST_CFLAGS_COMMON} ${TARGET_FLAGS}
|
||||
DEPS ${INTERCEPTION_TEST_COMPILE_DEPS})
|
||||
list(APPEND INTERCEPTION_TEST_OBJECTS ${output_obj})
|
||||
endforeach()
|
||||
get_interception_lib_for_arch(${arch} INTERCEPTION_COMMON_LIB
|
||||
INTERCEPTION_COMMON_LIB_NAME)
|
||||
# Add unittest target.
|
||||
set(INTERCEPTION_TEST_NAME "Interception-${arch}-Test")
|
||||
add_compiler_rt_test(InterceptionUnitTests ${INTERCEPTION_TEST_NAME}
|
||||
OBJECTS ${INTERCEPTION_TEST_OBJECTS}
|
||||
${INTERCEPTION_COMMON_LIB_NAME}
|
||||
DEPS ${INTERCEPTION_TEST_OBJECTS} ${INTERCEPTION_COMMON_LIB}
|
||||
LINK_FLAGS ${INTERCEPTION_TEST_LINK_FLAGS_COMMON}
|
||||
${TARGET_FLAGS})
|
||||
|
||||
|
||||
endmacro()
|
||||
|
||||
if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
|
||||
# We use just-built clang to build interception unittests, so we must
|
||||
# be sure that produced binaries would work.
|
||||
if(APPLE)
|
||||
add_interceptor_lib("RTInterception.test.osx"
|
||||
$<TARGET_OBJECTS:RTInterception.osx>)
|
||||
else()
|
||||
foreach(arch ${INTERCEPTION_UNITTEST_SUPPORTED_ARCH})
|
||||
add_interceptor_lib("RTInterception.test.${arch}"
|
||||
$<TARGET_OBJECTS:RTInterception.${arch}>)
|
||||
endforeach()
|
||||
endif()
|
||||
foreach(arch ${INTERCEPTION_UNITTEST_SUPPORTED_ARCH})
|
||||
add_interception_tests_for_arch(${arch})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
//===-- interception_linux_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 ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Tests for interception_linux.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "interception/interception.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// Too slow for debug build
|
||||
#if !SANITIZER_DEBUG
|
||||
#if SANITIZER_LINUX
|
||||
|
||||
static int InterceptorFunctionCalled;
|
||||
|
||||
DECLARE_REAL(int, isdigit, int);
|
||||
|
||||
INTERCEPTOR(int, isdigit, int d) {
|
||||
++InterceptorFunctionCalled;
|
||||
return d >= '0' && d <= '9';
|
||||
}
|
||||
|
||||
namespace __interception {
|
||||
|
||||
TEST(Interception, GetRealFunctionAddress) {
|
||||
uptr expected_malloc_address = (uptr)(void*)&malloc;
|
||||
uptr malloc_address = 0;
|
||||
EXPECT_TRUE(GetRealFunctionAddress("malloc", &malloc_address, 0, 0));
|
||||
EXPECT_EQ(expected_malloc_address, malloc_address);
|
||||
|
||||
uptr dummy_address = 0;
|
||||
EXPECT_TRUE(
|
||||
GetRealFunctionAddress("dummy_doesnt_exist__", &dummy_address, 0, 0));
|
||||
EXPECT_EQ(0U, dummy_address);
|
||||
}
|
||||
|
||||
TEST(Interception, Basic) {
|
||||
ASSERT_TRUE(INTERCEPT_FUNCTION(isdigit));
|
||||
|
||||
// After interception, the counter should be incremented.
|
||||
InterceptorFunctionCalled = 0;
|
||||
EXPECT_NE(0, isdigit('1'));
|
||||
EXPECT_EQ(1, InterceptorFunctionCalled);
|
||||
EXPECT_EQ(0, isdigit('a'));
|
||||
EXPECT_EQ(2, InterceptorFunctionCalled);
|
||||
|
||||
// Calling the REAL function should not affect the counter.
|
||||
InterceptorFunctionCalled = 0;
|
||||
EXPECT_NE(0, REAL(isdigit)('1'));
|
||||
EXPECT_EQ(0, REAL(isdigit)('a'));
|
||||
EXPECT_EQ(0, InterceptorFunctionCalled);
|
||||
}
|
||||
|
||||
} // namespace __interception
|
||||
|
||||
#endif // SANITIZER_LINUX
|
||||
#endif // #if !SANITIZER_DEBUG
|
||||
22
compiler-rt/lib/interception/tests/interception_test_main.cc
Normal file
22
compiler-rt/lib/interception/tests/interception_test_main.cc
Normal file
@@ -0,0 +1,22 @@
|
||||
//===-- interception_test_main.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.
|
||||
//
|
||||
// Testing the machinery for providing replacements/wrappers for system
|
||||
// functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::GTEST_FLAG(death_test_style) = "threadsafe";
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
142
compiler-rt/lib/interception/tests/interception_win_test.cc
Normal file
142
compiler-rt/lib/interception/tests/interception_win_test.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
//===-- interception_win_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 ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Tests for interception_win.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "interception/interception.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// Too slow for debug build
|
||||
#if !SANITIZER_DEBUG
|
||||
#if SANITIZER_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
namespace {
|
||||
|
||||
typedef int (*IdentityFunction)(int);
|
||||
|
||||
#if !SANITIZER_WINDOWS64
|
||||
u8 kIdentityCodeWithPrologue[] = {
|
||||
0x55, // push ebp
|
||||
0x8B, 0xEC, // mov ebp,esp
|
||||
0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8]
|
||||
0x5D, // pop ebp
|
||||
0xC3, // ret
|
||||
};
|
||||
|
||||
u8 kIdentityCodeWithPushPop[] = {
|
||||
0x55, // push ebp
|
||||
0x8B, 0xEC, // mov ebp,esp
|
||||
0x53, // push ebx
|
||||
0x50, // push eax
|
||||
0x58, // pop eax
|
||||
0x8B, 0x45, 0x08, // mov eax,dword ptr [ebp + 8]
|
||||
0x5B, // pop ebx
|
||||
0x5D, // pop ebp
|
||||
0xC3, // ret
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// A buffer holding the dynamically generated code under test.
|
||||
u8* ActiveCode;
|
||||
size_t ActiveCodeLength = 4096;
|
||||
|
||||
bool LoadActiveCode(u8* Code, size_t CodeLength, uptr* EntryPoint) {
|
||||
if (ActiveCode == nullptr) {
|
||||
ActiveCode =
|
||||
(u8*)::VirtualAlloc(nullptr, ActiveCodeLength, MEM_COMMIT | MEM_RESERVE,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
if (ActiveCode == nullptr) return false;
|
||||
}
|
||||
|
||||
size_t Position = 0;
|
||||
*EntryPoint = (uptr)&ActiveCode[0];
|
||||
|
||||
// Copy the function body.
|
||||
for (size_t i = 0; i < CodeLength; ++i)
|
||||
ActiveCode[Position++] = Code[i];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int InterceptorFunctionCalled;
|
||||
|
||||
NOINLINE int InterceptorFunction(int x) {
|
||||
++InterceptorFunctionCalled;
|
||||
return x;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace __interception {
|
||||
|
||||
// Tests for interception_win.h
|
||||
TEST(Interception, InternalGetProcAddress) {
|
||||
HMODULE ntdll_handle = ::GetModuleHandle("ntdll");
|
||||
ASSERT_NE(nullptr, ntdll_handle);
|
||||
uptr DbgPrint_expected = (uptr)::GetProcAddress(ntdll_handle, "DbgPrint");
|
||||
uptr isdigit_expected = (uptr)::GetProcAddress(ntdll_handle, "isdigit");
|
||||
uptr DbgPrint_adddress = InternalGetProcAddress(ntdll_handle, "DbgPrint");
|
||||
uptr isdigit_address = InternalGetProcAddress(ntdll_handle, "isdigit");
|
||||
|
||||
EXPECT_EQ(DbgPrint_expected, DbgPrint_adddress);
|
||||
EXPECT_EQ(isdigit_expected, isdigit_address);
|
||||
EXPECT_NE(DbgPrint_adddress, isdigit_address);
|
||||
}
|
||||
|
||||
void TestIdentityFunctionPatching(u8* IdentityCode, size_t IdentityCodeLength) {
|
||||
uptr IdentityAddress;
|
||||
ASSERT_TRUE(
|
||||
LoadActiveCode(IdentityCode, IdentityCodeLength, &IdentityAddress));
|
||||
IdentityFunction Identity = (IdentityFunction)IdentityAddress;
|
||||
|
||||
// Validate behavior before dynamic patching.
|
||||
InterceptorFunctionCalled = 0;
|
||||
EXPECT_EQ(0, Identity(0));
|
||||
EXPECT_EQ(42, Identity(42));
|
||||
EXPECT_EQ(0, InterceptorFunctionCalled);
|
||||
|
||||
// Patch the function.
|
||||
uptr RealIdentityAddress = 0;
|
||||
EXPECT_TRUE(OverrideFunction(IdentityAddress, (uptr)&InterceptorFunction,
|
||||
&RealIdentityAddress));
|
||||
IdentityFunction RealIdentity = (IdentityFunction)RealIdentityAddress;
|
||||
|
||||
// Calling the redirected function.
|
||||
InterceptorFunctionCalled = 0;
|
||||
EXPECT_EQ(0, Identity(0));
|
||||
EXPECT_EQ(42, Identity(42));
|
||||
EXPECT_EQ(2, InterceptorFunctionCalled);
|
||||
|
||||
// Calling the real function.
|
||||
InterceptorFunctionCalled = 0;
|
||||
EXPECT_EQ(0, RealIdentity(0));
|
||||
EXPECT_EQ(42, RealIdentity(42));
|
||||
EXPECT_EQ(0, InterceptorFunctionCalled);
|
||||
}
|
||||
|
||||
#if !SANITIZER_WINDOWS64
|
||||
TEST(Interception, OverrideFunction) {
|
||||
TestIdentityFunctionPatching(kIdentityCodeWithPrologue,
|
||||
sizeof(kIdentityCodeWithPrologue));
|
||||
TestIdentityFunctionPatching(kIdentityCodeWithPushPop,
|
||||
sizeof(kIdentityCodeWithPushPop));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace __interception
|
||||
|
||||
#endif // SANITIZER_WINDOWS
|
||||
#endif // #if !SANITIZER_DEBUG
|
||||
Reference in New Issue
Block a user