AddressSanitizer: start factoring out interception machinery
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@150083 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/interception/Makefile.mk b/lib/asan/interception/Makefile.mk
new file mode 100644
index 0000000..f8071a8
--- /dev/null
+++ b/lib/asan/interception/Makefile.mk
@@ -0,0 +1,22 @@
+#===- lib/asan/interception/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 := asan
+SubDirs :=
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
+ObjNames := $(Sources:%.cc=%.o)
+
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
+
+# Define a convenience variable for all the asan functions.
+AsanFunctions += $(Sources:%.cc=%)
diff --git a/lib/asan/interception/interception.h b/lib/asan/interception/interception.h
new file mode 100644
index 0000000..1400885
--- /dev/null
+++ b/lib/asan/interception/interception.h
@@ -0,0 +1,136 @@
+//===-- interception.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.
+//
+// Machinery for providing replacements/wrappers for system functions.
+//===----------------------------------------------------------------------===//
+
+#ifndef INTERCEPTION_H
+#define INTERCEPTION_H
+
+#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
+# error "Interception doesn't work on this operating system."
+#endif
+
+// How to use this library:
+// 1) Include this header to define your own interceptors
+// (see details below).
+// 2) Build all *.cc files and link against them.
+// On Mac you will also need to:
+// 3) Provide your own implementation for the following functions:
+// mach_error_t __interception::allocate_island(void **ptr,
+// size_t size,
+// void *hint);
+// mach_error_t __interception::deallocate_island(void *ptr);
+// See "interception_mac.h" for more details.
+
+// How to add an interceptor:
+// Suppose you need to wrap/replace system function (generally, from libc):
+// int foo(const char *bar, double baz);
+// You'll need to:
+// 1) define INTERCEPTOR(int, foo, const char *bar, double baz) { ... } in
+// your source file.
+// 2) Call "INTERCEPT_FUNCTION(foo)" prior to the first call of "foo".
+// INTERCEPT_FUNCTION(foo) evaluates to "true" iff the function was
+// intercepted successfully.
+// You can access original function by calling REAL(foo)(bar, baz).
+// By default, REAL(foo) will be visible only inside your interceptor, and if
+// you want to use it in other parts of RTL, you'll need to:
+// 3a) add DECLARE_REAL(int, foo, const char*, double); to a
+// header file.
+// However, if the call "INTERCEPT_FUNCTION(foo)" and definition for
+// INTERCEPTOR(..., foo, ...) are in different files, you'll instead need to:
+// 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double);
+// to a header file.
+
+// Notes: 1. Things may not work properly if macro INTERCEPT(...) {...} or
+// DECLARE_REAL(...); are located inside namespaces.
+// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo);" to
+// effectively redirect calls from "foo" to "zoo". In this case
+// you aren't required to implement
+// INTERCEPTOR(int, foo, const char *bar, double baz);
+// but instead you'll have to add
+// DEFINE_REAL(int, foo, const char *bar, double baz); in your
+// source file (to define a pointer to overriden function).
+
+// How it works:
+// To replace weak system functions on Linux we just need to declare functions
+// with same names in our library and then obtain the real function pointers
+// using dlsym(). This is not so on Mac OS, where the two-level namespace makes
+// our replacement functions invisible to other libraries. This may be overcomed
+// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
+// libraries in Chromium were noticed when doing so.
+// Instead we use mach_override, a handy framework for patching functions at
+// runtime. To avoid possible name clashes, our replacement functions have
+// the "wrap_" prefix on Mac.
+
+#if defined(__APPLE__)
+# define WRAP(x) wrap_##x
+# define WRAPPER_NAME(x) "wrap_"#x
+# define INTERCEPTOR_ATTRIBUTE
+#elif defined(_WIN32)
+// TODO(timurrrr): we're likely to use something else later on Windows.
+# define WRAP(x) wrap_##x
+# define WRAPPER_NAME(x) #x
+# define INTERCEPTOR_ATTRIBUTE
+#else
+# define WRAP(x) x
+# define WRAPPER_NAME(x) #x
+# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
+#endif
+
+#define PTR_TO_REAL(x) real_##x
+#define REAL(x) __interception::PTR_TO_REAL(x)
+#define FUNC_TYPE(x) x##_f
+
+#define DECLARE_REAL(ret_type, func, ...); \
+ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
+ namespace __interception { \
+ extern FUNC_TYPE(func) PTR_TO_REAL(func); \
+ }
+
+#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...); \
+ DECLARE_REAL(ret_type, func, ##__VA_ARGS__); \
+ extern "C" ret_type WRAP(func)(__VA_ARGS__);
+
+// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR
+// macros does its job. In exceptional cases you may need to call REAL(foo)
+// without defining INTERCEPTOR(..., foo, ...). For example, if you override
+// foo with an interceptor for other function.
+#define DEFINE_REAL(ret_type, func, ...); \
+ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
+ namespace __interception { \
+ FUNC_TYPE(func) PTR_TO_REAL(func); \
+ }
+
+#define INTERCEPTOR(ret_type, func, ...) \
+ DEFINE_REAL(ret_type, func, __VA_ARGS__); \
+ extern "C" \
+ INTERCEPTOR_ATTRIBUTE \
+ ret_type WRAP(func)(__VA_ARGS__)
+
+#define INCLUDED_FROM_INTERCEPTION_LIB
+
+#if defined(__linux__)
+# include "interception_linux.h"
+# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func)
+#elif defined(__APPLE__)
+# include "interception_mac.h"
+# define OVERRIDE_FUNCTION(old_func, new_func) \
+ OVERRIDE_FUNCTION_MAC(old_func, new_func)
+# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
+#else // defined(_WIN32)
+ // FIXME: deal with interception on Win.
+# define INTERCEPT_FUNCTON(func) true
+#endif
+
+#undef INCLUDED_FROM_INTERCEPTION_LIB
+
+#endif // INTERCEPTION_H
diff --git a/lib/asan/interception/interception_linux.cc b/lib/asan/interception/interception_linux.cc
new file mode 100644
index 0000000..db43e81
--- /dev/null
+++ b/lib/asan/interception/interception_linux.cc
@@ -0,0 +1,27 @@
+//===-- interception_linux.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.
+//
+// Linux-specific interception methods.
+//===----------------------------------------------------------------------===//
+
+#ifdef __linux__
+
+#include <dlfcn.h> // for dlsym
+
+namespace __interception {
+bool GetRealFunctionAddress(const char *func_name, void **func_addr) {
+ *func_addr = dlsym(RTLD_NEXT, func_name);
+ return (*func_addr != NULL);
+}
+} // namespace __interception
+
+
+#endif // __linux__
diff --git a/lib/asan/interception/interception_linux.h b/lib/asan/interception/interception_linux.h
new file mode 100644
index 0000000..214e683
--- /dev/null
+++ b/lib/asan/interception/interception_linux.h
@@ -0,0 +1,33 @@
+//===-- interception_linux.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.
+//
+// Linux-specific interception methods.
+//===----------------------------------------------------------------------===//
+
+#ifdef __linux__
+
+#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
+# error "interception_mac.h should be included from interception library only"
+#endif
+
+#ifndef INTERCEPTION_LINUX_H
+#define INTERCEPTION_LINUX_H
+
+namespace __interception {
+// returns true if a function with the given name was found.
+bool GetRealFunctionAddress(const char *func_name, void **func_addr);
+} // namespace __interception
+
+#define INTERCEPT_FUNCTION_LINUX(func) \
+ ::__interception::GetRealFunctionAddress(#func, (void**)&REAL(func))
+
+#endif // INTERCEPTION_LINUX_H
+#endif // __linux__
diff --git a/lib/asan/interception/interception_mac.cc b/lib/asan/interception/interception_mac.cc
new file mode 100644
index 0000000..7c63dd3
--- /dev/null
+++ b/lib/asan/interception/interception_mac.cc
@@ -0,0 +1,34 @@
+//===-- interception_mac.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.
+//
+// Mac-specific interception methods.
+//===----------------------------------------------------------------------===//
+
+#ifdef __APPLE__
+
+#define INCLUDED_FROM_INTERCEPTION_LIB
+#include "interception_mac.h"
+#undef INCLUDED_FROM_INTERCEPTION_LIB
+// FIXME(samsonov): Put mach_override/ under interception/
+#include "../mach_override/mach_override.h"
+
+namespace __interception {
+bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func) {
+ *orig_old_func = NULL;
+ int res = __asan_mach_override_ptr_custom(old_func, new_func,
+ orig_old_func,
+ __interception_allocate_island,
+ __interception_deallocate_island);
+ return (res == 0) && (*orig_old_func != NULL);
+}
+} // namespace __interception
+
+#endif // __APPLE__
diff --git a/lib/asan/interception/interception_mac.h b/lib/asan/interception/interception_mac.h
new file mode 100644
index 0000000..224d961
--- /dev/null
+++ b/lib/asan/interception/interception_mac.h
@@ -0,0 +1,47 @@
+//===-- interception_mac.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.
+//
+// Mac-specific interception methods.
+//===----------------------------------------------------------------------===//
+
+#ifdef __APPLE__
+
+#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
+# error "interception_mac.h should be included from interception.h only"
+#endif
+
+#ifndef INTERCEPTION_MAC_H
+#define INTERCEPTION_MAC_H
+
+#include <mach/mach_error.h>
+#include <stddef.h>
+
+// Allocate memory for the escape island. This cannot be moved to
+// mach_override, because each user of interceptors may specify its
+// own memory range for escape islands.
+extern "C" {
+mach_error_t __interception_allocate_island(void **ptr, size_t unused_size,
+ void *unused_hint);
+mach_error_t __interception_deallocate_island(void *ptr);
+} // extern "C"
+
+namespace __interception {
+// returns true if the old function existed.
+bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func);
+} // namespace __interception
+
+# define OVERRIDE_FUNCTION_MAC(old_func, new_func) \
+ ::__interception::OverrideFunction((void*)old_func, (void*)new_func, \
+ (void**)&REAL(old_func))
+# define INTERCEPT_FUNCTION_MAC(func) OVERRIDE_FUNCTION_MAC(func, WRAP(func))
+
+#endif // INTERCEPTION_MAC_H
+#endif // __APPLE__