Deliver lsan library.
authorMaxim Ostapenko <m.ostapenko@samsung.com>
Thu, 1 Sep 2016 08:15:47 +0000 (11:15 +0300)
committerMaxim Ostapenko <m.ostapenko@samsung.com>
Thu, 1 Sep 2016 13:00:23 +0000 (16:00 +0300)
Change-Id: I1ec597755ca5c0f4b0da43c0140fbd6165f27da1
Signed-off-by: Maxim Ostapenko <m.ostapenko@samsung.com>
120 files changed:
Makefile
libparserelf_x86.so [deleted file]
lsan/Makefile [new file with mode: 0644]
lsan/include/interception/interception.h [new file with mode: 0644]
lsan/include/interception/interception_linux.cc [new file with mode: 0644]
lsan/include/interception/interception_linux.h [new file with mode: 0644]
lsan/include/interception/interception_type_test.cc [new file with mode: 0644]
lsan/include/lsan/lsan.h [new file with mode: 0644]
lsan/include/lsan/lsan_allocator.h [new file with mode: 0644]
lsan/include/lsan/lsan_common.h [new file with mode: 0644]
lsan/include/lsan/lsan_flags.inc [new file with mode: 0644]
lsan/include/lsan/lsan_interface.h [new file with mode: 0644]
lsan/include/lsan/lsan_thread.h [new file with mode: 0644]
lsan/include/sanitizer_common/lsan_flags.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_addrhashmap.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_allocator.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_allocator_interface.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_allocator_internal.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_asm.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_atomic.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_atomic_clang.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_atomic_clang_other.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_atomic_clang_x86.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_atomic_msvc.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_bitvector.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_bvgraph.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_common.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_common_interceptors.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_common_interceptors_format.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_common_interceptors_ioctl.inc [new file with mode: 0755]
lsan/include/sanitizer_common/sanitizer_common_syscalls.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_deadlock_detector.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_deadlock_detector_interface.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_flag_parser.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_flags.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_flags.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_freebsd.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_interface_internal.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_internal_defs.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_lfstack.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_libc.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_libignore.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_linux.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_list.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_mac.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_malloc_mac.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_mutex.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_persistent_allocator.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_placement_new.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_platform.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_platform_interceptors.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_platform_limits_posix.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_posix.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_procmaps.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_quarantine.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_report_decorator.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_stackdepot.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_stackdepotbase.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_stacktrace.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_stacktrace_printer.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_stoptheworld.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_suppressions.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_symbolizer.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_symbolizer_internal.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_symbolizer_libbacktrace.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_symbolizer_mac.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_syscall_generic.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_syscall_linux_aarch64.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_syscall_linux_x86_64.inc [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_thread_registry.h [new file with mode: 0644]
lsan/include/sanitizer_common/sanitizer_tls_get_addr.h [new file with mode: 0644]
lsan/src/interception_linux.cc [new file with mode: 0644]
lsan/src/lsan.cc [new file with mode: 0644]
lsan/src/lsan_allocator.cc [new file with mode: 0644]
lsan/src/lsan_common.cc [new file with mode: 0644]
lsan/src/lsan_common_linux.cc [new file with mode: 0644]
lsan/src/lsan_interceptors.cc [new file with mode: 0644]
lsan/src/lsan_thread.cc [new file with mode: 0644]
lsan/src/sanitizer_allocator.cc [new file with mode: 0644]
lsan/src/sanitizer_common.cc [new file with mode: 0644]
lsan/src/sanitizer_common_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_coverage_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_coverage_mapping_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_deadlock_detector1.cc [new file with mode: 0644]
lsan/src/sanitizer_deadlock_detector2.cc [new file with mode: 0644]
lsan/src/sanitizer_flag_parser.cc [new file with mode: 0644]
lsan/src/sanitizer_flags.cc [new file with mode: 0644]
lsan/src/sanitizer_libc.cc [new file with mode: 0644]
lsan/src/sanitizer_libignore.cc [new file with mode: 0644]
lsan/src/sanitizer_linux.cc [new file with mode: 0644]
lsan/src/sanitizer_linux_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_mac.cc [new file with mode: 0644]
lsan/src/sanitizer_persistent_allocator.cc [new file with mode: 0644]
lsan/src/sanitizer_platform_limits_linux.cc [new file with mode: 0644]
lsan/src/sanitizer_platform_limits_posix.cc [new file with mode: 0644]
lsan/src/sanitizer_posix.cc [new file with mode: 0644]
lsan/src/sanitizer_posix_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_printf.cc [new file with mode: 0644]
lsan/src/sanitizer_procmaps_common.cc [new file with mode: 0644]
lsan/src/sanitizer_procmaps_freebsd.cc [new file with mode: 0644]
lsan/src/sanitizer_procmaps_linux.cc [new file with mode: 0644]
lsan/src/sanitizer_procmaps_mac.cc [new file with mode: 0644]
lsan/src/sanitizer_stackdepot.cc [new file with mode: 0644]
lsan/src/sanitizer_stacktrace.cc [new file with mode: 0644]
lsan/src/sanitizer_stacktrace_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_stacktrace_printer.cc [new file with mode: 0644]
lsan/src/sanitizer_stoptheworld_linux_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_suppressions.cc [new file with mode: 0644]
lsan/src/sanitizer_symbolizer.cc [new file with mode: 0644]
lsan/src/sanitizer_symbolizer_libbacktrace.cc [new file with mode: 0644]
lsan/src/sanitizer_symbolizer_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_symbolizer_mac.cc [new file with mode: 0644]
lsan/src/sanitizer_symbolizer_posix_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_symbolizer_win.cc [new file with mode: 0644]
lsan/src/sanitizer_thread_registry.cc [new file with mode: 0644]
lsan/src/sanitizer_tls_get_addr.cc [new file with mode: 0644]
lsan/src/sanitizer_unwind_linux_libcdep.cc [new file with mode: 0644]
lsan/src/sanitizer_win.cc [new file with mode: 0644]
packaging/swap-probe.spec
swap-probe-lsan.manifest [new file with mode: 0644]

index 43a860d..3909f61 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,8 @@ TIZEN_TARGET = da_probe_tizen.so
 DUMMY_TARGET = libdaprobe.so
 PARSE_ELF_LIB_TARGET = libparserelf.so
 PARSE_ELF_BIN_TARGET = parse_elf
+LSAN_DIR = lsan
+LSAN_TARGET = da_swap_lsan.so
 
 ## Since include directives do not impose additional dependencies, we can make
 ## Makefile more clear, simply putting all includes we ever need in single
@@ -163,6 +165,8 @@ tizen:              headers $(TIZEN_TARGET)
 dummy:         headers $(DUMMY_TARGET)
 elflib:                $(PARSE_ELF_LIB_OBJ) $(PARSE_ELF_LIB_TARGET)
 elfparser:     elflib $(PARSE_ELF_BIN_OBJ) $(PARSE_ELF_BIN_TARGET)
+lsan:
+        $(MAKE) -C lsan
 
 $(ASM_OBJ): $(ASM_SRC)
        $(CC) $(ASMFLAG) -c $^ -o $@
@@ -216,13 +220,12 @@ $(PARSE_ELF_BIN_TARGET): $(PARSE_ELF_BIN_OBJ) $(PARSE_ELF_LIB_TARGET)
 
 ldheader:      $(SOURCE_HEADERS)
 
-install: install_da install_ld install_elf
+install: install_da install_ld install_elf install_lsan
 
 install_da: all
        [ -d "$(DESTDIR)/$(INSTALLDIR)" ] || mkdir -p $(DESTDIR)/$(INSTALLDIR)
        install $(TIZEN_TARGET) $(DUMMY_TARGET) $(DESTDIR)/$(INSTALLDIR)/
 
-
 install_ld: ldheader # var_addr
        install -m 644 include/ld_preload_probes.h $(DESTDIR)/$(HEADER_INSTALLDIR)/ld_preload_probes.h
        install -m 644 include/ld_preload_types.h $(DESTDIR)/$(HEADER_INSTALLDIR)/ld_preload_types.h
@@ -235,8 +238,11 @@ install_elf: elflib elfparser
        install $(PARSE_ELF_BIN_TARGET) $(DESTDIR)/$(BIN_INSTALLDIR)/parse_elf
        install -m 644 elf_parsing/parse_elf.h $(DESTDIR)/$(HEADER_INSTALLDIR)/parse_elf.h
 
+install_lsan: lsan
+       [ -d "$(DESTDIR)/$(INSTALLDIR)" ] || mkdir -p $(DESTDIR)/$(INSTALLDIR)
+       install $(LSAN_DIR)/$(LSAN_TARGET) $(DESTDIR)/$(INSTALLDIR)/
 
 clean:
        rm -f *.so $(TIZEN_OBJS) $(PARSE_ELF_LIB_OBJ) $(PARSE_ELF_BIN_OBJ) $(GENERATED_HEADERS) $(API_NAME_LIST) $(SOURCE_HEADERS)
 
-.PHONY: all capi tizen dummy clean install_ld install_da install_elf install headers
+.PHONY: all capi tizen dummy clean install_ld install_da install_elf install headers lsan
diff --git a/libparserelf_x86.so b/libparserelf_x86.so
deleted file mode 100755 (executable)
index 6284167..0000000
Binary files a/libparserelf_x86.so and /dev/null differ
diff --git a/lsan/Makefile b/lsan/Makefile
new file mode 100644 (file)
index 0000000..d1f1ab9
--- /dev/null
@@ -0,0 +1,31 @@
+INCLUDE = -Iinclude -Iinclude/lsan/ -Iinclude/sanitizer_common
+
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+CXXFLAGS = -O2 -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -std=gnu++11 -m32
+
+SRC_DIR := src
+SOURCES := $(wildcard $(SRC_DIR)/*.cc)
+
+CFLAGS = $(CXXFLAGS) \
+       $(INCLUDE) \
+       $(DEFS)
+
+LDFLAGS= -ldl -pthread -lrt -shared -m32
+
+TARGET = da_swap_lsan.so
+
+all: lsan
+
+OBJS := $(SOURCES:.cc=.o)
+
+.cc.o:
+       $(CC) $(CFLAGS) -c -o $@ $<
+
+lsan:  $(OBJS)
+       $(CC) $(OBJS) -o $(TARGET) $(LDFLAGS)
+       rm -f src/*.o
+
+clean:
+       rm -f src/*.o *.so
+
+.PHONY: lsan
diff --git a/lsan/include/interception/interception.h b/lsan/include/interception/interception.h
new file mode 100644 (file)
index 0000000..20b162d
--- /dev/null
@@ -0,0 +1,259 @@
+//===-- interception.h ------------------------------------------*- C++ -*-===//
+//
+// 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(__FreeBSD__) && \
+  !defined(__APPLE__) && !defined(_WIN32)
+# error "Interception doesn't work on this operating system."
+#endif
+
+#include "sanitizer_internal_defs.h"
+
+// These typedefs should be used only in the interceptor definitions to replace
+// the standard system types (e.g. SSIZE_T instead of ssize_t)
+typedef __sanitizer::uptr    SIZE_T;
+typedef __sanitizer::sptr    SSIZE_T;
+typedef __sanitizer::sptr    PTRDIFF_T;
+typedef __sanitizer::s64     INTMAX_T;
+typedef __sanitizer::OFF_T   OFF_T;
+typedef __sanitizer::OFF64_T OFF64_T;
+
+// 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. See the notes below for cases when
+//         INTERCEPTOR_WITH_SUFFIX(...) should be used instead.
+//      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 INTERCEPTOR(...) {...} 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
+//           DECLARE_REAL(int, foo, const char *bar, double baz) in your
+//           source file (to define a pointer to overriden function).
+//        3. Some Mac functions have symbol variants discriminated by
+//           additional suffixes, e.g. _$UNIX2003 (see
+//           https://developer.apple.com/library/mac/#releasenotes/Darwin/SymbolVariantsRelNotes/index.html
+//           for more details). To intercept such functions you need to use the
+//           INTERCEPTOR_WITH_SUFFIX(...) macro.
+
+// How it works:
+// To replace 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().
+// There is one complication. A user may also intercept some of the functions
+// we intercept. To resolve this we declare our interceptors with __interceptor_
+// prefix, and then make actual interceptors weak aliases to __interceptor_
+// functions.
+//
+// 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 create a dylib containing a __DATA,__interpose section that
+// associates library functions with their wrappers. When this dylib is
+// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all
+// the calls to interposed functions done through stubs to the wrapper
+// functions.
+// As it's decided at compile time which functions are to be intercepted on Mac,
+// INTERCEPT_FUNCTION() is effectively a no-op on this system.
+
+#if defined(__APPLE__)
+#include <sys/cdefs.h>  // For __DARWIN_ALIAS_C().
+
+// Just a pair of pointers.
+struct interpose_substitution {
+  const uptr replacement;
+  const uptr original;
+};
+
+// For a function foo() create a global pair of pointers { wrap_foo, foo } in
+// the __DATA,__interpose section.
+// As a result all the calls to foo() will be routed to wrap_foo() at runtime.
+#define INTERPOSER(func_name) __attribute__((used)) \
+const interpose_substitution substitution_##func_name[] \
+    __attribute__((section("__DATA, __interpose"))) = { \
+    { reinterpret_cast<const uptr>(WRAP(func_name)), \
+      reinterpret_cast<const uptr>(func_name) } \
+}
+
+// For a function foo() and a wrapper function bar() create a global pair
+// of pointers { bar, foo } in the __DATA,__interpose section.
+// As a result all the calls to foo() will be routed to bar() at runtime.
+#define INTERPOSER_2(func_name, wrapper_name) __attribute__((used)) \
+const interpose_substitution substitution_##func_name[] \
+    __attribute__((section("__DATA, __interpose"))) = { \
+    { reinterpret_cast<const uptr>(wrapper_name), \
+      reinterpret_cast<const uptr>(func_name) } \
+}
+
+# define WRAP(x) wrap_##x
+# define WRAPPER_NAME(x) "wrap_"#x
+# define INTERCEPTOR_ATTRIBUTE
+# define DECLARE_WRAPPER(ret_type, func, ...)
+
+#elif defined(_WIN32)
+# define WRAP(x) __asan_wrap_##x
+# define WRAPPER_NAME(x) "__asan_wrap_"#x
+# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
+# define DECLARE_WRAPPER(ret_type, func, ...) \
+    extern "C" ret_type func(__VA_ARGS__);
+# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
+    extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
+#elif defined(__FreeBSD__)
+# define WRAP(x) __interceptor_ ## x
+# define WRAPPER_NAME(x) "__interceptor_" #x
+# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
+// FreeBSD's dynamic linker (incompliantly) gives non-weak symbols higher
+// priority than weak ones so weak aliases won't work for indirect calls
+// in position-independent (-fPIC / -fPIE) mode.
+# define DECLARE_WRAPPER(ret_type, func, ...) \
+     extern "C" ret_type func(__VA_ARGS__) \
+     __attribute__((alias("__interceptor_" #func), visibility("default")));
+#else
+# define WRAP(x) __interceptor_ ## x
+# define WRAPPER_NAME(x) "__interceptor_" #x
+# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
+# define DECLARE_WRAPPER(ret_type, func, ...) \
+    extern "C" ret_type func(__VA_ARGS__) \
+    __attribute__((weak, alias("__interceptor_" #func), visibility("default")));
+#endif
+
+#if !defined(__APPLE__)
+# 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); \
+    }
+#else  // __APPLE__
+# define REAL(x) x
+# define DECLARE_REAL(ret_type, func, ...) \
+    extern "C" ret_type func(__VA_ARGS__);
+#endif  // __APPLE__
+
+#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.
+#if !defined(__APPLE__)
+# define DEFINE_REAL(ret_type, func, ...) \
+    typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
+    namespace __interception { \
+      FUNC_TYPE(func) PTR_TO_REAL(func); \
+    }
+#else
+# define DEFINE_REAL(ret_type, func, ...)
+#endif
+
+#if !defined(__APPLE__)
+#define INTERCEPTOR(ret_type, func, ...) \
+  DEFINE_REAL(ret_type, func, __VA_ARGS__) \
+  DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \
+  extern "C" \
+  INTERCEPTOR_ATTRIBUTE \
+  ret_type WRAP(func)(__VA_ARGS__)
+
+// We don't need INTERCEPTOR_WITH_SUFFIX on non-Darwin for now.
+#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \
+  INTERCEPTOR(ret_type, func, __VA_ARGS__)
+
+#else  // __APPLE__
+
+#define INTERCEPTOR_ZZZ(suffix, ret_type, func, ...) \
+  extern "C" ret_type func(__VA_ARGS__) suffix; \
+  extern "C" ret_type WRAP(func)(__VA_ARGS__); \
+  INTERPOSER(func); \
+  extern "C" INTERCEPTOR_ATTRIBUTE ret_type WRAP(func)(__VA_ARGS__)
+
+#define INTERCEPTOR(ret_type, func, ...) \
+  INTERCEPTOR_ZZZ(/*no symbol variants*/, ret_type, func, __VA_ARGS__)
+
+#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \
+  INTERCEPTOR_ZZZ(__DARWIN_ALIAS_C(func), ret_type, func, __VA_ARGS__)
+
+// Override |overridee| with |overrider|.
+#define OVERRIDE_FUNCTION(overridee, overrider) \
+  INTERPOSER_2(overridee, WRAP(overrider))
+#endif
+
+#if defined(_WIN32)
+# define INTERCEPTOR_WINAPI(ret_type, func, ...) \
+    typedef ret_type (__stdcall *FUNC_TYPE(func))(__VA_ARGS__); \
+    namespace __interception { \
+      FUNC_TYPE(func) PTR_TO_REAL(func); \
+    } \
+    extern "C" \
+    INTERCEPTOR_ATTRIBUTE \
+    ret_type __stdcall WRAP(func)(__VA_ARGS__)
+#endif
+
+// ISO C++ forbids casting between pointer-to-function and pointer-to-object,
+// so we use casting via an integral type __interception::uptr,
+// assuming that system is POSIX-compliant. Using other hacks seem
+// challenging, as we don't even pass function type to
+// INTERCEPT_FUNCTION macro, only its name.
+namespace __interception {
+#if defined(_WIN64)
+typedef unsigned long long uptr;  // NOLINT
+#else
+typedef unsigned long uptr;  // NOLINT
+#endif  // _WIN64
+}  // namespace __interception
+
+#define INCLUDED_FROM_INTERCEPTION_LIB
+
+#if defined(__linux__) || defined(__FreeBSD__)
+# include "interception_linux.h"
+# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
+# define INTERCEPT_FUNCTION_VER(func, symver) \
+    INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver)
+#elif defined(__APPLE__)
+# include "interception_mac.h"
+# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
+# define INTERCEPT_FUNCTION_VER(func, symver) \
+    INTERCEPT_FUNCTION_VER_MAC(func, symver)
+#else  // defined(_WIN32)
+# include "interception_win.h"
+# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func)
+# define INTERCEPT_FUNCTION_VER(func, symver) \
+    INTERCEPT_FUNCTION_VER_WIN(func, symver)
+#endif
+
+#undef INCLUDED_FROM_INTERCEPTION_LIB
+
+#endif  // INTERCEPTION_H
diff --git a/lsan/include/interception/interception_linux.cc b/lsan/include/interception/interception_linux.cc
new file mode 100644 (file)
index 0000000..0a8305b
--- /dev/null
@@ -0,0 +1,34 @@
+//===-- interception_linux.cc -----------------------------------*- C++ -*-===//
+//
+// 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.
+//===----------------------------------------------------------------------===//
+
+#if defined(__linux__) || defined(__FreeBSD__)
+#include "interception.h"
+
+#include <dlfcn.h>   // for dlsym() and dlvsym()
+
+namespace __interception {
+bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,
+    uptr real, uptr wrapper) {
+  *func_addr = (uptr)dlsym(RTLD_NEXT, func_name);
+  return real == wrapper;
+}
+
+#if !defined(__ANDROID__)  // android does not have dlvsym
+void *GetFuncAddrVer(const char *func_name, const char *ver) {
+  return dlvsym(RTLD_NEXT, func_name, ver);
+}
+#endif  // !defined(__ANDROID__)
+
+}  // namespace __interception
+
+
+#endif  // __linux__ || __FreeBSD__
diff --git a/lsan/include/interception/interception_linux.h b/lsan/include/interception/interception_linux.h
new file mode 100644 (file)
index 0000000..61bf48a
--- /dev/null
@@ -0,0 +1,45 @@
+//===-- interception_linux.h ------------------------------------*- C++ -*-===//
+//
+// 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.
+//===----------------------------------------------------------------------===//
+
+#if defined(__linux__) || defined(__FreeBSD__)
+
+#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
+# error "interception_linux.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, uptr *func_addr,
+    uptr real, uptr wrapper);
+void *GetFuncAddrVer(const char *func_name, const char *ver);
+}  // namespace __interception
+
+#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)                          \
+  ::__interception::GetRealFunctionAddress(                                \
+      #func, (::__interception::uptr *)&__interception::PTR_TO_REAL(func), \
+      (::__interception::uptr) & (func),                                   \
+      (::__interception::uptr) & WRAP(func))
+
+#if !defined(__ANDROID__)  // android does not have dlvsym
+#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
+  (::__interception::real_##func = (func##_f)(                \
+       unsigned long)::__interception::GetFuncAddrVer(#func, symver))
+#else
+#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
+  INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
+#endif  // !defined(__ANDROID__)
+
+#endif  // INTERCEPTION_LINUX_H
+#endif  // __linux__ || __FreeBSD__
diff --git a/lsan/include/interception/interception_type_test.cc b/lsan/include/interception/interception_type_test.cc
new file mode 100644 (file)
index 0000000..6ba45e2
--- /dev/null
@@ -0,0 +1,37 @@
+//===-- interception_type_test.cc -------------------------------*- C++ -*-===//
+//
+// 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.
+//
+// Compile-time tests of the internal type definitions.
+//===----------------------------------------------------------------------===//
+
+#if defined(__linux__) || defined(__APPLE__)
+
+#include "interception.h"
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdint.h>
+
+COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t));
+COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t));
+COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t));
+COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t));
+
+#ifndef __APPLE__
+COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t));
+#endif
+
+// The following are the cases when pread (and friends) is used instead of
+// pread64. In those cases we need OFF_T to match off_t. We don't care about the
+// rest (they depend on _FILE_OFFSET_BITS setting when building an application).
+# if defined(__ANDROID__) || !defined _FILE_OFFSET_BITS || \
+  _FILE_OFFSET_BITS != 64
+COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t));
+# endif
+
+#endif
diff --git a/lsan/include/lsan/lsan.h b/lsan/include/lsan/lsan.h
new file mode 100644 (file)
index 0000000..30f06ca
--- /dev/null
@@ -0,0 +1,53 @@
+//=-- lsan.h --------------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Private header for standalone LSan RTL.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+
+#define GET_STACK_TRACE(max_size, fast)                                        \
+  BufferedStackTrace stack;                                                    \
+  {                                                                            \
+    uptr stack_top = 0, stack_bottom = 0;                                      \
+    ThreadContext *t;                                                          \
+    if (fast && (t = CurrentThreadContext())) {                                \
+      stack_top = t->stack_end();                                              \
+      stack_bottom = t->stack_begin();                                         \
+    }                                                                          \
+    stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(),    \
+                 /* context */ 0, stack_top, stack_bottom, fast);              \
+  }
+
+#define GET_STACK_TRACE_FATAL \
+  GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_MALLOC                                      \
+  GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
+                  common_flags()->fast_unwind_on_malloc)
+
+#ifndef LSAN_DYNAMIC
+# ifdef PIC
+#  define LSAN_DYNAMIC 1
+# else
+#  define LSAN_DYNAMIC 0
+# endif
+#endif
+
+namespace __lsan {
+
+void InitializeInterceptors();
+
+}  // namespace __lsan
+
+extern bool lsan_inited;
+extern bool lsan_init_is_running;
+
+extern "C" void __lsan_init();
diff --git a/lsan/include/lsan/lsan_allocator.h b/lsan/include/lsan/lsan_allocator.h
new file mode 100644 (file)
index 0000000..58e4074
--- /dev/null
@@ -0,0 +1,39 @@
+//=-- lsan_allocator.h ----------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Allocator for standalone LSan.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LSAN_ALLOCATOR_H
+#define LSAN_ALLOCATOR_H
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __lsan {
+
+void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
+               bool cleared);
+void Deallocate(void *p);
+void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
+                 uptr alignment);
+uptr GetMallocUsableSize(const void *p);
+
+int PointerIsMine(void *p);
+
+template<typename Callable>
+void ForEachChunk(const Callable &callback);
+
+void GetAllocatorCacheRange(uptr *begin, uptr *end);
+void AllocatorThreadFinish();
+void InitializeAllocator();
+
+}  // namespace __lsan
+
+#endif  // LSAN_ALLOCATOR_H
diff --git a/lsan/include/lsan/lsan_common.h b/lsan/include/lsan/lsan_common.h
new file mode 100644 (file)
index 0000000..0f07fd0
--- /dev/null
@@ -0,0 +1,190 @@
+//=-- lsan_common.h -------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Private LSan header.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LSAN_COMMON_H
+#define LSAN_COMMON_H
+
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_platform.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+#if (SANITIZER_LINUX && !SANITIZER_ANDROID) && (SANITIZER_WORDSIZE == 64) \
+     && (defined(__x86_64__) ||  defined(__mips64) ||  defined(__aarch64__))
+#define CAN_SANITIZE_LEAKS 1
+#elif defined(__arm__) || defined(__i386__)
+#define CAN_SANITIZE_LEAKS 1
+#else
+#define CAN_SANITIZE_LEAKS 0
+#endif
+
+namespace __sanitizer {
+class FlagParser;
+}
+
+namespace __lsan {
+
+// Chunk tags.
+enum ChunkTag {
+  kDirectlyLeaked = 0,  // default
+  kIndirectlyLeaked = 1,
+  kReachable = 2,
+  kIgnored = 3
+};
+
+struct Flags {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
+
+  void SetDefaults();
+  uptr pointer_alignment() const {
+    return use_unaligned ? 1 : sizeof(uptr);
+  }
+};
+
+extern Flags lsan_flags;
+inline Flags *flags() { return &lsan_flags; }
+void RegisterLsanFlags(FlagParser *parser, Flags *f);
+
+struct Leak {
+  u32 id;
+  uptr hit_count;
+  uptr total_size;
+  u32 stack_trace_id;
+  bool is_directly_leaked;
+  bool is_suppressed;
+};
+
+struct LeakedObject {
+  u32 leak_id;
+  u32 first_four_bytes;
+  uptr addr;
+  uptr size;
+};
+
+// Aggregates leaks by stack trace prefix.
+class LeakReport {
+ public:
+  LeakReport() : next_id_(0), leaks_(1), leaked_objects_(1) {}
+  void AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size,
+                      ChunkTag tag);
+  void ReportTopLeaks(uptr max_leaks);
+  void PrintSummary();
+  void ApplySuppressions();
+  uptr UnsuppressedLeakCount();
+  void Clear();
+  uptr LeakedObjectsNum();
+  const void *GetLeakedObjectsPtr();
+
+
+ private:
+  void PrintReportForLeak(uptr index);
+  void PrintLeakedObjectsForLeak(uptr index);
+
+  u32 next_id_;
+  InternalMmapVector<Leak> leaks_;
+  InternalMmapVector<LeakedObject> leaked_objects_;
+};
+
+typedef InternalMmapVector<uptr> Frontier;
+
+// Platform-specific functions.
+void InitializePlatformSpecificModules();
+void ProcessGlobalRegions(Frontier *frontier);
+void ProcessPlatformSpecificAllocations(Frontier *frontier);
+// Run stoptheworld while holding any platform-specific locks.
+void DoStopTheWorld(StopTheWorldCallback callback, void* argument);
+
+void ScanRangeForPointers(uptr begin, uptr end,
+                          Frontier *frontier,
+                          const char *region_type, ChunkTag tag);
+
+enum IgnoreObjectResult {
+  kIgnoreObjectSuccess,
+  kIgnoreObjectAlreadyIgnored,
+  kIgnoreObjectInvalid
+};
+
+// Functions called from the parent tool.
+void InitCommonLsan();
+void DoLeakCheck();
+bool DisabledInThisThread();
+
+// Special case for "new T[0]" where T is a type with DTOR.
+// new T[0] will allocate one word for the array size (0) and store a pointer
+// to the end of allocated chunk.
+inline bool IsSpecialCaseOfOperatorNew0(uptr chunk_beg, uptr chunk_size,
+                                        uptr addr) {
+  return chunk_size == sizeof(uptr) && chunk_beg + chunk_size == addr &&
+         *reinterpret_cast<uptr *>(chunk_beg) == 0;
+}
+
+// The following must be implemented in the parent tool.
+
+void ForEachChunk(ForEachChunkCallback callback, void *arg);
+// Returns the address range occupied by the global allocator object.
+void GetAllocatorGlobalRange(uptr *begin, uptr *end);
+// Wrappers for allocator's ForceLock()/ForceUnlock().
+void LockAllocator();
+void UnlockAllocator();
+// Returns true if [addr, addr + sizeof(void *)) is poisoned.
+bool WordIsPoisoned(uptr addr);
+// Wrappers for ThreadRegistry access.
+void LockThreadRegistry();
+void UnlockThreadRegistry();
+bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
+                           uptr *tls_begin, uptr *tls_end,
+                           uptr *cache_begin, uptr *cache_end);
+void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
+                            void *arg);
+// If called from the main thread, updates the main thread's TID in the thread
+// registry. We need this to handle processes that fork() without a subsequent
+// exec(), which invalidates the recorded TID. To update it, we must call
+// gettid() from the main thread. Our solution is to call this function before
+// leak checking and also before every call to pthread_create() (to handle cases
+// where leak checking is initiated from a non-main thread).
+void EnsureMainThreadIDIsCorrect();
+// If p points into a chunk that has been allocated to the user, returns its
+// user-visible address. Otherwise, returns 0.
+uptr PointsIntoChunk(void *p);
+// Returns address of user-visible chunk contained in this allocator chunk.
+uptr GetUserBegin(uptr chunk);
+// Helper for __lsan_ignore_object().
+IgnoreObjectResult IgnoreObjectLocked(const void *p);
+// Wrapper for chunk metadata operations.
+class LsanMetadata {
+ public:
+  // Constructor accepts address of user-visible chunk.
+  explicit LsanMetadata(uptr chunk);
+  bool allocated() const;
+  ChunkTag tag() const;
+  void set_tag(ChunkTag value);
+  uptr requested_size() const;
+  u32 stack_trace_id() const;
+ private:
+  void *metadata_;
+};
+
+}  // namespace __lsan
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+int __lsan_is_turned_off();
+
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__lsan_default_suppressions();
+}  // extern "C"
+
+#endif  // LSAN_COMMON_H
diff --git a/lsan/include/lsan/lsan_flags.inc b/lsan/include/lsan/lsan_flags.inc
new file mode 100644 (file)
index 0000000..1f0e5c6
--- /dev/null
@@ -0,0 +1,41 @@
+//===-- lsan_flags.inc ------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// LSan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LSAN_FLAG
+# error "Define LSAN_FLAG prior to including this file!"
+#endif
+
+// LSAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+LSAN_FLAG(bool, report_objects, true,
+          "Print addresses of leaked objects after main leak report.")
+LSAN_FLAG(
+    int, resolution, 0,
+    "Aggregate two objects into one leak if this many stack frames match. If "
+    "zero, the entire stack trace must match.")
+LSAN_FLAG(int, max_leaks, 0, "The number of leaks reported.")
+
+// Flags controlling the root set of reachable memory.
+LSAN_FLAG(bool, use_globals, true,
+          "Root set: include global variables (.data and .bss)")
+LSAN_FLAG(bool, use_stacks, true, "Root set: include thread stacks")
+LSAN_FLAG(bool, use_registers, true, "Root set: include thread registers")
+LSAN_FLAG(bool, use_tls, true,
+          "Root set: include TLS and thread-specific storage")
+LSAN_FLAG(bool, use_root_regions, true,
+          "Root set: include regions added via __lsan_register_root_region().")
+
+LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.")
+LSAN_FLAG(bool, use_poisoned, false,
+          "Consider pointers found in poisoned memory to be valid.")
+LSAN_FLAG(bool, log_pointers, false, "Debug logging")
+LSAN_FLAG(bool, log_threads, false, "Debug logging")
+LSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
diff --git a/lsan/include/lsan/lsan_interface.h b/lsan/include/lsan/lsan_interface.h
new file mode 100644 (file)
index 0000000..9181afd
--- /dev/null
@@ -0,0 +1,92 @@
+//===-- sanitizer/lsan_interface.h ------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+//
+// Public interface header.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_LSAN_INTERFACE_H
+#define SANITIZER_LSAN_INTERFACE_H
+
+#include <sanitizer/common_interface_defs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  // Allocations made between calls to __lsan_disable() and __lsan_enable() will
+  // be treated as non-leaks. Disable/enable pairs may be nested.
+  void __lsan_disable();
+  void __lsan_enable();
+
+  // The heap object into which p points will be treated as a non-leak.
+  void __lsan_ignore_object(const void *p);
+
+  // Memory regions registered through this interface will be treated as sources
+  // of live pointers during leak checking. Useful if you store pointers in
+  // mapped memory.
+  // Points of note:
+  // - __lsan_unregister_root_region() must be called with the same pointer and
+  // size that have earlier been passed to __lsan_register_root_region()
+  // - LSan will skip any inaccessible memory when scanning a root region. E.g.,
+  // if you map memory within a larger region that you have mprotect'ed, you can
+  // register the entire large region.
+  // - the implementation is not optimized for performance. This interface is
+  // intended to be used for a small number of relatively static regions.
+  void __lsan_register_root_region(const void *p, size_t size);
+  void __lsan_unregister_root_region(const void *p, size_t size);
+
+  // Check for leaks now. This function behaves identically to the default
+  // end-of-process leak check. In particular, it will terminate the process if
+  // leaks are found and the exitcode runtime flag is non-zero.
+  // Subsequent calls to this function will have no effect and end-of-process
+  // leak check will not run. Effectively, end-of-process leak check is moved to
+  // the time of first invocation of this function.
+  // By calling this function early during process shutdown, you can instruct
+  // LSan to ignore shutdown-only leaks which happen later on.
+  void __lsan_do_leak_check();
+
+  // Check for leaks now. Returns zero if no leaks have been found or if leak
+  // detection is disabled, non-zero otherwise.
+  // This function may be called repeatedly, e.g. to periodically check a
+  // long-running process. It prints a leak report if appropriate, but does not
+  // terminate the process. It does not affect the behavior of
+  // __lsan_do_leak_check() or the end-of-process leak check, and is not
+  // affected by them.
+  int __lsan_do_recoverable_leak_check();
+
+  // The user may optionally provide this function to disallow leak checking
+  // for the program it is linked into (if the return value is non-zero). This
+  // function must be defined as returning a constant value; any behavior beyond
+  // that is unsupported.
+  int __lsan_is_turned_off();
+
+  // This function may be optionally provided by the user and should return
+  // a string containing LSan suppressions.
+  const char *__lsan_default_suppressions();
+
+  // The user (e.g. SWAP) may request all leaked chunks information stored into
+  // LeakReport object. This function returns the number of leaked objects in
+  // order to allow user iterate through them safely.
+  int __lsan_get_leaked_objects_num();
+
+  // This is hacky. But user (e.g. SWAP) may want extract information about
+  // leaked objects. Don't even try to modify data, returned by this function.
+  const void *__lsan_get_leaked_objects_ptr();
+
+#ifdef __cplusplus
+}  // extern "C"
+
+namespace __lsan {
+class ScopedDisabler {
+ public:
+  ScopedDisabler() { __lsan_disable(); }
+  ~ScopedDisabler() { __lsan_enable(); }
+};
+}  // namespace __lsan
+#endif
+
+#endif  // SANITIZER_LSAN_INTERFACE_H
diff --git a/lsan/include/lsan/lsan_thread.h b/lsan/include/lsan/lsan_thread.h
new file mode 100644 (file)
index 0000000..2fc4ecd
--- /dev/null
@@ -0,0 +1,52 @@
+//=-- lsan_thread.h -------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Thread registry for standalone LSan.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LSAN_THREAD_H
+#define LSAN_THREAD_H
+
+#include "sanitizer_common/sanitizer_thread_registry.h"
+
+namespace __lsan {
+
+class ThreadContext : public ThreadContextBase {
+ public:
+  explicit ThreadContext(int tid);
+  void OnStarted(void *arg) override;
+  void OnFinished() override;
+  uptr stack_begin() { return stack_begin_; }
+  uptr stack_end() { return stack_end_; }
+  uptr tls_begin() { return tls_begin_; }
+  uptr tls_end() { return tls_end_; }
+  uptr cache_begin() { return cache_begin_; }
+  uptr cache_end() { return cache_end_; }
+ private:
+  uptr stack_begin_, stack_end_,
+       cache_begin_, cache_end_,
+       tls_begin_, tls_end_;
+};
+
+void InitializeThreadRegistry();
+
+void ThreadStart(u32 tid, uptr os_id);
+void ThreadFinish();
+u32 ThreadCreate(u32 tid, uptr uid, bool detached);
+void ThreadJoin(u32 tid);
+u32 ThreadTid(uptr uid);
+
+bool ThreadIsMine(u32 tid);
+u32 GetCurrentThread();
+void SetCurrentThread(u32 tid);
+ThreadContext *CurrentThreadContext();
+void EnsureMainThreadIDIsCorrect();
+}  // namespace __lsan
+
+#endif  // LSAN_THREAD_H
diff --git a/lsan/include/sanitizer_common/lsan_flags.inc b/lsan/include/sanitizer_common/lsan_flags.inc
new file mode 100644 (file)
index 0000000..73a980e
--- /dev/null
@@ -0,0 +1,41 @@
+//===-- lsan_flags.inc ------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// LSan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LSAN_FLAG
+# error "Define LSAN_FLAG prior to including this file!"
+#endif
+
+// LSAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+LSAN_FLAG(bool, report_objects, false,
+          "Print addresses of leaked objects after main leak report.")
+LSAN_FLAG(
+    int, resolution, 0,
+    "Aggregate two objects into one leak if this many stack frames match. If "
+    "zero, the entire stack trace must match.")
+LSAN_FLAG(int, max_leaks, 0, "The number of leaks reported.")
+
+// Flags controlling the root set of reachable memory.
+LSAN_FLAG(bool, use_globals, true,
+          "Root set: include global variables (.data and .bss)")
+LSAN_FLAG(bool, use_stacks, true, "Root set: include thread stacks")
+LSAN_FLAG(bool, use_registers, true, "Root set: include thread registers")
+LSAN_FLAG(bool, use_tls, true,
+          "Root set: include TLS and thread-specific storage")
+LSAN_FLAG(bool, use_root_regions, true,
+          "Root set: include regions added via __lsan_register_root_region().")
+
+LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.")
+LSAN_FLAG(bool, use_poisoned, false,
+          "Consider pointers found in poisoned memory to be valid.")
+LSAN_FLAG(bool, log_pointers, false, "Debug logging")
+LSAN_FLAG(bool, log_threads, false, "Debug logging")
+LSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
diff --git a/lsan/include/sanitizer_common/sanitizer_addrhashmap.h b/lsan/include/sanitizer_common/sanitizer_addrhashmap.h
new file mode 100644 (file)
index 0000000..3bc40ef
--- /dev/null
@@ -0,0 +1,340 @@
+//===-- sanitizer_addrhashmap.h ---------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Concurrent uptr->T hashmap.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ADDRHASHMAP_H
+#define SANITIZER_ADDRHASHMAP_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_allocator_internal.h"
+
+namespace __sanitizer {
+
+// Concurrent uptr->T hashmap.
+// T must be a POD type, kSize is preferably a prime but can be any number.
+// Usage example:
+//
+// typedef AddrHashMap<uptr, 11> Map;
+// Map m;
+// {
+//   Map::Handle h(&m, addr);
+//   use h.operator->() to access the data
+//   if h.created() then the element was just created, and the current thread
+//     has exclusive access to it
+//   otherwise the current thread has only read access to the data
+// }
+// {
+//   Map::Handle h(&m, addr, true);
+//   this will remove the data from the map in Handle dtor
+//   the current thread has exclusive access to the data
+//   if !h.exists() then the element never existed
+// }
+template<typename T, uptr kSize>
+class AddrHashMap {
+ private:
+  struct Cell {
+    atomic_uintptr_t addr;
+    T                val;
+  };
+
+  struct AddBucket {
+    uptr cap;
+    uptr size;
+    Cell cells[1];  // variable len
+  };
+
+  static const uptr kBucketSize = 3;
+
+  struct Bucket {
+    RWMutex          mtx;
+    atomic_uintptr_t add;
+    Cell             cells[kBucketSize];
+  };
+
+ public:
+  AddrHashMap();
+
+  class Handle {
+   public:
+    Handle(AddrHashMap<T, kSize> *map, uptr addr);
+    Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove);
+    Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove, bool create);
+
+    ~Handle();
+    T *operator->();
+    bool created() const;
+    bool exists() const;
+
+   private:
+    friend AddrHashMap<T, kSize>;
+    AddrHashMap<T, kSize> *map_;
+    Bucket                *bucket_;
+    Cell                  *cell_;
+    uptr                   addr_;
+    uptr                   addidx_;
+    bool                   created_;
+    bool                   remove_;
+    bool                   create_;
+  };
+
+ private:
+  friend class Handle;
+  Bucket *table_;
+
+  void acquire(Handle *h);
+  void release(Handle *h);
+  uptr calcHash(uptr addr);
+};
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr) {
+  map_ = map;
+  addr_ = addr;
+  remove_ = false;
+  create_ = true;
+  map_->acquire(this);
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
+    bool remove) {
+  map_ = map;
+  addr_ = addr;
+  remove_ = remove;
+  create_ = true;
+  map_->acquire(this);
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
+    bool remove, bool create) {
+  map_ = map;
+  addr_ = addr;
+  remove_ = remove;
+  create_ = create;
+  map_->acquire(this);
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::~Handle() {
+  map_->release(this);
+}
+
+template <typename T, uptr kSize>
+T *AddrHashMap<T, kSize>::Handle::operator->() {
+  return &cell_->val;
+}
+
+template<typename T, uptr kSize>
+bool AddrHashMap<T, kSize>::Handle::created() const {
+  return created_;
+}
+
+template<typename T, uptr kSize>
+bool AddrHashMap<T, kSize>::Handle::exists() const {
+  return cell_ != nullptr;
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::AddrHashMap() {
+  table_ = (Bucket*)MmapOrDie(kSize * sizeof(table_[0]), "AddrHashMap");
+}
+
+template<typename T, uptr kSize>
+void AddrHashMap<T, kSize>::acquire(Handle *h) {
+  uptr addr = h->addr_;
+  uptr hash = calcHash(addr);
+  Bucket *b = &table_[hash];
+
+  h->created_ = false;
+  h->addidx_ = -1U;
+  h->bucket_ = b;
+  h->cell_ = nullptr;
+
+  // If we want to remove the element, we need exclusive access to the bucket,
+  // so skip the lock-free phase.
+  if (h->remove_)
+    goto locked;
+
+ retry:
+  // First try to find an existing element w/o read mutex.
+  CHECK(!h->remove_);
+  // Check the embed cells.
+  for (uptr i = 0; i < kBucketSize; i++) {
+    Cell *c = &b->cells[i];
+    uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
+    if (addr1 == addr) {
+      h->cell_ = c;
+      return;
+    }
+  }
+
+  // Check the add cells with read lock.
+  if (atomic_load(&b->add, memory_order_relaxed)) {
+    b->mtx.ReadLock();
+    AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
+    for (uptr i = 0; i < add->size; i++) {
+      Cell *c = &add->cells[i];
+      uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+      if (addr1 == addr) {
+        h->addidx_ = i;
+        h->cell_ = c;
+        return;
+      }
+    }
+    b->mtx.ReadUnlock();
+  }
+
+ locked:
+  // Re-check existence under write lock.
+  // Embed cells.
+  b->mtx.Lock();
+  for (uptr i = 0; i < kBucketSize; i++) {
+    Cell *c = &b->cells[i];
+    uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+    if (addr1 == addr) {
+      if (h->remove_) {
+        h->cell_ = c;
+        return;
+      }
+      b->mtx.Unlock();
+      goto retry;
+    }
+  }
+
+  // Add cells.
+  AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
+  if (add) {
+    for (uptr i = 0; i < add->size; i++) {
+      Cell *c = &add->cells[i];
+      uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+      if (addr1 == addr) {
+        if (h->remove_) {
+          h->addidx_ = i;
+          h->cell_ = c;
+          return;
+        }
+        b->mtx.Unlock();
+        goto retry;
+      }
+    }
+  }
+
+  // The element does not exist, no need to create it if we want to remove.
+  if (h->remove_ || !h->create_) {
+    b->mtx.Unlock();
+    return;
+  }
+
+  // Now try to create it under the mutex.
+  h->created_ = true;
+  // See if we have a free embed cell.
+  for (uptr i = 0; i < kBucketSize; i++) {
+    Cell *c = &b->cells[i];
+    uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+    if (addr1 == 0) {
+      h->cell_ = c;
+      return;
+    }
+  }
+
+  // Store in the add cells.
+  if (!add) {
+    // Allocate a new add array.
+    const uptr kInitSize = 64;
+    add = (AddBucket*)InternalAlloc(kInitSize);
+    internal_memset(add, 0, kInitSize);
+    add->cap = (kInitSize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
+    add->size = 0;
+    atomic_store(&b->add, (uptr)add, memory_order_relaxed);
+  }
+  if (add->size == add->cap) {
+    // Grow existing add array.
+    uptr oldsize = sizeof(*add) + (add->cap - 1) * sizeof(add->cells[0]);
+    uptr newsize = oldsize * 2;
+    AddBucket *add1 = (AddBucket*)InternalAlloc(newsize);
+    internal_memset(add1, 0, newsize);
+    add1->cap = (newsize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
+    add1->size = add->size;
+    internal_memcpy(add1->cells, add->cells, add->size * sizeof(add->cells[0]));
+    InternalFree(add);
+    atomic_store(&b->add, (uptr)add1, memory_order_relaxed);
+    add = add1;
+  }
+  // Store.
+  uptr i = add->size++;
+  Cell *c = &add->cells[i];
+  CHECK_EQ(atomic_load(&c->addr, memory_order_relaxed), 0);
+  h->addidx_ = i;
+  h->cell_ = c;
+}
+
+template<typename T, uptr kSize>
+void AddrHashMap<T, kSize>::release(Handle *h) {
+  if (!h->cell_)
+    return;
+  Bucket *b = h->bucket_;
+  Cell *c = h->cell_;
+  uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+  if (h->created_) {
+    // Denote completion of insertion.
+    CHECK_EQ(addr1, 0);
+    // After the following store, the element becomes available
+    // for lock-free reads.
+    atomic_store(&c->addr, h->addr_, memory_order_release);
+    b->mtx.Unlock();
+  } else if (h->remove_) {
+    // Denote that the cell is empty now.
+    CHECK_EQ(addr1, h->addr_);
+    atomic_store(&c->addr, 0, memory_order_release);
+    // See if we need to compact the bucket.
+    AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
+    if (h->addidx_ == -1U) {
+      // Removed from embed array, move an add element into the freed cell.
+      if (add && add->size != 0) {
+        uptr last = --add->size;
+        Cell *c1 = &add->cells[last];
+        c->val = c1->val;
+        uptr addr1 = atomic_load(&c1->addr, memory_order_relaxed);
+        atomic_store(&c->addr, addr1, memory_order_release);
+        atomic_store(&c1->addr, 0, memory_order_release);
+      }
+    } else {
+      // Removed from add array, compact it.
+      uptr last = --add->size;
+      Cell *c1 = &add->cells[last];
+      if (c != c1) {
+        *c = *c1;
+        atomic_store(&c1->addr, 0, memory_order_relaxed);
+      }
+    }
+    if (add && add->size == 0) {
+      // FIXME(dvyukov): free add?
+    }
+    b->mtx.Unlock();
+  } else {
+    CHECK_EQ(addr1, h->addr_);
+    if (h->addidx_ != -1U)
+      b->mtx.ReadUnlock();
+  }
+}
+
+template<typename T, uptr kSize>
+uptr AddrHashMap<T, kSize>::calcHash(uptr addr) {
+  addr += addr << 10;
+  addr ^= addr >> 6;
+  return addr % kSize;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_ADDRHASHMAP_H
diff --git a/lsan/include/sanitizer_common/sanitizer_allocator.h b/lsan/include/sanitizer_common/sanitizer_allocator.h
new file mode 100644 (file)
index 0000000..9cfa408
--- /dev/null
@@ -0,0 +1,1480 @@
+//===-- sanitizer_allocator.h -----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Specialized memory allocator for ThreadSanitizer, MemorySanitizer, etc.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_H
+#define SANITIZER_ALLOCATOR_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_lfstack.h"
+
+namespace __sanitizer {
+
+// Prints error message and kills the program.
+void NORETURN ReportAllocatorCannotReturnNull();
+
+// SizeClassMap maps allocation sizes into size classes and back.
+// Class 0 corresponds to size 0.
+// Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16).
+// Next 4 classes: 256 + i * 64  (i = 1 to 4).
+// Next 4 classes: 512 + i * 128 (i = 1 to 4).
+// ...
+// Next 4 classes: 2^k + i * 2^(k-2) (i = 1 to 4).
+// Last class corresponds to kMaxSize = 1 << kMaxSizeLog.
+//
+// This structure of the size class map gives us:
+//   - Efficient table-free class-to-size and size-to-class functions.
+//   - Difference between two consequent size classes is betweed 14% and 25%
+//
+// This class also gives a hint to a thread-caching allocator about the amount
+// of chunks that need to be cached per-thread:
+//  - kMaxNumCached is the maximal number of chunks per size class.
+//  - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class.
+//
+// Part of output of SizeClassMap::Print():
+// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0
+// c01 => s: 16 diff: +16 00% l 4 cached: 256 4096; id 1
+// c02 => s: 32 diff: +16 100% l 5 cached: 256 8192; id 2
+// c03 => s: 48 diff: +16 50% l 5 cached: 256 12288; id 3
+// c04 => s: 64 diff: +16 33% l 6 cached: 256 16384; id 4
+// c05 => s: 80 diff: +16 25% l 6 cached: 256 20480; id 5
+// c06 => s: 96 diff: +16 20% l 6 cached: 256 24576; id 6
+// c07 => s: 112 diff: +16 16% l 6 cached: 256 28672; id 7
+//
+// c08 => s: 128 diff: +16 14% l 7 cached: 256 32768; id 8
+// c09 => s: 144 diff: +16 12% l 7 cached: 256 36864; id 9
+// c10 => s: 160 diff: +16 11% l 7 cached: 256 40960; id 10
+// c11 => s: 176 diff: +16 10% l 7 cached: 256 45056; id 11
+// c12 => s: 192 diff: +16 09% l 7 cached: 256 49152; id 12
+// c13 => s: 208 diff: +16 08% l 7 cached: 256 53248; id 13
+// c14 => s: 224 diff: +16 07% l 7 cached: 256 57344; id 14
+// c15 => s: 240 diff: +16 07% l 7 cached: 256 61440; id 15
+//
+// c16 => s: 256 diff: +16 06% l 8 cached: 256 65536; id 16
+// c17 => s: 320 diff: +64 25% l 8 cached: 204 65280; id 17
+// c18 => s: 384 diff: +64 20% l 8 cached: 170 65280; id 18
+// c19 => s: 448 diff: +64 16% l 8 cached: 146 65408; id 19
+//
+// c20 => s: 512 diff: +64 14% l 9 cached: 128 65536; id 20
+// c21 => s: 640 diff: +128 25% l 9 cached: 102 65280; id 21
+// c22 => s: 768 diff: +128 20% l 9 cached: 85 65280; id 22
+// c23 => s: 896 diff: +128 16% l 9 cached: 73 65408; id 23
+//
+// c24 => s: 1024 diff: +128 14% l 10 cached: 64 65536; id 24
+// c25 => s: 1280 diff: +256 25% l 10 cached: 51 65280; id 25
+// c26 => s: 1536 diff: +256 20% l 10 cached: 42 64512; id 26
+// c27 => s: 1792 diff: +256 16% l 10 cached: 36 64512; id 27
+//
+// ...
+//
+// c48 => s: 65536 diff: +8192 14% l 16 cached: 1 65536; id 48
+// c49 => s: 81920 diff: +16384 25% l 16 cached: 1 81920; id 49
+// c50 => s: 98304 diff: +16384 20% l 16 cached: 1 98304; id 50
+// c51 => s: 114688 diff: +16384 16% l 16 cached: 1 114688; id 51
+//
+// c52 => s: 131072 diff: +16384 14% l 17 cached: 1 131072; id 52
+
+template <uptr kMaxSizeLog, uptr kMaxNumCachedT, uptr kMaxBytesCachedLog>
+class SizeClassMap {
+  static const uptr kMinSizeLog = 4;
+  static const uptr kMidSizeLog = kMinSizeLog + 4;
+  static const uptr kMinSize = 1 << kMinSizeLog;
+  static const uptr kMidSize = 1 << kMidSizeLog;
+  static const uptr kMidClass = kMidSize / kMinSize;
+  static const uptr S = 2;
+  static const uptr M = (1 << S) - 1;
+
+ public:
+  static const uptr kMaxNumCached = kMaxNumCachedT;
+  // We transfer chunks between central and thread-local free lists in batches.
+  // For small size classes we allocate batches separately.
+  // For large size classes we use one of the chunks to store the batch.
+  struct TransferBatch {
+    TransferBatch *next;
+    uptr count;
+    void *batch[kMaxNumCached];
+  };
+
+  static const uptr kMaxSize = 1UL << kMaxSizeLog;
+  static const uptr kNumClasses =
+      kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1;
+  COMPILER_CHECK(kNumClasses >= 32 && kNumClasses <= 256);
+  static const uptr kNumClassesRounded =
+      kNumClasses == 32  ? 32 :
+      kNumClasses <= 64  ? 64 :
+      kNumClasses <= 128 ? 128 : 256;
+
+  static uptr Size(uptr class_id) {
+    if (class_id <= kMidClass)
+      return kMinSize * class_id;
+    class_id -= kMidClass;
+    uptr t = kMidSize << (class_id >> S);
+    return t + (t >> S) * (class_id & M);
+  }
+
+  static uptr ClassID(uptr size) {
+    if (size <= kMidSize)
+      return (size + kMinSize - 1) >> kMinSizeLog;
+    if (size > kMaxSize) return 0;
+    uptr l = MostSignificantSetBitIndex(size);
+    uptr hbits = (size >> (l - S)) & M;
+    uptr lbits = size & ((1 << (l - S)) - 1);
+    uptr l1 = l - kMidSizeLog;
+    return kMidClass + (l1 << S) + hbits + (lbits > 0);
+  }
+
+  static uptr MaxCached(uptr class_id) {
+    if (class_id == 0) return 0;
+    uptr n = (1UL << kMaxBytesCachedLog) / Size(class_id);
+    return Max<uptr>(1, Min(kMaxNumCached, n));
+  }
+
+  static void Print() {
+    uptr prev_s = 0;
+    uptr total_cached = 0;
+    for (uptr i = 0; i < kNumClasses; i++) {
+      uptr s = Size(i);
+      if (s >= kMidSize / 2 && (s & (s - 1)) == 0)
+        Printf("\n");
+      uptr d = s - prev_s;
+      uptr p = prev_s ? (d * 100 / prev_s) : 0;
+      uptr l = s ? MostSignificantSetBitIndex(s) : 0;
+      uptr cached = MaxCached(i) * s;
+      Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd "
+             "cached: %zd %zd; id %zd\n",
+             i, Size(i), d, p, l, MaxCached(i), cached, ClassID(s));
+      total_cached += cached;
+      prev_s = s;
+    }
+    Printf("Total cached: %zd\n", total_cached);
+  }
+
+  static bool SizeClassRequiresSeparateTransferBatch(uptr class_id) {
+    return Size(class_id) < sizeof(TransferBatch) -
+        sizeof(uptr) * (kMaxNumCached - MaxCached(class_id));
+  }
+
+  static void Validate() {
+    for (uptr c = 1; c < kNumClasses; c++) {
+      // Printf("Validate: c%zd\n", c);
+      uptr s = Size(c);
+      CHECK_NE(s, 0U);
+      CHECK_EQ(ClassID(s), c);
+      if (c != kNumClasses - 1)
+        CHECK_EQ(ClassID(s + 1), c + 1);
+      CHECK_EQ(ClassID(s - 1), c);
+      if (c)
+        CHECK_GT(Size(c), Size(c-1));
+    }
+    CHECK_EQ(ClassID(kMaxSize + 1), 0);
+
+    for (uptr s = 1; s <= kMaxSize; s++) {
+      uptr c = ClassID(s);
+      // Printf("s%zd => c%zd\n", s, c);
+      CHECK_LT(c, kNumClasses);
+      CHECK_GE(Size(c), s);
+      if (c > 0)
+        CHECK_LT(Size(c-1), s);
+    }
+  }
+};
+
+typedef SizeClassMap<17, 128, 16> DefaultSizeClassMap;
+typedef SizeClassMap<17, 64,  14> CompactSizeClassMap;
+template<class SizeClassAllocator> struct SizeClassAllocatorLocalCache;
+
+// Memory allocator statistics
+enum AllocatorStat {
+  AllocatorStatAllocated,
+  AllocatorStatMapped,
+  AllocatorStatCount
+};
+
+typedef uptr AllocatorStatCounters[AllocatorStatCount];
+
+// Per-thread stats, live in per-thread cache.
+class AllocatorStats {
+ public:
+  void Init() {
+    internal_memset(this, 0, sizeof(*this));
+  }
+  void InitLinkerInitialized() {}
+
+  void Add(AllocatorStat i, uptr v) {
+    v += atomic_load(&stats_[i], memory_order_relaxed);
+    atomic_store(&stats_[i], v, memory_order_relaxed);
+  }
+
+  void Sub(AllocatorStat i, uptr v) {
+    v = atomic_load(&stats_[i], memory_order_relaxed) - v;
+    atomic_store(&stats_[i], v, memory_order_relaxed);
+  }
+
+  void Set(AllocatorStat i, uptr v) {
+    atomic_store(&stats_[i], v, memory_order_relaxed);
+  }
+
+  uptr Get(AllocatorStat i) const {
+    return atomic_load(&stats_[i], memory_order_relaxed);
+  }
+
+ private:
+  friend class AllocatorGlobalStats;
+  AllocatorStats *next_;
+  AllocatorStats *prev_;
+  atomic_uintptr_t stats_[AllocatorStatCount];
+};
+
+// Global stats, used for aggregation and querying.
+class AllocatorGlobalStats : public AllocatorStats {
+ public:
+  void InitLinkerInitialized() {
+    next_ = this;
+    prev_ = this;
+  }
+  void Init() {
+    internal_memset(this, 0, sizeof(*this));
+    InitLinkerInitialized();
+  }
+
+  void Register(AllocatorStats *s) {
+    SpinMutexLock l(&mu_);
+    s->next_ = next_;
+    s->prev_ = this;
+    next_->prev_ = s;
+    next_ = s;
+  }
+
+  void Unregister(AllocatorStats *s) {
+    SpinMutexLock l(&mu_);
+    s->prev_->next_ = s->next_;
+    s->next_->prev_ = s->prev_;
+    for (int i = 0; i < AllocatorStatCount; i++)
+      Add(AllocatorStat(i), s->Get(AllocatorStat(i)));
+  }
+
+  void Get(AllocatorStatCounters s) const {
+    internal_memset(s, 0, AllocatorStatCount * sizeof(uptr));
+    SpinMutexLock l(&mu_);
+    const AllocatorStats *stats = this;
+    for (;;) {
+      for (int i = 0; i < AllocatorStatCount; i++)
+        s[i] += stats->Get(AllocatorStat(i));
+      stats = stats->next_;
+      if (stats == this)
+        break;
+    }
+    // All stats must be non-negative.
+    for (int i = 0; i < AllocatorStatCount; i++)
+      s[i] = ((sptr)s[i]) >= 0 ? s[i] : 0;
+  }
+
+ private:
+  mutable SpinMutex mu_;
+};
+
+// Allocators call these callbacks on mmap/munmap.
+struct NoOpMapUnmapCallback {
+  void OnMap(uptr p, uptr size) const { }
+  void OnUnmap(uptr p, uptr size) const { }
+};
+
+// Callback type for iterating over chunks.
+typedef void (*ForEachChunkCallback)(uptr chunk, void *arg);
+
+// SizeClassAllocator64 -- allocator for 64-bit address space.
+//
+// Space: a portion of address space of kSpaceSize bytes starting at
+// a fixed address (kSpaceBeg). Both constants are powers of two and
+// kSpaceBeg is kSpaceSize-aligned.
+// At the beginning the entire space is mprotect-ed, then small parts of it
+// are mapped on demand.
+//
+// Region: a part of Space dedicated to a single size class.
+// There are kNumClasses Regions of equal size.
+//
+// UserChunk: a piece of memory returned to user.
+// MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk.
+//
+// A Region looks like this:
+// UserChunk1 ... UserChunkN <gap> MetaChunkN ... MetaChunk1
+template <const uptr kSpaceBeg, const uptr kSpaceSize,
+          const uptr kMetadataSize, class SizeClassMap,
+          class MapUnmapCallback = NoOpMapUnmapCallback>
+class SizeClassAllocator64 {
+ public:
+  typedef typename SizeClassMap::TransferBatch Batch;
+  typedef SizeClassAllocator64<kSpaceBeg, kSpaceSize, kMetadataSize,
+      SizeClassMap, MapUnmapCallback> ThisT;
+  typedef SizeClassAllocatorLocalCache<ThisT> AllocatorCache;
+
+  void Init() {
+    CHECK_EQ(kSpaceBeg,
+             reinterpret_cast<uptr>(MmapNoAccess(kSpaceBeg, kSpaceSize)));
+    MapWithCallback(kSpaceEnd, AdditionalSize());
+  }
+
+  void MapWithCallback(uptr beg, uptr size) {
+    CHECK_EQ(beg, reinterpret_cast<uptr>(MmapFixedOrDie(beg, size)));
+    MapUnmapCallback().OnMap(beg, size);
+  }
+
+  void UnmapWithCallback(uptr beg, uptr size) {
+    MapUnmapCallback().OnUnmap(beg, size);
+    UnmapOrDie(reinterpret_cast<void *>(beg), size);
+  }
+
+  static bool CanAllocate(uptr size, uptr alignment) {
+    return size <= SizeClassMap::kMaxSize &&
+      alignment <= SizeClassMap::kMaxSize;
+  }
+
+  NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c,
+                                uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    RegionInfo *region = GetRegionInfo(class_id);
+    Batch *b = region->free_list.Pop();
+    if (!b)
+      b = PopulateFreeList(stat, c, class_id, region);
+    region->n_allocated += b->count;
+    return b;
+  }
+
+  NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) {
+    RegionInfo *region = GetRegionInfo(class_id);
+    CHECK_GT(b->count, 0);
+    region->free_list.Push(b);
+    region->n_freed += b->count;
+  }
+
+  static bool PointerIsMine(const void *p) {
+    return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize;
+  }
+
+  static uptr GetSizeClass(const void *p) {
+    return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClassesRounded;
+  }
+
+  void *GetBlockBegin(const void *p) {
+    uptr class_id = GetSizeClass(p);
+    uptr size = SizeClassMap::Size(class_id);
+    if (!size) return nullptr;
+    uptr chunk_idx = GetChunkIdx((uptr)p, size);
+    uptr reg_beg = (uptr)p & ~(kRegionSize - 1);
+    uptr beg = chunk_idx * size;
+    uptr next_beg = beg + size;
+    if (class_id >= kNumClasses) return nullptr;
+    RegionInfo *region = GetRegionInfo(class_id);
+    if (region->mapped_user >= next_beg)
+      return reinterpret_cast<void*>(reg_beg + beg);
+    return nullptr;
+  }
+
+  static uptr GetActuallyAllocatedSize(void *p) {
+    CHECK(PointerIsMine(p));
+    return SizeClassMap::Size(GetSizeClass(p));
+  }
+
+  uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
+
+  void *GetMetaData(const void *p) {
+    uptr class_id = GetSizeClass(p);
+    uptr size = SizeClassMap::Size(class_id);
+    uptr chunk_idx = GetChunkIdx(reinterpret_cast<uptr>(p), size);
+    return reinterpret_cast<void*>(kSpaceBeg + (kRegionSize * (class_id + 1)) -
+                                   (1 + chunk_idx) * kMetadataSize);
+  }
+
+  bool PointsIntoChunk(const void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr class_id = GetSizeClass(p);
+    uptr size = SizeClassMap::Size(class_id);
+    uptr beg = kSpaceBeg + kRegionSize * class_id;
+    uptr n_chunks = kRegionSize / (size + kMetadataSize);
+    uptr gap_start = beg + n_chunks * size + 1;
+    return beg <= mem < gap_start;
+  }
+
+  uptr TotalMemoryUsed() {
+    uptr res = 0;
+    for (uptr i = 0; i < kNumClasses; i++)
+      res += GetRegionInfo(i)->allocated_user;
+    return res;
+  }
+
+  // Test-only.
+  void TestOnlyUnmap() {
+    UnmapWithCallback(kSpaceBeg, kSpaceSize + AdditionalSize());
+  }
+
+  void PrintStats() {
+    uptr total_mapped = 0;
+    uptr n_allocated = 0;
+    uptr n_freed = 0;
+    for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
+      RegionInfo *region = GetRegionInfo(class_id);
+      total_mapped += region->mapped_user;
+      n_allocated += region->n_allocated;
+      n_freed += region->n_freed;
+    }
+    Printf("Stats: SizeClassAllocator64: %zdM mapped in %zd allocations; "
+           "remains %zd\n",
+           total_mapped >> 20, n_allocated, n_allocated - n_freed);
+    for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
+      RegionInfo *region = GetRegionInfo(class_id);
+      if (region->mapped_user == 0) continue;
+      Printf("  %02zd (%zd): total: %zd K allocs: %zd remains: %zd\n",
+             class_id,
+             SizeClassMap::Size(class_id),
+             region->mapped_user >> 10,
+             region->n_allocated,
+             region->n_allocated - region->n_freed);
+    }
+  }
+
+  // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
+  // introspection API.
+  void ForceLock() {
+    for (uptr i = 0; i < kNumClasses; i++) {
+      GetRegionInfo(i)->mutex.Lock();
+    }
+  }
+
+  void ForceUnlock() {
+    for (int i = (int)kNumClasses - 1; i >= 0; i--) {
+      GetRegionInfo(i)->mutex.Unlock();
+    }
+  }
+
+  // Iterate over all existing chunks.
+  // The allocator must be locked when calling this function.
+  void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+    for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
+      RegionInfo *region = GetRegionInfo(class_id);
+      uptr chunk_size = SizeClassMap::Size(class_id);
+      uptr region_beg = kSpaceBeg + class_id * kRegionSize;
+      for (uptr chunk = region_beg;
+           chunk < region_beg + region->allocated_user;
+           chunk += chunk_size) {
+        // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk));
+        callback(chunk, arg);
+      }
+    }
+  }
+
+  static uptr AdditionalSize() {
+    return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
+                     GetPageSizeCached());
+  }
+
+  typedef SizeClassMap SizeClassMapT;
+  static const uptr kNumClasses = SizeClassMap::kNumClasses;
+  static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;
+
+ private:
+  static const uptr kRegionSize = kSpaceSize / kNumClassesRounded;
+  static const uptr kSpaceEnd = kSpaceBeg + kSpaceSize;
+  COMPILER_CHECK(kSpaceBeg % kSpaceSize == 0);
+  // kRegionSize must be >= 2^32.
+  COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2)));
+  // Populate the free list with at most this number of bytes at once
+  // or with one element if its size is greater.
+  static const uptr kPopulateSize = 1 << 14;
+  // Call mmap for user memory with at least this size.
+  static const uptr kUserMapSize = 1 << 16;
+  // Call mmap for metadata memory with at least this size.
+  static const uptr kMetaMapSize = 1 << 16;
+
+  struct RegionInfo {
+    BlockingMutex mutex;
+    LFStack<Batch> free_list;
+    uptr allocated_user;  // Bytes allocated for user memory.
+    uptr allocated_meta;  // Bytes allocated for metadata.
+    uptr mapped_user;  // Bytes mapped for user memory.
+    uptr mapped_meta;  // Bytes mapped for metadata.
+    uptr n_allocated, n_freed;  // Just stats.
+  };
+  COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize);
+
+  RegionInfo *GetRegionInfo(uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg + kSpaceSize);
+    return &regions[class_id];
+  }
+
+  static uptr GetChunkIdx(uptr chunk, uptr size) {
+    uptr offset = chunk % kRegionSize;
+    // Here we divide by a non-constant. This is costly.
+    // size always fits into 32-bits. If the offset fits too, use 32-bit div.
+    if (offset >> (SANITIZER_WORDSIZE / 2))
+      return offset / size;
+    return (u32)offset / (u32)size;
+  }
+
+  NOINLINE Batch* PopulateFreeList(AllocatorStats *stat, AllocatorCache *c,
+                                   uptr class_id, RegionInfo *region) {
+    BlockingMutexLock l(&region->mutex);
+    Batch *b = region->free_list.Pop();
+    if (b)
+      return b;
+    uptr size = SizeClassMap::Size(class_id);
+    uptr count = size < kPopulateSize ? SizeClassMap::MaxCached(class_id) : 1;
+    uptr beg_idx = region->allocated_user;
+    uptr end_idx = beg_idx + count * size;
+    uptr region_beg = kSpaceBeg + kRegionSize * class_id;
+    if (end_idx + size > region->mapped_user) {
+      // Do the mmap for the user memory.
+      uptr map_size = kUserMapSize;
+      while (end_idx + size > region->mapped_user + map_size)
+        map_size += kUserMapSize;
+      CHECK_GE(region->mapped_user + map_size, end_idx);
+      MapWithCallback(region_beg + region->mapped_user, map_size);
+      stat->Add(AllocatorStatMapped, map_size);
+      region->mapped_user += map_size;
+    }
+    uptr total_count = (region->mapped_user - beg_idx - size)
+        / size / count * count;
+    region->allocated_meta += total_count * kMetadataSize;
+    if (region->allocated_meta > region->mapped_meta) {
+      uptr map_size = kMetaMapSize;
+      while (region->allocated_meta > region->mapped_meta + map_size)
+        map_size += kMetaMapSize;
+      // Do the mmap for the metadata.
+      CHECK_GE(region->mapped_meta + map_size, region->allocated_meta);
+      MapWithCallback(region_beg + kRegionSize -
+                      region->mapped_meta - map_size, map_size);
+      region->mapped_meta += map_size;
+    }
+    CHECK_LE(region->allocated_meta, region->mapped_meta);
+    if (region->mapped_user + region->mapped_meta > kRegionSize) {
+      Printf("%s: Out of memory. Dying. ", SanitizerToolName);
+      Printf("The process has exhausted %zuMB for size class %zu.\n",
+          kRegionSize / 1024 / 1024, size);
+      Die();
+    }
+    for (;;) {
+      if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id))
+        b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
+      else
+        b = (Batch*)(region_beg + beg_idx);
+      b->count = count;
+      for (uptr i = 0; i < count; i++)
+        b->batch[i] = (void*)(region_beg + beg_idx + i * size);
+      region->allocated_user += count * size;
+      CHECK_LE(region->allocated_user, region->mapped_user);
+      beg_idx += count * size;
+      if (beg_idx + count * size + size > region->mapped_user)
+        break;
+      CHECK_GT(b->count, 0);
+      region->free_list.Push(b);
+    }
+    return b;
+  }
+};
+
+// Maps integers in rage [0, kSize) to u8 values.
+template<u64 kSize>
+class FlatByteMap {
+ public:
+  void TestOnlyInit() {
+    internal_memset(map_, 0, sizeof(map_));
+  }
+
+  void set(uptr idx, u8 val) {
+    CHECK_LT(idx, kSize);
+    CHECK_EQ(0U, map_[idx]);
+    map_[idx] = val;
+  }
+  u8 operator[] (uptr idx) {
+    CHECK_LT(idx, kSize);
+    // FIXME: CHECK may be too expensive here.
+    return map_[idx];
+  }
+ private:
+  u8 map_[kSize];
+};
+
+// TwoLevelByteMap maps integers in range [0, kSize1*kSize2) to u8 values.
+// It is implemented as a two-dimensional array: array of kSize1 pointers
+// to kSize2-byte arrays. The secondary arrays are mmaped on demand.
+// Each value is initially zero and can be set to something else only once.
+// Setting and getting values from multiple threads is safe w/o extra locking.
+template <u64 kSize1, u64 kSize2, class MapUnmapCallback = NoOpMapUnmapCallback>
+class TwoLevelByteMap {
+ public:
+  void TestOnlyInit() {
+    internal_memset(map1_, 0, sizeof(map1_));
+    mu_.Init();
+  }
+
+  void TestOnlyUnmap() {
+    for (uptr i = 0; i < kSize1; i++) {
+      u8 *p = Get(i);
+      if (!p) continue;
+      MapUnmapCallback().OnUnmap(reinterpret_cast<uptr>(p), kSize2);
+      UnmapOrDie(p, kSize2);
+    }
+  }
+
+  uptr size() const { return kSize1 * kSize2; }
+  uptr size1() const { return kSize1; }
+  uptr size2() const { return kSize2; }
+
+  void set(uptr idx, u8 val) {
+    CHECK_LT(idx, kSize1 * kSize2);
+    u8 *map2 = GetOrCreate(idx / kSize2);
+    CHECK_EQ(0U, map2[idx % kSize2]);
+    map2[idx % kSize2] = val;
+  }
+
+  u8 operator[] (uptr idx) const {
+    CHECK_LT(idx, kSize1 * kSize2);
+    u8 *map2 = Get(idx / kSize2);
+    if (!map2) return 0;
+    return map2[idx % kSize2];
+  }
+
+ private:
+  u8 *Get(uptr idx) const {
+    CHECK_LT(idx, kSize1);
+    return reinterpret_cast<u8 *>(
+        atomic_load(&map1_[idx], memory_order_acquire));
+  }
+
+  u8 *GetOrCreate(uptr idx) {
+    u8 *res = Get(idx);
+    if (!res) {
+      SpinMutexLock l(&mu_);
+      if (!(res = Get(idx))) {
+        res = (u8*)MmapOrDie(kSize2, "TwoLevelByteMap");
+        MapUnmapCallback().OnMap(reinterpret_cast<uptr>(res), kSize2);
+        atomic_store(&map1_[idx], reinterpret_cast<uptr>(res),
+                     memory_order_release);
+      }
+    }
+    return res;
+  }
+
+  atomic_uintptr_t map1_[kSize1];
+  StaticSpinMutex mu_;
+};
+
+// SizeClassAllocator32 -- allocator for 32-bit address space.
+// This allocator can theoretically be used on 64-bit arch, but there it is less
+// efficient than SizeClassAllocator64.
+//
+// [kSpaceBeg, kSpaceBeg + kSpaceSize) is the range of addresses which can
+// be returned by MmapOrDie().
+//
+// Region:
+//   a result of a single call to MmapAlignedOrDie(kRegionSize, kRegionSize).
+// Since the regions are aligned by kRegionSize, there are exactly
+// kNumPossibleRegions possible regions in the address space and so we keep
+// a ByteMap possible_regions to store the size classes of each Region.
+// 0 size class means the region is not used by the allocator.
+//
+// One Region is used to allocate chunks of a single size class.
+// A Region looks like this:
+// UserChunk1 .. UserChunkN <gap> MetaChunkN .. MetaChunk1
+//
+// In order to avoid false sharing the objects of this class should be
+// chache-line aligned.
+template <const uptr kSpaceBeg, const u64 kSpaceSize,
+          const uptr kMetadataSize, class SizeClassMap,
+          const uptr kRegionSizeLog,
+          class ByteMap,
+          class MapUnmapCallback = NoOpMapUnmapCallback>
+class SizeClassAllocator32 {
+ public:
+  typedef typename SizeClassMap::TransferBatch Batch;
+  typedef SizeClassAllocator32<kSpaceBeg, kSpaceSize, kMetadataSize,
+      SizeClassMap, kRegionSizeLog, ByteMap, MapUnmapCallback> ThisT;
+  typedef SizeClassAllocatorLocalCache<ThisT> AllocatorCache;
+
+  void Init() {
+    possible_regions.TestOnlyInit();
+    internal_memset(size_class_info_array, 0, sizeof(size_class_info_array));
+  }
+
+  void *MapWithCallback(uptr size) {
+    size = RoundUpTo(size, GetPageSizeCached());
+    void *res = MmapOrDie(size, "SizeClassAllocator32");
+    MapUnmapCallback().OnMap((uptr)res, size);
+    return res;
+  }
+
+  void UnmapWithCallback(uptr beg, uptr size) {
+    MapUnmapCallback().OnUnmap(beg, size);
+    UnmapOrDie(reinterpret_cast<void *>(beg), size);
+  }
+
+  static bool CanAllocate(uptr size, uptr alignment) {
+    return size <= SizeClassMap::kMaxSize &&
+      alignment <= SizeClassMap::kMaxSize;
+  }
+
+  void *GetMetaData(const void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr beg = ComputeRegionBeg(mem);
+    uptr size = SizeClassMap::Size(GetSizeClass(p));
+    u32 offset = mem - beg;
+    uptr n = offset / (u32)size;  // 32-bit division
+    uptr meta = (beg + kRegionSize) - (n + 1) * kMetadataSize;
+    return reinterpret_cast<void*>(meta);
+  }
+
+  bool PointsIntoChunk(const void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr beg = ComputeRegionBeg(mem);
+    uptr size = SizeClassMap::Size(GetSizeClass(p));
+    uptr n_chunks = kRegionSize / (size + kMetadataSize);
+    uptr gap_start = beg + n_chunks * size + 1;
+    return (beg <= mem) && (mem < gap_start);
+  }
+
+  NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c,
+                                uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    SizeClassInfo *sci = GetSizeClassInfo(class_id);
+    SpinMutexLock l(&sci->mutex);
+    if (sci->free_list.empty())
+      PopulateFreeList(stat, c, sci, class_id);
+    CHECK(!sci->free_list.empty());
+    Batch *b = sci->free_list.front();
+    sci->free_list.pop_front();
+    return b;
+  }
+
+  NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) {
+    CHECK_LT(class_id, kNumClasses);
+    SizeClassInfo *sci = GetSizeClassInfo(class_id);
+    SpinMutexLock l(&sci->mutex);
+    CHECK_GT(b->count, 0);
+    sci->free_list.push_front(b);
+  }
+
+  bool PointerIsMine(const void *p) {
+    return GetSizeClass(p) != 0;
+  }
+
+  uptr GetSizeClass(const void *p) {
+    return possible_regions[ComputeRegionId(reinterpret_cast<uptr>(p))];
+  }
+
+  void *GetBlockBegin(const void *p) {
+    CHECK(PointerIsMine(p));
+    uptr mem = reinterpret_cast<uptr>(p);
+    uptr beg = ComputeRegionBeg(mem);
+    uptr size = SizeClassMap::Size(GetSizeClass(p));
+    u32 offset = mem - beg;
+    u32 n = offset / (u32)size;  // 32-bit division
+    uptr res = beg + (n * (u32)size);
+    return reinterpret_cast<void*>(res);
+  }
+
+  uptr GetActuallyAllocatedSize(void *p) {
+    CHECK(PointerIsMine(p));
+    return SizeClassMap::Size(GetSizeClass(p));
+  }
+
+  uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
+
+  uptr TotalMemoryUsed() {
+    // No need to lock here.
+    uptr res = 0;
+    for (uptr i = 0; i < kNumPossibleRegions; i++)
+      if (possible_regions[i])
+        res += kRegionSize;
+    return res;
+  }
+
+  void TestOnlyUnmap() {
+    for (uptr i = 0; i < kNumPossibleRegions; i++)
+      if (possible_regions[i])
+        UnmapWithCallback((i * kRegionSize), kRegionSize);
+  }
+
+  // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
+  // introspection API.
+  void ForceLock() {
+    for (uptr i = 0; i < kNumClasses; i++) {
+      GetSizeClassInfo(i)->mutex.Lock();
+    }
+  }
+
+  void ForceUnlock() {
+    for (int i = kNumClasses - 1; i >= 0; i--) {
+      GetSizeClassInfo(i)->mutex.Unlock();
+    }
+  }
+
+  // Iterate over all existing chunks.
+  // The allocator must be locked when calling this function.
+  void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+    for (uptr region = 0; region < kNumPossibleRegions; region++)
+      if (possible_regions[region]) {
+        uptr chunk_size = SizeClassMap::Size(possible_regions[region]);
+        uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize);
+        uptr region_beg = region * kRegionSize;
+        for (uptr chunk = region_beg;
+             chunk < region_beg + max_chunks_in_region * chunk_size;
+             chunk += chunk_size) {
+          // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk));
+          callback(chunk, arg);
+        }
+      }
+  }
+
+  void PrintStats() {
+  }
+
+  static uptr AdditionalSize() {
+    return 0;
+  }
+
+  typedef SizeClassMap SizeClassMapT;
+  static const uptr kNumClasses = SizeClassMap::kNumClasses;
+
+ private:
+  static const uptr kRegionSize = 1 << kRegionSizeLog;
+  static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize;
+
+  struct SizeClassInfo {
+    SpinMutex mutex;
+    IntrusiveList<Batch> free_list;
+    char padding[kCacheLineSize - sizeof(uptr) - sizeof(IntrusiveList<Batch>)];
+  };
+  COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize);
+
+  uptr ComputeRegionId(uptr mem) {
+    uptr res = mem >> kRegionSizeLog;
+    CHECK_LT(res, kNumPossibleRegions);
+    return res;
+  }
+
+  uptr ComputeRegionBeg(uptr mem) {
+    return mem & ~(kRegionSize - 1);
+  }
+
+  uptr AllocateRegion(AllocatorStats *stat, uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    uptr res = reinterpret_cast<uptr>(MmapAlignedOrDie(kRegionSize, kRegionSize,
+                                      "SizeClassAllocator32"));
+    MapUnmapCallback().OnMap(res, kRegionSize);
+    stat->Add(AllocatorStatMapped, kRegionSize);
+    CHECK_EQ(0U, (res & (kRegionSize - 1)));
+    possible_regions.set(ComputeRegionId(res), static_cast<u8>(class_id));
+    return res;
+  }
+
+  SizeClassInfo *GetSizeClassInfo(uptr class_id) {
+    CHECK_LT(class_id, kNumClasses);
+    return &size_class_info_array[class_id];
+  }
+
+  void PopulateFreeList(AllocatorStats *stat, AllocatorCache *c,
+                        SizeClassInfo *sci, uptr class_id) {
+    uptr size = SizeClassMap::Size(class_id);
+    uptr reg = AllocateRegion(stat, class_id);
+    uptr n_chunks = kRegionSize / (size + kMetadataSize);
+    uptr max_count = SizeClassMap::MaxCached(class_id);
+    Batch *b = nullptr;
+    for (uptr i = reg; i < reg + n_chunks * size; i += size) {
+      if (!b) {
+        if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id))
+          b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
+        else
+          b = (Batch*)i;
+        b->count = 0;
+      }
+      b->batch[b->count++] = (void*)i;
+      if (b->count == max_count) {
+        CHECK_GT(b->count, 0);
+        sci->free_list.push_back(b);
+        b = nullptr;
+      }
+    }
+    if (b) {
+      CHECK_GT(b->count, 0);
+      sci->free_list.push_back(b);
+    }
+  }
+
+  ByteMap possible_regions;
+  SizeClassInfo size_class_info_array[kNumClasses];
+};
+
+// Objects of this type should be used as local caches for SizeClassAllocator64
+// or SizeClassAllocator32. Since the typical use of this class is to have one
+// object per thread in TLS, is has to be POD.
+template<class SizeClassAllocator>
+struct SizeClassAllocatorLocalCache {
+  typedef SizeClassAllocator Allocator;
+  static const uptr kNumClasses = SizeClassAllocator::kNumClasses;
+
+  void Init(AllocatorGlobalStats *s) {
+    stats_.Init();
+    if (s)
+      s->Register(&stats_);
+  }
+
+  void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) {
+    Drain(allocator);
+    if (s)
+      s->Unregister(&stats_);
+  }
+
+  void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
+    CHECK_NE(class_id, 0UL);
+    CHECK_LT(class_id, kNumClasses);
+    stats_.Add(AllocatorStatAllocated, SizeClassMap::Size(class_id));
+    PerClass *c = &per_class_[class_id];
+    if (UNLIKELY(c->count == 0))
+      Refill(allocator, class_id);
+    void *res = c->batch[--c->count];
+    PREFETCH(c->batch[c->count - 1]);
+    return res;
+  }
+
+  void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
+    CHECK_NE(class_id, 0UL);
+    CHECK_LT(class_id, kNumClasses);
+    // If the first allocator call on a new thread is a deallocation, then
+    // max_count will be zero, leading to check failure.
+    InitCache();
+    stats_.Sub(AllocatorStatAllocated, SizeClassMap::Size(class_id));
+    PerClass *c = &per_class_[class_id];
+    CHECK_NE(c->max_count, 0UL);
+    if (UNLIKELY(c->count == c->max_count))
+      Drain(allocator, class_id);
+    c->batch[c->count++] = p;
+  }
+
+  void Drain(SizeClassAllocator *allocator) {
+    for (uptr class_id = 0; class_id < kNumClasses; class_id++) {
+      PerClass *c = &per_class_[class_id];
+      while (c->count > 0)
+        Drain(allocator, class_id);
+    }
+  }
+
+  // private:
+  typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap;
+  typedef typename SizeClassMap::TransferBatch Batch;
+  struct PerClass {
+    uptr count;
+    uptr max_count;
+    void *batch[2 * SizeClassMap::kMaxNumCached];
+  };
+  PerClass per_class_[kNumClasses];
+  AllocatorStats stats_;
+
+  void InitCache() {
+    if (per_class_[1].max_count)
+      return;
+    for (uptr i = 0; i < kNumClasses; i++) {
+      PerClass *c = &per_class_[i];
+      c->max_count = 2 * SizeClassMap::MaxCached(i);
+    }
+  }
+
+  NOINLINE void Refill(SizeClassAllocator *allocator, uptr class_id) {
+    InitCache();
+    PerClass *c = &per_class_[class_id];
+    Batch *b = allocator->AllocateBatch(&stats_, this, class_id);
+    CHECK_GT(b->count, 0);
+    for (uptr i = 0; i < b->count; i++)
+      c->batch[i] = b->batch[i];
+    c->count = b->count;
+    if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id))
+      Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b);
+  }
+
+  NOINLINE void Drain(SizeClassAllocator *allocator, uptr class_id) {
+    InitCache();
+    PerClass *c = &per_class_[class_id];
+    Batch *b;
+    if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id))
+      b = (Batch*)Allocate(allocator, SizeClassMap::ClassID(sizeof(Batch)));
+    else
+      b = (Batch*)c->batch[0];
+    uptr cnt = Min(c->max_count / 2, c->count);
+    for (uptr i = 0; i < cnt; i++) {
+      b->batch[i] = c->batch[i];
+      c->batch[i] = c->batch[i + c->max_count / 2];
+    }
+    b->count = cnt;
+    c->count -= cnt;
+    CHECK_GT(b->count, 0);
+    allocator->DeallocateBatch(&stats_, class_id, b);
+  }
+};
+
+// This class can (de)allocate only large chunks of memory using mmap/unmap.
+// The main purpose of this allocator is to cover large and rare allocation
+// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64).
+template <class MapUnmapCallback = NoOpMapUnmapCallback>
+class LargeMmapAllocator {
+ public:
+  void InitLinkerInitialized(bool may_return_null) {
+    page_size_ = GetPageSizeCached();
+    atomic_store(&may_return_null_, may_return_null, memory_order_relaxed);
+  }
+
+  void Init(bool may_return_null) {
+    internal_memset(this, 0, sizeof(*this));
+    InitLinkerInitialized(may_return_null);
+  }
+
+  void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) {
+    CHECK(IsPowerOfTwo(alignment));
+    uptr map_size = RoundUpMapSize(size);
+    if (alignment > page_size_)
+      map_size += alignment;
+    // Overflow.
+    if (map_size < size)
+      return ReturnNullOrDie();
+    uptr map_beg = reinterpret_cast<uptr>(
+        MmapOrDie(map_size, "LargeMmapAllocator"));
+    CHECK(IsAligned(map_beg, page_size_));
+    MapUnmapCallback().OnMap(map_beg, map_size);
+    uptr map_end = map_beg + map_size;
+    uptr res = map_beg + page_size_;
+    if (res & (alignment - 1))  // Align.
+      res += alignment - (res & (alignment - 1));
+    CHECK(IsAligned(res, alignment));
+    CHECK(IsAligned(res, page_size_));
+    CHECK_GE(res + size, map_beg);
+    CHECK_LE(res + size, map_end);
+    Header *h = GetHeader(res);
+    h->size = size;
+    h->map_beg = map_beg;
+    h->map_size = map_size;
+    uptr size_log = MostSignificantSetBitIndex(map_size);
+    CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log));
+    {
+      SpinMutexLock l(&mutex_);
+      uptr idx = n_chunks_++;
+      chunks_sorted_ = false;
+      CHECK_LT(idx, kMaxNumChunks);
+      h->chunk_idx = idx;
+      chunks_[idx] = h;
+      stats.n_allocs++;
+      stats.currently_allocated += map_size;
+      stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated);
+      stats.by_size_log[size_log]++;
+      stat->Add(AllocatorStatAllocated, map_size);
+      stat->Add(AllocatorStatMapped, map_size);
+    }
+    return reinterpret_cast<void*>(res);
+  }
+
+  void *ReturnNullOrDie() {
+    if (atomic_load(&may_return_null_, memory_order_acquire))
+      return nullptr;
+    ReportAllocatorCannotReturnNull();
+  }
+
+  void SetMayReturnNull(bool may_return_null) {
+    atomic_store(&may_return_null_, may_return_null, memory_order_release);
+  }
+
+  void Deallocate(AllocatorStats *stat, void *p) {
+    Header *h = GetHeader(p);
+    {
+      SpinMutexLock l(&mutex_);
+      uptr idx = h->chunk_idx;
+      CHECK_EQ(chunks_[idx], h);
+      CHECK_LT(idx, n_chunks_);
+      chunks_[idx] = chunks_[n_chunks_ - 1];
+      chunks_[idx]->chunk_idx = idx;
+      n_chunks_--;
+      chunks_sorted_ = false;
+      stats.n_frees++;
+      stats.currently_allocated -= h->map_size;
+      stat->Sub(AllocatorStatAllocated, h->map_size);
+      stat->Sub(AllocatorStatMapped, h->map_size);
+    }
+    MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
+    UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size);
+  }
+
+  uptr TotalMemoryUsed() {
+    SpinMutexLock l(&mutex_);
+    uptr res = 0;
+    for (uptr i = 0; i < n_chunks_; i++) {
+      Header *h = chunks_[i];
+      CHECK_EQ(h->chunk_idx, i);
+      res += RoundUpMapSize(h->size);
+    }
+    return res;
+  }
+
+  bool PointerIsMine(const void *p) {
+    return GetBlockBegin(p) != nullptr;
+  }
+
+  uptr GetActuallyAllocatedSize(void *p) {
+    return RoundUpTo(GetHeader(p)->size, page_size_);
+  }
+
+  // At least page_size_/2 metadata bytes is available.
+  void *GetMetaData(const void *p) {
+    // Too slow: CHECK_EQ(p, GetBlockBegin(p));
+    if (!IsAligned(reinterpret_cast<uptr>(p), page_size_)) {
+      Printf("%s: bad pointer %p\n", SanitizerToolName, p);
+      CHECK(IsAligned(reinterpret_cast<uptr>(p), page_size_));
+    }
+    return GetHeader(p) + 1;
+  }
+
+  void *GetBlockBegin(const void *ptr) {
+    uptr p = reinterpret_cast<uptr>(ptr);
+    SpinMutexLock l(&mutex_);
+    uptr nearest_chunk = 0;
+    // Cache-friendly linear search.
+    for (uptr i = 0; i < n_chunks_; i++) {
+      uptr ch = reinterpret_cast<uptr>(chunks_[i]);
+      if (p < ch) continue;  // p is at left to this chunk, skip it.
+      if (p - ch < p - nearest_chunk)
+        nearest_chunk = ch;
+    }
+    if (!nearest_chunk)
+      return nullptr;
+    Header *h = reinterpret_cast<Header *>(nearest_chunk);
+    CHECK_GE(nearest_chunk, h->map_beg);
+    CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
+    CHECK_LE(nearest_chunk, p);
+    if (h->map_beg + h->map_size <= p)
+      return nullptr;
+    return GetUser(h);
+  }
+
+  // This function does the same as GetBlockBegin, but is much faster.
+  // Must be called with the allocator locked.
+  void *GetBlockBeginFastLocked(void *ptr) {
+    mutex_.CheckLocked();
+    uptr p = reinterpret_cast<uptr>(ptr);
+    uptr n = n_chunks_;
+    if (!n) return nullptr;
+    if (!chunks_sorted_) {
+      // Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate.
+      SortArray(reinterpret_cast<uptr*>(chunks_), n);
+      for (uptr i = 0; i < n; i++)
+        chunks_[i]->chunk_idx = i;
+      chunks_sorted_ = true;
+      min_mmap_ = reinterpret_cast<uptr>(chunks_[0]);
+      max_mmap_ = reinterpret_cast<uptr>(chunks_[n - 1]) +
+          chunks_[n - 1]->map_size;
+    }
+    if (p < min_mmap_ || p >= max_mmap_)
+      return nullptr;
+    uptr beg = 0, end = n - 1;
+    // This loop is a log(n) lower_bound. It does not check for the exact match
+    // to avoid expensive cache-thrashing loads.
+    while (end - beg >= 2) {
+      uptr mid = (beg + end) / 2;  // Invariant: mid >= beg + 1
+      if (p < reinterpret_cast<uptr>(chunks_[mid]))
+        end = mid - 1;  // We are not interested in chunks_[mid].
+      else
+        beg = mid;  // chunks_[mid] may still be what we want.
+    }
+
+    if (beg < end) {
+      CHECK_EQ(beg + 1, end);
+      // There are 2 chunks left, choose one.
+      if (p >= reinterpret_cast<uptr>(chunks_[end]))
+        beg = end;
+    }
+
+    Header *h = chunks_[beg];
+    if (h->map_beg + h->map_size <= p || p < h->map_beg)
+      return nullptr;
+    return GetUser(h);
+  }
+
+  void PrintStats() {
+    Printf("Stats: LargeMmapAllocator: allocated %zd times, "
+           "remains %zd (%zd K) max %zd M; by size logs: ",
+           stats.n_allocs, stats.n_allocs - stats.n_frees,
+           stats.currently_allocated >> 10, stats.max_allocated >> 20);
+    for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) {
+      uptr c = stats.by_size_log[i];
+      if (!c) continue;
+      Printf("%zd:%zd; ", i, c);
+    }
+    Printf("\n");
+  }
+
+  // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
+  // introspection API.
+  void ForceLock() {
+    mutex_.Lock();
+  }
+
+  void ForceUnlock() {
+    mutex_.Unlock();
+  }
+
+  // Iterate over all existing chunks.
+  // The allocator must be locked when calling this function.
+  void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+    for (uptr i = 0; i < n_chunks_; i++)
+      callback(reinterpret_cast<uptr>(GetUser(chunks_[i])), arg);
+  }
+
+ private:
+  static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18);
+  struct Header {
+    uptr map_beg;
+    uptr map_size;
+    uptr size;
+    uptr chunk_idx;
+  };
+
+  Header *GetHeader(uptr p) {
+    CHECK(IsAligned(p, page_size_));
+    return reinterpret_cast<Header*>(p - page_size_);
+  }
+  Header *GetHeader(const void *p) {
+    return GetHeader(reinterpret_cast<uptr>(p));
+  }
+
+  void *GetUser(Header *h) {
+    CHECK(IsAligned((uptr)h, page_size_));
+    return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + page_size_);
+  }
+
+  uptr RoundUpMapSize(uptr size) {
+    return RoundUpTo(size, page_size_) + page_size_;
+  }
+
+  uptr page_size_;
+  Header *chunks_[kMaxNumChunks];
+  uptr n_chunks_;
+  uptr min_mmap_, max_mmap_;
+  bool chunks_sorted_;
+  struct Stats {
+    uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
+  } stats;
+  atomic_uint8_t may_return_null_;
+  SpinMutex mutex_;
+};
+
+// This class implements a complete memory allocator by using two
+// internal allocators:
+// PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
+//  When allocating 2^x bytes it should return 2^x aligned chunk.
+// PrimaryAllocator is used via a local AllocatorCache.
+// SecondaryAllocator can allocate anything, but is not efficient.
+template <class PrimaryAllocator, class AllocatorCache,
+          class SecondaryAllocator>  // NOLINT
+class CombinedAllocator {
+ public:
+  void InitCommon(bool may_return_null) {
+    primary_.Init();
+    atomic_store(&may_return_null_, may_return_null, memory_order_relaxed);
+  }
+
+  void InitLinkerInitialized(bool may_return_null) {
+    secondary_.InitLinkerInitialized(may_return_null);
+    stats_.InitLinkerInitialized();
+    InitCommon(may_return_null);
+  }
+
+  void Init(bool may_return_null) {
+    secondary_.Init(may_return_null);
+    stats_.Init();
+    InitCommon(may_return_null);
+  }
+
+  void *Allocate(AllocatorCache *cache, uptr size, uptr alignment,
+                 bool cleared = false, bool check_rss_limit = false) {
+    // Returning 0 on malloc(0) may break a lot of code.
+    if (size == 0)
+      size = 1;
+    if (size + alignment < size)
+      return ReturnNullOrDie();
+    if (check_rss_limit && RssLimitIsExceeded())
+      return ReturnNullOrDie();
+    if (alignment > 8)
+      size = RoundUpTo(size, alignment);
+    void *res;
+    bool from_primary = primary_.CanAllocate(size, alignment);
+    if (from_primary)
+      res = cache->Allocate(&primary_, primary_.ClassID(size));
+    else
+      res = secondary_.Allocate(&stats_, size, alignment);
+    if (alignment > 8)
+      CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
+    if (cleared && res && from_primary)
+      internal_bzero_aligned16(res, RoundUpTo(size, 16));
+    return res;
+  }
+
+  bool MayReturnNull() const {
+    return atomic_load(&may_return_null_, memory_order_acquire);
+  }
+
+  void *ReturnNullOrDie() {
+    if (MayReturnNull())
+      return nullptr;
+    ReportAllocatorCannotReturnNull();
+  }
+
+  void SetMayReturnNull(bool may_return_null) {
+    secondary_.SetMayReturnNull(may_return_null);
+    atomic_store(&may_return_null_, may_return_null, memory_order_release);
+  }
+
+  bool RssLimitIsExceeded() {
+    return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire);
+  }
+
+  void SetRssLimitIsExceeded(bool rss_limit_is_exceeded) {
+    atomic_store(&rss_limit_is_exceeded_, rss_limit_is_exceeded,
+                 memory_order_release);
+  }
+
+  void Deallocate(AllocatorCache *cache, void *p) {
+    if (!p) return;
+    if (primary_.PointerIsMine(p))
+      cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
+    else
+      secondary_.Deallocate(&stats_, p);
+  }
+
+  void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
+                   uptr alignment) {
+    if (!p)
+      return Allocate(cache, new_size, alignment);
+    if (!new_size) {
+      Deallocate(cache, p);
+      return nullptr;
+    }
+    CHECK(PointerIsMine(p));
+    uptr old_size = GetActuallyAllocatedSize(p);
+    uptr memcpy_size = Min(new_size, old_size);
+    void *new_p = Allocate(cache, new_size, alignment);
+    if (new_p)
+      internal_memcpy(new_p, p, memcpy_size);
+    Deallocate(cache, p);
+    return new_p;
+  }
+
+  bool PointerIsMine(void *p) {
+    if (primary_.PointerIsMine(p))
+      return true;
+    return secondary_.PointerIsMine(p);
+  }
+
+  bool FromPrimary(void *p) {
+    return primary_.PointerIsMine(p);
+  }
+
+  void *GetMetaData(const void *p) {
+    if (primary_.PointerIsMine(p))
+      return primary_.GetMetaData(p);
+    return secondary_.GetMetaData(p);
+  }
+
+  void *GetBlockBegin(const void *p) {
+    if (primary_.PointerIsMine(p))
+      return primary_.GetBlockBegin(p);
+    return secondary_.GetBlockBegin(p);
+  }
+
+  // This function does the same as GetBlockBegin, but is much faster.
+  // Must be called with the allocator locked.
+  void *GetBlockBeginFastLocked(void *p) {
+    if (primary_.PointerIsMine(p))
+      return primary_.GetBlockBegin(p);
+    return secondary_.GetBlockBeginFastLocked(p);
+  }
+
+  uptr GetActuallyAllocatedSize(void *p) {
+    if (primary_.PointerIsMine(p))
+      return primary_.GetActuallyAllocatedSize(p);
+    return secondary_.GetActuallyAllocatedSize(p);
+  }
+
+  uptr TotalMemoryUsed() {
+    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
+  }
+
+  void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
+
+  void InitCache(AllocatorCache *cache) {
+    cache->Init(&stats_);
+  }
+
+  void DestroyCache(AllocatorCache *cache) {
+    cache->Destroy(&primary_, &stats_);
+  }
+
+  void SwallowCache(AllocatorCache *cache) {
+    cache->Drain(&primary_);
+  }
+
+  void GetStats(AllocatorStatCounters s) const {
+    stats_.Get(s);
+  }
+
+  void PrintStats() {
+    primary_.PrintStats();
+    secondary_.PrintStats();
+  }
+
+  // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
+  // introspection API.
+  void ForceLock() {
+    primary_.ForceLock();
+    secondary_.ForceLock();
+  }
+
+  void ForceUnlock() {
+    secondary_.ForceUnlock();
+    primary_.ForceUnlock();
+  }
+
+  bool PointsIntoChunk(const void *p) {
+    if (primary_.PointerIsMine(p))
+      return primary_.PointsIntoChunk(p);
+    return true;
+  }
+
+  // Iterate over all existing chunks.
+  // The allocator must be locked when calling this function.
+  void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+    primary_.ForEachChunk(callback, arg);
+    secondary_.ForEachChunk(callback, arg);
+  }
+
+ private:
+  PrimaryAllocator primary_;
+  SecondaryAllocator secondary_;
+  AllocatorGlobalStats stats_;
+  atomic_uint8_t may_return_null_;
+  atomic_uint8_t rss_limit_is_exceeded_;
+};
+
+// Returns true if calloc(size, n) should return 0 due to overflow in size*n.
+bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_ALLOCATOR_H
diff --git a/lsan/include/sanitizer_common/sanitizer_allocator_interface.h b/lsan/include/sanitizer_common/sanitizer_allocator_interface.h
new file mode 100644 (file)
index 0000000..fd5bed3
--- /dev/null
@@ -0,0 +1,36 @@
+//===-- sanitizer_allocator_interface.h ------------------------- C++ -----===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Re-declaration of functions from public sanitizer allocator interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_INTERFACE_H
+#define SANITIZER_ALLOCATOR_INTERFACE_H
+
+#include "sanitizer_internal_defs.h"
+
+using __sanitizer::uptr;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_estimated_allocated_size(uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p);
+SANITIZER_INTERFACE_ATTRIBUTE uptr
+__sanitizer_get_allocated_size(const void *p);
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes();
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_heap_size();
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_free_bytes();
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_unmapped_bytes();
+
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+    /* OPTIONAL */ void __sanitizer_malloc_hook(void *ptr, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+    /* OPTIONAL */ void __sanitizer_free_hook(void *ptr);
+}  // extern "C"
+
+#endif  // SANITIZER_ALLOCATOR_INTERFACE_H
diff --git a/lsan/include/sanitizer_common/sanitizer_allocator_internal.h b/lsan/include/sanitizer_common/sanitizer_allocator_internal.h
new file mode 100644 (file)
index 0000000..99d8516
--- /dev/null
@@ -0,0 +1,61 @@
+//===-- sanitizer_allocator_internal.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This allocator is used inside run-times.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_INTERNAL_H
+#define SANITIZER_ALLOCATOR_INTERNAL_H
+
+#include "sanitizer_allocator.h"
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+// FIXME: Check if we may use even more compact size class map for internal
+// purposes.
+typedef CompactSizeClassMap InternalSizeClassMap;
+
+static const uptr kInternalAllocatorSpace = 0;
+static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
+static const uptr kInternalAllocatorRegionSizeLog = 20;
+#if SANITIZER_WORDSIZE == 32
+static const uptr kInternalAllocatorNumRegions =
+    kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
+typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
+#else
+static const uptr kInternalAllocatorNumRegions =
+    kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
+typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
+#endif
+typedef SizeClassAllocator32<
+    kInternalAllocatorSpace, kInternalAllocatorSize, 0, InternalSizeClassMap,
+    kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator;
+
+typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
+    InternalAllocatorCache;
+
+typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
+                          LargeMmapAllocator<> > InternalAllocator;
+
+void *InternalAlloc(uptr size, InternalAllocatorCache *cache = nullptr);
+void InternalFree(void *p, InternalAllocatorCache *cache = nullptr);
+InternalAllocator *internal_allocator();
+
+enum InternalAllocEnum {
+  INTERNAL_ALLOC
+};
+
+} // namespace __sanitizer
+
+inline void *operator new(__sanitizer::operator_new_size_type size,
+                          InternalAllocEnum) {
+  return InternalAlloc(size);
+}
+
+#endif // SANITIZER_ALLOCATOR_INTERNAL_H
diff --git a/lsan/include/sanitizer_common/sanitizer_asm.h b/lsan/include/sanitizer_common/sanitizer_asm.h
new file mode 100644 (file)
index 0000000..985f59c
--- /dev/null
@@ -0,0 +1,42 @@
+//===-- sanitizer_asm.h -----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Various support for assemebler.
+//
+//===----------------------------------------------------------------------===//
+
+// Some toolchains do not support .cfi asm directives, so we have to hide
+// them inside macros.
+#if defined(__clang__) ||                                                      \
+    (defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM))
+  // GCC defined __GCC_HAVE_DWARF2_CFI_ASM if it supports CFI.
+  // Clang seems to support CFI by default (or not?).
+  // We need two versions of macros: for inline asm and standalone asm files.
+# define CFI_INL_ADJUST_CFA_OFFSET(n) ".cfi_adjust_cfa_offset " #n ";"
+
+# define CFI_STARTPROC .cfi_startproc
+# define CFI_ENDPROC .cfi_endproc
+# define CFI_ADJUST_CFA_OFFSET(n) .cfi_adjust_cfa_offset n
+# define CFI_DEF_CFA_OFFSET(n) .cfi_def_cfa_offset n
+# define CFI_REL_OFFSET(reg, n) .cfi_rel_offset reg, n
+# define CFI_OFFSET(reg, n) .cfi_offset reg, n
+# define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg
+# define CFI_DEF_CFA(reg, n) .cfi_def_cfa reg, n
+# define CFI_RESTORE(reg) .cfi_restore reg
+
+#else  // No CFI
+# define CFI_INL_ADJUST_CFA_OFFSET(n)
+# define CFI_STARTPROC
+# define CFI_ENDPROC
+# define CFI_ADJUST_CFA_OFFSET(n)
+# define CFI_DEF_CFA_OFFSET(n)
+# define CFI_REL_OFFSET(reg, n)
+# define CFI_OFFSET(reg, n)
+# define CFI_DEF_CFA_REGISTER(reg)
+# define CFI_DEF_CFA(reg, n)
+# define CFI_RESTORE(reg)
+#endif
diff --git a/lsan/include/sanitizer_common/sanitizer_atomic.h b/lsan/include/sanitizer_common/sanitizer_atomic.h
new file mode 100644 (file)
index 0000000..4973b7d
--- /dev/null
@@ -0,0 +1,80 @@
+//===-- sanitizer_atomic.h --------------------------------------*- C++ -*-===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ATOMIC_H
+#define SANITIZER_ATOMIC_H
+
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+enum memory_order {
+  memory_order_relaxed = 1 << 0,
+  memory_order_consume = 1 << 1,
+  memory_order_acquire = 1 << 2,
+  memory_order_release = 1 << 3,
+  memory_order_acq_rel = 1 << 4,
+  memory_order_seq_cst = 1 << 5
+};
+
+struct atomic_uint8_t {
+  typedef u8 Type;
+  volatile Type val_dont_use;
+};
+
+struct atomic_uint16_t {
+  typedef u16 Type;
+  volatile Type val_dont_use;
+};
+
+struct atomic_uint32_t {
+  typedef u32 Type;
+  volatile Type val_dont_use;
+};
+
+struct atomic_uint64_t {
+  typedef u64 Type;
+  // On 32-bit platforms u64 is not necessary aligned on 8 bytes.
+  volatile ALIGNED(8) Type val_dont_use;
+};
+
+struct atomic_uintptr_t {
+  typedef uptr Type;
+  volatile Type val_dont_use;
+};
+
+}  // namespace __sanitizer
+
+#if defined(__clang__) || defined(__GNUC__)
+# include "sanitizer_atomic_clang.h"
+#elif defined(_MSC_VER)
+# include "sanitizer_atomic_msvc.h"
+#else
+# error "Unsupported compiler"
+#endif
+
+namespace __sanitizer {
+
+// Clutter-reducing helpers.
+
+template<typename T>
+INLINE typename T::Type atomic_load_relaxed(const volatile T *a) {
+  return atomic_load(a, memory_order_relaxed);
+}
+
+template<typename T>
+INLINE void atomic_store_relaxed(volatile T *a, typename T::Type v) {
+  atomic_store(a, v, memory_order_relaxed);
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_ATOMIC_H
diff --git a/lsan/include/sanitizer_common/sanitizer_atomic_clang.h b/lsan/include/sanitizer_common/sanitizer_atomic_clang.h
new file mode 100644 (file)
index 0000000..c600999
--- /dev/null
@@ -0,0 +1,98 @@
+//===-- sanitizer_atomic_clang.h --------------------------------*- C++ -*-===//
+//
+// 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.
+// Not intended for direct inclusion. Include sanitizer_atomic.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ATOMIC_CLANG_H
+#define SANITIZER_ATOMIC_CLANG_H
+
+#if defined(__i386__) || defined(__x86_64__)
+# include "sanitizer_atomic_clang_x86.h"
+#else
+# include "sanitizer_atomic_clang_other.h"
+#endif
+
+namespace __sanitizer {
+
+// We would like to just use compiler builtin atomic operations
+// for loads and stores, but they are mostly broken in clang:
+// - they lead to vastly inefficient code generation
+// (http://llvm.org/bugs/show_bug.cgi?id=17281)
+// - 64-bit atomic operations are not implemented on x86_32
+// (http://llvm.org/bugs/show_bug.cgi?id=15034)
+// - they are not implemented on ARM
+// error: undefined reference to '__atomic_load_4'
+
+// See http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
+// for mappings of the memory model to different processors.
+
+INLINE void atomic_signal_fence(memory_order) {
+  __asm__ __volatile__("" ::: "memory");
+}
+
+INLINE void atomic_thread_fence(memory_order) {
+  __sync_synchronize();
+}
+
+template<typename T>
+INLINE typename T::Type atomic_fetch_add(volatile T *a,
+    typename T::Type v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  return __sync_fetch_and_add(&a->val_dont_use, v);
+}
+
+template<typename T>
+INLINE typename T::Type atomic_fetch_sub(volatile T *a,
+    typename T::Type v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  return __sync_fetch_and_add(&a->val_dont_use, -v);
+}
+
+template<typename T>
+INLINE typename T::Type atomic_exchange(volatile T *a,
+    typename T::Type v, memory_order mo) {
+  DCHECK(!((uptr)a % sizeof(*a)));
+  if (mo & (memory_order_release | memory_order_acq_rel | memory_order_seq_cst))
+    __sync_synchronize();
+  v = __sync_lock_test_and_set(&a->val_dont_use, v);
+  if (mo == memory_order_seq_cst)
+    __sync_synchronize();
+  return v;
+}
+
+template<typename T>
+INLINE bool atomic_compare_exchange_strong(volatile T *a,
+                                           typename T::Type *cmp,
+                                           typename T::Type xchg,
+                                           memory_order mo) {
+  typedef typename T::Type Type;
+  Type cmpv = *cmp;
+  Type prev = __sync_val_compare_and_swap(&a->val_dont_use, cmpv, xchg);
+  if (prev == cmpv)
+    return true;
+  *cmp = prev;
+  return false;
+}
+
+template<typename T>
+INLINE bool atomic_compare_exchange_weak(volatile T *a,
+                                         typename T::Type *cmp,
+                                         typename T::Type xchg,
+                                         memory_order mo) {
+  return atomic_compare_exchange_strong(a, cmp, xchg, mo);
+}
+
+}  // namespace __sanitizer
+
+#undef ATOMIC_ORDER
+
+#endif  // SANITIZER_ATOMIC_CLANG_H
diff --git a/lsan/include/sanitizer_common/sanitizer_atomic_clang_other.h b/lsan/include/sanitizer_common/sanitizer_atomic_clang_other.h
new file mode 100644 (file)
index 0000000..c66c099
--- /dev/null
@@ -0,0 +1,95 @@
+//===-- sanitizer_atomic_clang_other.h --------------------------*- C++ -*-===//
+//
+// 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.
+// Not intended for direct inclusion. Include sanitizer_atomic.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
+#define SANITIZER_ATOMIC_CLANG_OTHER_H
+
+namespace __sanitizer {
+
+INLINE void proc_yield(int cnt) {
+  __asm__ __volatile__("" ::: "memory");
+}
+
+template<typename T>
+INLINE typename T::Type atomic_load(
+    const volatile T *a, memory_order mo) {
+  DCHECK(mo & (memory_order_relaxed | memory_order_consume
+      | memory_order_acquire | memory_order_seq_cst));
+  DCHECK(!((uptr)a % sizeof(*a)));
+  typename T::Type v;
+
+  if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+    // Assume that aligned loads are atomic.
+    if (mo == memory_order_relaxed) {
+      v = a->val_dont_use;
+    } else if (mo == memory_order_consume) {
+      // Assume that processor respects data dependencies
+      // (and that compiler won't break them).
+      __asm__ __volatile__("" ::: "memory");
+      v = a->val_dont_use;
+      __asm__ __volatile__("" ::: "memory");
+    } else if (mo == memory_order_acquire) {
+      __asm__ __volatile__("" ::: "memory");
+      v = a->val_dont_use;
+      __sync_synchronize();
+    } else {  // seq_cst
+      // E.g. on POWER we need a hw fence even before the store.
+      __sync_synchronize();
+      v = a->val_dont_use;
+      __sync_synchronize();
+    }
+  } else {
+    // 64-bit load on 32-bit platform.
+    // Gross, but simple and reliable.
+    // Assume that it is not in read-only memory.
+    v = __sync_fetch_and_add(
+        const_cast<typename T::Type volatile *>(&a->val_dont_use), 0);
+  }
+  return v;
+}
+
+template<typename T>
+INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
+  DCHECK(mo & (memory_order_relaxed | memory_order_release
+      | memory_order_seq_cst));
+  DCHECK(!((uptr)a % sizeof(*a)));
+
+  if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+    // Assume that aligned loads are atomic.
+    if (mo == memory_order_relaxed) {
+      a->val_dont_use = v;
+    } else if (mo == memory_order_release) {
+      __sync_synchronize();
+      a->val_dont_use = v;
+      __asm__ __volatile__("" ::: "memory");
+    } else {  // seq_cst
+      __sync_synchronize();
+      a->val_dont_use = v;
+      __sync_synchronize();
+    }
+  } else {
+    // 64-bit store on 32-bit platform.
+    // Gross, but simple and reliable.
+    typename T::Type cmp = a->val_dont_use;
+    typename T::Type cur;
+    for (;;) {
+      cur = __sync_val_compare_and_swap(&a->val_dont_use, cmp, v);
+      if (cmp == v)
+        break;
+      cmp = cur;
+    }
+  }
+}
+
+}  // namespace __sanitizer
+
+#endif  // #ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
diff --git a/lsan/include/sanitizer_common/sanitizer_atomic_clang_x86.h b/lsan/include/sanitizer_common/sanitizer_atomic_clang_x86.h
new file mode 100644 (file)
index 0000000..5df210e
--- /dev/null
@@ -0,0 +1,114 @@
+//===-- sanitizer_atomic_clang_x86.h ----------------------------*- C++ -*-===//
+//
+// 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.
+// Not intended for direct inclusion. Include sanitizer_atomic.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ATOMIC_CLANG_X86_H
+#define SANITIZER_ATOMIC_CLANG_X86_H
+
+namespace __sanitizer {
+
+INLINE void proc_yield(int cnt) {
+  __asm__ __volatile__("" ::: "memory");
+  for (int i = 0; i < cnt; i++)
+    __asm__ __volatile__("pause");
+  __asm__ __volatile__("" ::: "memory");
+}
+
+template<typename T>
+INLINE typename T::Type atomic_load(
+    const volatile T *a, memory_order mo) {
+  DCHECK(mo & (memory_order_relaxed | memory_order_consume
+      | memory_order_acquire | memory_order_seq_cst));
+  DCHECK(!((uptr)a % sizeof(*a)));
+  typename T::Type v;
+
+  if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+    // Assume that aligned loads are atomic.
+    if (mo == memory_order_relaxed) {
+      v = a->val_dont_use;
+    } else if (mo == memory_order_consume) {
+      // Assume that processor respects data dependencies
+      // (and that compiler won't break them).
+      __asm__ __volatile__("" ::: "memory");
+      v = a->val_dont_use;
+      __asm__ __volatile__("" ::: "memory");
+    } else if (mo == memory_order_acquire) {
+      __asm__ __volatile__("" ::: "memory");
+      v = a->val_dont_use;
+      // On x86 loads are implicitly acquire.
+      __asm__ __volatile__("" ::: "memory");
+    } else {  // seq_cst
+      // On x86 plain MOV is enough for seq_cst store.
+      __asm__ __volatile__("" ::: "memory");
+      v = a->val_dont_use;
+      __asm__ __volatile__("" ::: "memory");
+    }
+  } else {
+    // 64-bit load on 32-bit platform.
+    __asm__ __volatile__(
+        "movq %1, %%mm0;"  // Use mmx reg for 64-bit atomic moves
+        "movq %%mm0, %0;"  // (ptr could be read-only)
+        "emms;"            // Empty mmx state/Reset FP regs
+        : "=m" (v)
+        : "m" (a->val_dont_use)
+        : // mark the FP stack and mmx registers as clobbered
+          "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
+#ifdef __MMX__
+          "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+#endif  // #ifdef __MMX__
+          "memory");
+  }
+  return v;
+}
+
+template<typename T>
+INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
+  DCHECK(mo & (memory_order_relaxed | memory_order_release
+      | memory_order_seq_cst));
+  DCHECK(!((uptr)a % sizeof(*a)));
+
+  if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+    // Assume that aligned loads are atomic.
+    if (mo == memory_order_relaxed) {
+      a->val_dont_use = v;
+    } else if (mo == memory_order_release) {
+      // On x86 stores are implicitly release.
+      __asm__ __volatile__("" ::: "memory");
+      a->val_dont_use = v;
+      __asm__ __volatile__("" ::: "memory");
+    } else {  // seq_cst
+      // On x86 stores are implicitly release.
+      __asm__ __volatile__("" ::: "memory");
+      a->val_dont_use = v;
+      __sync_synchronize();
+    }
+  } else {
+    // 64-bit store on 32-bit platform.
+    __asm__ __volatile__(
+        "movq %1, %%mm0;"  // Use mmx reg for 64-bit atomic moves
+        "movq %%mm0, %0;"
+        "emms;"            // Empty mmx state/Reset FP regs
+        : "=m" (a->val_dont_use)
+        : "m" (v)
+        : // mark the FP stack and mmx registers as clobbered
+          "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
+#ifdef __MMX__
+          "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+#endif  // #ifdef __MMX__
+          "memory");
+    if (mo == memory_order_seq_cst)
+      __sync_synchronize();
+  }
+}
+
+}  // namespace __sanitizer
+
+#endif  // #ifndef SANITIZER_ATOMIC_CLANG_X86_H
diff --git a/lsan/include/sanitizer_common/sanitizer_atomic_msvc.h b/lsan/include/sanitizer_common/sanitizer_atomic_msvc.h
new file mode 100644 (file)
index 0000000..4ac3b90
--- /dev/null
@@ -0,0 +1,259 @@
+//===-- sanitizer_atomic_msvc.h ---------------------------------*- C++ -*-===//
+//
+// 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.
+// Not intended for direct inclusion. Include sanitizer_atomic.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ATOMIC_MSVC_H
+#define SANITIZER_ATOMIC_MSVC_H
+
+extern "C" void _ReadWriteBarrier();
+#pragma intrinsic(_ReadWriteBarrier)
+extern "C" void _mm_mfence();
+#pragma intrinsic(_mm_mfence)
+extern "C" void _mm_pause();
+#pragma intrinsic(_mm_pause)
+extern "C" char _InterlockedExchange8(   // NOLINT
+    char volatile *Addend, char Value);  // NOLINT
+#pragma intrinsic(_InterlockedExchange8)
+extern "C" short _InterlockedExchange16(   // NOLINT
+    short volatile *Addend, short Value);  // NOLINT
+#pragma intrinsic(_InterlockedExchange16)
+extern "C" long _InterlockedExchange(    // NOLINT
+    long volatile *Addend, long Value);  // NOLINT
+#pragma intrinsic(_InterlockedExchange)
+extern "C" long _InterlockedExchangeAdd(  // NOLINT
+    long volatile * Addend, long Value);  // NOLINT
+#pragma intrinsic(_InterlockedExchangeAdd)
+extern "C" short _InterlockedCompareExchange16(  // NOLINT
+    short volatile *Destination,                 // NOLINT
+    short Exchange, short Comparand);            // NOLINT
+#pragma intrinsic(_InterlockedCompareExchange16)
+extern "C"
+long long _InterlockedCompareExchange64(  // NOLINT
+    long long volatile *Destination,              // NOLINT
+    long long Exchange, long long Comparand);     // NOLINT
+#pragma intrinsic(_InterlockedCompareExchange64)
+extern "C" void *_InterlockedCompareExchangePointer(
+    void *volatile *Destination,
+    void *Exchange, void *Comparand);
+#pragma intrinsic(_InterlockedCompareExchangePointer)
+extern "C"
+long __cdecl _InterlockedCompareExchange(  // NOLINT
+    long volatile *Destination,            // NOLINT
+    long Exchange, long Comparand);        // NOLINT
+#pragma intrinsic(_InterlockedCompareExchange)
+
+#ifdef _WIN64
+extern "C" long long _InterlockedExchangeAdd64(     // NOLINT
+    long long volatile * Addend, long long Value);  // NOLINT
+#pragma intrinsic(_InterlockedExchangeAdd64)
+#endif
+
+namespace __sanitizer {
+
+INLINE void atomic_signal_fence(memory_order) {
+  _ReadWriteBarrier();
+}
+
+INLINE void atomic_thread_fence(memory_order) {
+  _mm_mfence();
+}
+
+INLINE void proc_yield(int cnt) {
+  for (int i = 0; i < cnt; i++)
+    _mm_pause();
+}
+
+template<typename T>
+INLINE typename T::Type atomic_load(
+    const volatile T *a, memory_order mo) {
+  DCHECK(mo & (memory_order_relaxed | memory_order_consume
+      | memory_order_acquire | memory_order_seq_cst));
+  DCHECK(!((uptr)a % sizeof(*a)));
+  typename T::Type v;
+  // FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
+  if (mo == memory_order_relaxed) {
+    v = a->val_dont_use;
+  } else {
+    atomic_signal_fence(memory_order_seq_cst);
+    v = a->val_dont_use;
+    atomic_signal_fence(memory_order_seq_cst);
+  }
+  return v;
+}
+
+template<typename T>
+INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
+  DCHECK(mo & (memory_order_relaxed | memory_order_release
+      | memory_order_seq_cst));
+  DCHECK(!((uptr)a % sizeof(*a)));
+  // FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
+  if (mo == memory_order_relaxed) {
+    a->val_dont_use = v;
+  } else {
+    atomic_signal_fence(memory_order_seq_cst);
+    a->val_dont_use = v;
+    atomic_signal_fence(memory_order_seq_cst);
+  }
+  if (mo == memory_order_seq_cst)
+    atomic_thread_fence(memory_order_seq_cst);
+}
+
+INLINE u32 atomic_fetch_add(volatile atomic_uint32_t *a,
+    u32 v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  return (u32)_InterlockedExchangeAdd(
+      (volatile long*)&a->val_dont_use, (long)v);  // NOLINT
+}
+
+INLINE uptr atomic_fetch_add(volatile atomic_uintptr_t *a,
+    uptr v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+#ifdef _WIN64
+  return (uptr)_InterlockedExchangeAdd64(
+      (volatile long long*)&a->val_dont_use, (long long)v);  // NOLINT
+#else
+  return (uptr)_InterlockedExchangeAdd(
+      (volatile long*)&a->val_dont_use, (long)v);  // NOLINT
+#endif
+}
+
+INLINE u32 atomic_fetch_sub(volatile atomic_uint32_t *a,
+    u32 v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  return (u32)_InterlockedExchangeAdd(
+      (volatile long*)&a->val_dont_use, -(long)v);  // NOLINT
+}
+
+INLINE uptr atomic_fetch_sub(volatile atomic_uintptr_t *a,
+    uptr v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+#ifdef _WIN64
+  return (uptr)_InterlockedExchangeAdd64(
+      (volatile long long*)&a->val_dont_use, -(long long)v);  // NOLINT
+#else
+  return (uptr)_InterlockedExchangeAdd(
+      (volatile long*)&a->val_dont_use, -(long)v);  // NOLINT
+#endif
+}
+
+INLINE u8 atomic_exchange(volatile atomic_uint8_t *a,
+    u8 v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v);
+}
+
+INLINE u16 atomic_exchange(volatile atomic_uint16_t *a,
+    u16 v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v);
+}
+
+INLINE u32 atomic_exchange(volatile atomic_uint32_t *a,
+    u32 v, memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v);
+}
+
+#ifndef _WIN64
+
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
+                                           u8 *cmp,
+                                           u8 xchgv,
+                                           memory_order mo) {
+  (void)mo;
+  DCHECK(!((uptr)a % sizeof(*a)));
+  u8 cmpv = *cmp;
+  u8 prev;
+  __asm {
+    mov al, cmpv
+    mov ecx, a
+    mov dl, xchgv
+    lock cmpxchg [ecx], dl
+    mov prev, al
+  }
+  if (prev == cmpv)
+    return true;
+  *cmp = prev;
+  return false;
+}
+
+#endif
+
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
+                                           uptr *cmp,
+                                           uptr xchg,
+                                           memory_order mo) {
+  uptr cmpv = *cmp;
+  uptr prev = (uptr)_InterlockedCompareExchangePointer(
+      (void*volatile*)&a->val_dont_use, (void*)xchg, (void*)cmpv);
+  if (prev == cmpv)
+    return true;
+  *cmp = prev;
+  return false;
+}
+
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uint16_t *a,
+                                           u16 *cmp,
+                                           u16 xchg,
+                                           memory_order mo) {
+  u16 cmpv = *cmp;
+  u16 prev = (u16)_InterlockedCompareExchange16(
+      (volatile short*)&a->val_dont_use, (short)xchg, (short)cmpv);
+  if (prev == cmpv)
+    return true;
+  *cmp = prev;
+  return false;
+}
+
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uint32_t *a,
+                                           u32 *cmp,
+                                           u32 xchg,
+                                           memory_order mo) {
+  u32 cmpv = *cmp;
+  u32 prev = (u32)_InterlockedCompareExchange(
+      (volatile long*)&a->val_dont_use, (long)xchg, (long)cmpv);
+  if (prev == cmpv)
+    return true;
+  *cmp = prev;
+  return false;
+}
+
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uint64_t *a,
+                                           u64 *cmp,
+                                           u64 xchg,
+                                           memory_order mo) {
+  u64 cmpv = *cmp;
+  u64 prev = (u64)_InterlockedCompareExchange64(
+      (volatile long long*)&a->val_dont_use, (long long)xchg, (long long)cmpv);
+  if (prev == cmpv)
+    return true;
+  *cmp = prev;
+  return false;
+}
+
+template<typename T>
+INLINE bool atomic_compare_exchange_weak(volatile T *a,
+                                         typename T::Type *cmp,
+                                         typename T::Type xchg,
+                                         memory_order mo) {
+  return atomic_compare_exchange_strong(a, cmp, xchg, mo);
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_ATOMIC_CLANG_H
diff --git a/lsan/include/sanitizer_common/sanitizer_bitvector.h b/lsan/include/sanitizer_common/sanitizer_bitvector.h
new file mode 100644 (file)
index 0000000..bb2872f
--- /dev/null
@@ -0,0 +1,349 @@
+//===-- sanitizer_bitvector.h -----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Specializer BitVector implementation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_BITVECTOR_H
+#define SANITIZER_BITVECTOR_H
+
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+// Fixed size bit vector based on a single basic integer.
+template <class basic_int_t = uptr>
+class BasicBitVector {
+ public:
+  enum SizeEnum { kSize = sizeof(basic_int_t) * 8 };
+
+  uptr size() const { return kSize; }
+  // No CTOR.
+  void clear() { bits_ = 0; }
+  void setAll() { bits_ = ~(basic_int_t)0; }
+  bool empty() const { return bits_ == 0; }
+
+  // Returns true if the bit has changed from 0 to 1.
+  bool setBit(uptr idx) {
+    basic_int_t old = bits_;
+    bits_ |= mask(idx);
+    return bits_ != old;
+  }
+
+  // Returns true if the bit has changed from 1 to 0.
+  bool clearBit(uptr idx) {
+    basic_int_t old = bits_;
+    bits_ &= ~mask(idx);
+    return bits_ != old;
+  }
+
+  bool getBit(uptr idx) const { return (bits_ & mask(idx)) != 0; }
+
+  uptr getAndClearFirstOne() {
+    CHECK(!empty());
+    uptr idx = LeastSignificantSetBitIndex(bits_);
+    clearBit(idx);
+    return idx;
+  }
+
+  // Do "this |= v" and return whether new bits have been added.
+  bool setUnion(const BasicBitVector &v) {
+    basic_int_t old = bits_;
+    bits_ |= v.bits_;
+    return bits_ != old;
+  }
+
+  // Do "this &= v" and return whether any bits have been removed.
+  bool setIntersection(const BasicBitVector &v) {
+    basic_int_t old = bits_;
+    bits_ &= v.bits_;
+    return bits_ != old;
+  }
+
+  // Do "this &= ~v" and return whether any bits have been removed.
+  bool setDifference(const BasicBitVector &v) {
+    basic_int_t old = bits_;
+    bits_ &= ~v.bits_;
+    return bits_ != old;
+  }
+
+  void copyFrom(const BasicBitVector &v) { bits_ = v.bits_; }
+
+  // Returns true if 'this' intersects with 'v'.
+  bool intersectsWith(const BasicBitVector &v) const {
+    return (bits_ & v.bits_) != 0;
+  }
+
+  // for (BasicBitVector<>::Iterator it(bv); it.hasNext();) {
+  //   uptr idx = it.next();
+  //   use(idx);
+  // }
+  class Iterator {
+   public:
+    Iterator() { }
+    explicit Iterator(const BasicBitVector &bv) : bv_(bv) {}
+    bool hasNext() const { return !bv_.empty(); }
+    uptr next() { return bv_.getAndClearFirstOne(); }
+    void clear() { bv_.clear(); }
+   private:
+    BasicBitVector bv_;
+  };
+
+ private:
+  basic_int_t mask(uptr idx) const {
+    CHECK_LT(idx, size());
+    return (basic_int_t)1UL << idx;
+  }
+  basic_int_t bits_;
+};
+
+// Fixed size bit vector of (kLevel1Size*BV::kSize**2) bits.
+// The implementation is optimized for better performance on
+// sparse bit vectors, i.e. the those with few set bits.
+template <uptr kLevel1Size = 1, class BV = BasicBitVector<> >
+class TwoLevelBitVector {
+  // This is essentially a 2-level bit vector.
+  // Set bit in the first level BV indicates that there are set bits
+  // in the corresponding BV of the second level.
+  // This structure allows O(kLevel1Size) time for clear() and empty(),
+  // as well fast handling of sparse BVs.
+ public:
+  enum SizeEnum { kSize = BV::kSize * BV::kSize * kLevel1Size };
+  // No CTOR.
+
+  uptr size() const { return kSize; }
+
+  void clear() {
+    for (uptr i = 0; i < kLevel1Size; i++)
+      l1_[i].clear();
+  }
+
+  void setAll() {
+    for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+      l1_[i0].setAll();
+      for (uptr i1 = 0; i1 < BV::kSize; i1++)
+        l2_[i0][i1].setAll();
+    }
+  }
+
+  bool empty() const {
+    for (uptr i = 0; i < kLevel1Size; i++)
+      if (!l1_[i].empty())
+        return false;
+    return true;
+  }
+
+  // Returns true if the bit has changed from 0 to 1.
+  bool setBit(uptr idx) {
+    check(idx);
+    uptr i0 = idx0(idx);
+    uptr i1 = idx1(idx);
+    uptr i2 = idx2(idx);
+    if (!l1_[i0].getBit(i1)) {
+      l1_[i0].setBit(i1);
+      l2_[i0][i1].clear();
+    }
+    bool res = l2_[i0][i1].setBit(i2);
+    // Printf("%s: %zd => %zd %zd %zd; %d\n", __func__,
+    // idx, i0, i1, i2, res);
+    return res;
+  }
+
+  bool clearBit(uptr idx) {
+    check(idx);
+    uptr i0 = idx0(idx);
+    uptr i1 = idx1(idx);
+    uptr i2 = idx2(idx);
+    bool res = false;
+    if (l1_[i0].getBit(i1)) {
+      res = l2_[i0][i1].clearBit(i2);
+      if (l2_[i0][i1].empty())
+        l1_[i0].clearBit(i1);
+    }
+    return res;
+  }
+
+  bool getBit(uptr idx) const {
+    check(idx);
+    uptr i0 = idx0(idx);
+    uptr i1 = idx1(idx);
+    uptr i2 = idx2(idx);
+    // Printf("%s: %zd => %zd %zd %zd\n", __func__, idx, i0, i1, i2);
+    return l1_[i0].getBit(i1) && l2_[i0][i1].getBit(i2);
+  }
+
+  uptr getAndClearFirstOne() {
+    for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+      if (l1_[i0].empty()) continue;
+      uptr i1 = l1_[i0].getAndClearFirstOne();
+      uptr i2 = l2_[i0][i1].getAndClearFirstOne();
+      if (!l2_[i0][i1].empty())
+        l1_[i0].setBit(i1);
+      uptr res = i0 * BV::kSize * BV::kSize + i1 * BV::kSize + i2;
+      // Printf("getAndClearFirstOne: %zd %zd %zd => %zd\n", i0, i1, i2, res);
+      return res;
+    }
+    CHECK(0);
+    return 0;
+  }
+
+  // Do "this |= v" and return whether new bits have been added.
+  bool setUnion(const TwoLevelBitVector &v) {
+    bool res = false;
+    for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+      BV t = v.l1_[i0];
+      while (!t.empty()) {
+        uptr i1 = t.getAndClearFirstOne();
+        if (l1_[i0].setBit(i1))
+          l2_[i0][i1].clear();
+        if (l2_[i0][i1].setUnion(v.l2_[i0][i1]))
+          res = true;
+      }
+    }
+    return res;
+  }
+
+  // Do "this &= v" and return whether any bits have been removed.
+  bool setIntersection(const TwoLevelBitVector &v) {
+    bool res = false;
+    for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+      if (l1_[i0].setIntersection(v.l1_[i0]))
+        res = true;
+      if (!l1_[i0].empty()) {
+        BV t = l1_[i0];
+        while (!t.empty()) {
+          uptr i1 = t.getAndClearFirstOne();
+          if (l2_[i0][i1].setIntersection(v.l2_[i0][i1]))
+            res = true;
+          if (l2_[i0][i1].empty())
+            l1_[i0].clearBit(i1);
+        }
+      }
+    }
+    return res;
+  }
+
+  // Do "this &= ~v" and return whether any bits have been removed.
+  bool setDifference(const TwoLevelBitVector &v) {
+    bool res = false;
+    for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+      BV t = l1_[i0];
+      t.setIntersection(v.l1_[i0]);
+      while (!t.empty()) {
+        uptr i1 = t.getAndClearFirstOne();
+        if (l2_[i0][i1].setDifference(v.l2_[i0][i1]))
+          res = true;
+        if (l2_[i0][i1].empty())
+          l1_[i0].clearBit(i1);
+      }
+    }
+    return res;
+  }
+
+  void copyFrom(const TwoLevelBitVector &v) {
+    clear();
+    setUnion(v);
+  }
+
+  // Returns true if 'this' intersects with 'v'.
+  bool intersectsWith(const TwoLevelBitVector &v) const {
+    for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+      BV t = l1_[i0];
+      t.setIntersection(v.l1_[i0]);
+      while (!t.empty()) {
+        uptr i1 = t.getAndClearFirstOne();
+        if (!v.l1_[i0].getBit(i1)) continue;
+        if (l2_[i0][i1].intersectsWith(v.l2_[i0][i1]))
+          return true;
+      }
+    }
+    return false;
+  }
+
+  // for (TwoLevelBitVector<>::Iterator it(bv); it.hasNext();) {
+  //   uptr idx = it.next();
+  //   use(idx);
+  // }
+  class Iterator {
+   public:
+    Iterator() { }
+    explicit Iterator(const TwoLevelBitVector &bv) : bv_(bv), i0_(0), i1_(0) {
+      it1_.clear();
+      it2_.clear();
+    }
+
+    bool hasNext() const {
+      if (it1_.hasNext()) return true;
+      for (uptr i = i0_; i < kLevel1Size; i++)
+        if (!bv_.l1_[i].empty()) return true;
+      return false;
+    }
+
+    uptr next() {
+      // Printf("++++: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
+      //       it2_.hasNext(), kSize);
+      if (!it1_.hasNext() && !it2_.hasNext()) {
+        for (; i0_ < kLevel1Size; i0_++) {
+          if (bv_.l1_[i0_].empty()) continue;
+          it1_ = typename BV::Iterator(bv_.l1_[i0_]);
+          // Printf("+i0: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
+          //   it2_.hasNext(), kSize);
+          break;
+        }
+      }
+      if (!it2_.hasNext()) {
+        CHECK(it1_.hasNext());
+        i1_ = it1_.next();
+        it2_ = typename BV::Iterator(bv_.l2_[i0_][i1_]);
+        // Printf("++i1: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
+        //       it2_.hasNext(), kSize);
+      }
+      CHECK(it2_.hasNext());
+      uptr i2 = it2_.next();
+      uptr res = i0_ * BV::kSize * BV::kSize + i1_ * BV::kSize + i2;
+      // Printf("+ret: %zd %zd; %d %d; size %zd; res: %zd\n", i0_, i1_,
+      //       it1_.hasNext(), it2_.hasNext(), kSize, res);
+      if (!it1_.hasNext() && !it2_.hasNext())
+        i0_++;
+      return res;
+    }
+
+   private:
+    const TwoLevelBitVector &bv_;
+    uptr i0_, i1_;
+    typename BV::Iterator it1_, it2_;
+  };
+
+ private:
+  void check(uptr idx) const { CHECK_LE(idx, size()); }
+
+  uptr idx0(uptr idx) const {
+    uptr res = idx / (BV::kSize * BV::kSize);
+    CHECK_LE(res, kLevel1Size);
+    return res;
+  }
+
+  uptr idx1(uptr idx) const {
+    uptr res = (idx / BV::kSize) % BV::kSize;
+    CHECK_LE(res, BV::kSize);
+    return res;
+  }
+
+  uptr idx2(uptr idx) const {
+    uptr res = idx % BV::kSize;
+    CHECK_LE(res, BV::kSize);
+    return res;
+  }
+
+  BV l1_[kLevel1Size];
+  BV l2_[kLevel1Size][BV::kSize];
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_BITVECTOR_H
diff --git a/lsan/include/sanitizer_common/sanitizer_bvgraph.h b/lsan/include/sanitizer_common/sanitizer_bvgraph.h
new file mode 100644 (file)
index 0000000..6ef0e81
--- /dev/null
@@ -0,0 +1,163 @@
+//===-- sanitizer_bvgraph.h -------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime.
+// BVGraph -- a directed graph.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_BVGRAPH_H
+#define SANITIZER_BVGRAPH_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_bitvector.h"
+
+namespace __sanitizer {
+
+// Directed graph of fixed size implemented as an array of bit vectors.
+// Not thread-safe, all accesses should be protected by an external lock.
+template<class BV>
+class BVGraph {
+ public:
+  enum SizeEnum { kSize = BV::kSize };
+  uptr size() const { return kSize; }
+  // No CTOR.
+  void clear() {
+    for (uptr i = 0; i < size(); i++)
+      v[i].clear();
+  }
+
+  bool empty() const {
+    for (uptr i = 0; i < size(); i++)
+      if (!v[i].empty())
+        return false;
+    return true;
+  }
+
+  // Returns true if a new edge was added.
+  bool addEdge(uptr from, uptr to) {
+    check(from, to);
+    return v[from].setBit(to);
+  }
+
+  // Returns true if at least one new edge was added.
+  uptr addEdges(const BV &from, uptr to, uptr added_edges[],
+                uptr max_added_edges) {
+    uptr res = 0;
+    t1.copyFrom(from);
+    while (!t1.empty()) {
+      uptr node = t1.getAndClearFirstOne();
+      if (v[node].setBit(to))
+        if (res < max_added_edges)
+          added_edges[res++] = node;
+    }
+    return res;
+  }
+
+  // *EXPERIMENTAL*
+  // Returns true if an edge from=>to exist.
+  // This function does not use any global state except for 'this' itself,
+  // and thus can be called from different threads w/o locking.
+  // This would be racy.
+  // FIXME: investigate how much we can prove about this race being "benign".
+  bool hasEdge(uptr from, uptr to) { return v[from].getBit(to); }
+
+  // Returns true if the edge from=>to was removed.
+  bool removeEdge(uptr from, uptr to) {
+    return v[from].clearBit(to);
+  }
+
+  // Returns true if at least one edge *=>to was removed.
+  bool removeEdgesTo(const BV &to) {
+    bool res = 0;
+    for (uptr from = 0; from < size(); from++) {
+      if (v[from].setDifference(to))
+        res = true;
+    }
+    return res;
+  }
+
+  // Returns true if at least one edge from=>* was removed.
+  bool removeEdgesFrom(const BV &from) {
+    bool res = false;
+    t1.copyFrom(from);
+    while (!t1.empty()) {
+      uptr idx = t1.getAndClearFirstOne();
+      if (!v[idx].empty()) {
+        v[idx].clear();
+        res = true;
+      }
+    }
+    return res;
+  }
+
+  void removeEdgesFrom(uptr from) {
+    return v[from].clear();
+  }
+
+  bool hasEdge(uptr from, uptr to) const {
+    check(from, to);
+    return v[from].getBit(to);
+  }
+
+  // Returns true if there is a path from the node 'from'
+  // to any of the nodes in 'targets'.
+  bool isReachable(uptr from, const BV &targets) {
+    BV &to_visit = t1,
+       &visited = t2;
+    to_visit.copyFrom(v[from]);
+    visited.clear();
+    visited.setBit(from);
+    while (!to_visit.empty()) {
+      uptr idx = to_visit.getAndClearFirstOne();
+      if (visited.setBit(idx))
+        to_visit.setUnion(v[idx]);
+    }
+    return targets.intersectsWith(visited);
+  }
+
+  // Finds a path from 'from' to one of the nodes in 'target',
+  // stores up to 'path_size' items of the path into 'path',
+  // returns the path length, or 0 if there is no path of size 'path_size'.
+  uptr findPath(uptr from, const BV &targets, uptr *path, uptr path_size) {
+    if (path_size == 0)
+      return 0;
+    path[0] = from;
+    if (targets.getBit(from))
+      return 1;
+    // The function is recursive, so we don't want to create BV on stack.
+    // Instead of a getAndClearFirstOne loop we use the slower iterator.
+    for (typename BV::Iterator it(v[from]); it.hasNext(); ) {
+      uptr idx = it.next();
+      if (uptr res = findPath(idx, targets, path + 1, path_size - 1))
+        return res + 1;
+    }
+    return 0;
+  }
+
+  // Same as findPath, but finds a shortest path.
+  uptr findShortestPath(uptr from, const BV &targets, uptr *path,
+                        uptr path_size) {
+    for (uptr p = 1; p <= path_size; p++)
+      if (findPath(from, targets, path, p) == p)
+        return p;
+    return 0;
+  }
+
+ private:
+  void check(uptr idx1, uptr idx2) const {
+    CHECK_LT(idx1, size());
+    CHECK_LT(idx2, size());
+  }
+  BV v[kSize];
+  // Keep temporary vectors here since we can not create large objects on stack.
+  BV t1, t2;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_BVGRAPH_H
diff --git a/lsan/include/sanitizer_common/sanitizer_common.h b/lsan/include/sanitizer_common/sanitizer_common.h
new file mode 100644 (file)
index 0000000..1550154
--- /dev/null
@@ -0,0 +1,727 @@
+//===-- sanitizer_common.h --------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between run-time libraries of sanitizers.
+//
+// It declares common functions and classes that are used in both runtimes.
+// Implementation of some functions are provided in sanitizer_common, while
+// others must be defined by run-time library itself.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_COMMON_H
+#define SANITIZER_COMMON_H
+
+#include "sanitizer_flags.h"
+#include "sanitizer_interface_internal.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
+#include "sanitizer_mutex.h"
+
+#ifdef _MSC_VER
+extern "C" void _ReadWriteBarrier();
+#pragma intrinsic(_ReadWriteBarrier)
+#endif
+
+namespace __sanitizer {
+struct StackTrace;
+struct AddressInfo;
+
+// Constants.
+const uptr kWordSize = SANITIZER_WORDSIZE / 8;
+const uptr kWordSizeInBits = 8 * kWordSize;
+
+#if defined(__powerpc__) || defined(__powerpc64__)
+  const uptr kCacheLineSize = 128;
+#else
+  const uptr kCacheLineSize = 64;
+#endif
+
+const uptr kMaxPathLength = 4096;
+
+// 16K loaded modules should be enough for everyone.
+static const uptr kMaxNumberOfModules = 1 << 14;
+
+const uptr kMaxThreadStackSize = 1 << 30;  // 1Gb
+
+// Denotes fake PC values that come from JIT/JAVA/etc.
+// For such PC values __tsan_symbolize_external() will be called.
+const u64 kExternalPCBit = 1ULL << 60;
+
+extern const char *SanitizerToolName;  // Can be changed by the tool.
+
+extern atomic_uint32_t current_verbosity;
+INLINE void SetVerbosity(int verbosity) {
+  atomic_store(&current_verbosity, verbosity, memory_order_relaxed);
+}
+INLINE int Verbosity() {
+  return atomic_load(&current_verbosity, memory_order_relaxed);
+}
+
+uptr GetPageSize();
+uptr GetPageSizeCached();
+uptr GetMmapGranularity();
+uptr GetMaxVirtualAddress();
+// Threads
+uptr GetTid();
+uptr GetThreadSelf();
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+                                uptr *stack_bottom);
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+                          uptr *tls_addr, uptr *tls_size);
+
+// Memory management
+void *MmapOrDie(uptr size, const char *mem_type);
+void UnmapOrDie(void *addr, uptr size);
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
+                         const char *name = nullptr);
+void *MmapNoReserveOrDie(uptr size, const char *mem_type);
+void *MmapFixedOrDie(uptr fixed_addr, uptr size);
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr);
+// Map aligned chunk of address space; size and alignment are powers of two.
+void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
+// Disallow access to a memory range.  Use MmapNoAccess to allocate an
+// unaccessible memory.
+bool MprotectNoAccess(uptr addr, uptr size);
+
+// Used to check if we can map shadow memory to a fixed location.
+bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
+void FlushUnneededShadowMemory(uptr addr, uptr size);
+void IncreaseTotalMmap(uptr size);
+void DecreaseTotalMmap(uptr size);
+uptr GetRSS();
+void NoHugePagesInRegion(uptr addr, uptr length);
+void DontDumpShadowMemory(uptr addr, uptr length);
+// Check if the built VMA size matches the runtime one.
+void CheckVMASize();
+
+// InternalScopedBuffer can be used instead of large stack arrays to
+// keep frame size low.
+// FIXME: use InternalAlloc instead of MmapOrDie once
+// InternalAlloc is made libc-free.
+template<typename T>
+class InternalScopedBuffer {
+ public:
+  explicit InternalScopedBuffer(uptr cnt) {
+    cnt_ = cnt;
+    ptr_ = (T*)MmapOrDie(cnt * sizeof(T), "InternalScopedBuffer");
+  }
+  ~InternalScopedBuffer() {
+    UnmapOrDie(ptr_, cnt_ * sizeof(T));
+  }
+  T &operator[](uptr i) { return ptr_[i]; }
+  T *data() { return ptr_; }
+  uptr size() { return cnt_ * sizeof(T); }
+
+ private:
+  T *ptr_;
+  uptr cnt_;
+  // Disallow evil constructors.
+  InternalScopedBuffer(const InternalScopedBuffer&);
+  void operator=(const InternalScopedBuffer&);
+};
+
+class InternalScopedString : public InternalScopedBuffer<char> {
+ public:
+  explicit InternalScopedString(uptr max_length)
+      : InternalScopedBuffer<char>(max_length), length_(0) {
+    (*this)[0] = '\0';
+  }
+  uptr length() { return length_; }
+  void clear() {
+    (*this)[0] = '\0';
+    length_ = 0;
+  }
+  void append(const char *format, ...);
+
+ private:
+  uptr length_;
+};
+
+// Simple low-level (mmap-based) allocator for internal use. Doesn't have
+// constructor, so all instances of LowLevelAllocator should be
+// linker initialized.
+class LowLevelAllocator {
+ public:
+  // Requires an external lock.
+  void *Allocate(uptr size);
+ private:
+  char *allocated_end_;
+  char *allocated_current_;
+};
+typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size);
+// Allows to register tool-specific callbacks for LowLevelAllocator.
+// Passing NULL removes the callback.
+void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback);
+
+// IO
+void RawWrite(const char *buffer);
+bool ColorizeReports();
+void Printf(const char *format, ...);
+void Report(const char *format, ...);
+void SetPrintfAndReportCallback(void (*callback)(const char *));
+#define VReport(level, ...)                                              \
+  do {                                                                   \
+    if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \
+  } while (0)
+#define VPrintf(level, ...)                                              \
+  do {                                                                   \
+    if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \
+  } while (0)
+
+// Can be used to prevent mixing error reports from different sanitizers.
+extern StaticSpinMutex CommonSanitizerReportMutex;
+
+struct ReportFile {
+  void Write(const char *buffer, uptr length);
+  bool SupportsColors();
+  void SetReportPath(const char *path);
+
+  // Don't use fields directly. They are only declared public to allow
+  // aggregate initialization.
+
+  // Protects fields below.
+  StaticSpinMutex *mu;
+  // Opened file descriptor. Defaults to stderr. It may be equal to
+  // kInvalidFd, in which case new file will be opened when necessary.
+  fd_t fd;
+  // Path prefix of report file, set via __sanitizer_set_report_path.
+  char path_prefix[kMaxPathLength];
+  // Full path to report, obtained as <path_prefix>.PID
+  char full_path[kMaxPathLength];
+  // PID of the process that opened fd. If a fork() occurs,
+  // the PID of child will be different from fd_pid.
+  uptr fd_pid;
+
+ private:
+  void ReopenIfNecessary();
+};
+extern ReportFile report_file;
+
+extern uptr stoptheworld_tracer_pid;
+extern uptr stoptheworld_tracer_ppid;
+
+enum FileAccessMode {
+  RdOnly,
+  WrOnly,
+  RdWr
+};
+
+// Returns kInvalidFd on error.
+fd_t OpenFile(const char *filename, FileAccessMode mode,
+              error_t *errno_p = nullptr);
+void CloseFile(fd_t);
+
+// Return true on success, false on error.
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size,
+                  uptr *bytes_read = nullptr, error_t *error_p = nullptr);
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size,
+                 uptr *bytes_written = nullptr, error_t *error_p = nullptr);
+
+bool RenameFile(const char *oldpath, const char *newpath,
+                error_t *error_p = nullptr);
+
+// Scoped file handle closer.
+struct FileCloser {
+  explicit FileCloser(fd_t fd) : fd(fd) {}
+  ~FileCloser() { CloseFile(fd); }
+  fd_t fd;
+};
+
+bool SupportsColoredOutput(fd_t fd);
+
+// Opens the file 'file_name" and reads up to 'max_len' bytes.
+// The resulting buffer is mmaped and stored in '*buff'.
+// The size of the mmaped region is stored in '*buff_size'.
+// The total number of read bytes is stored in '*read_len'.
+// Returns true if file was successfully opened and read.
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+                      uptr *read_len, uptr max_len = 1 << 26,
+                      error_t *errno_p = nullptr);
+// Maps given file to virtual memory, and returns pointer to it
+// (or NULL if mapping fails). Stores the size of mmaped region
+// in '*buff_size'.
+void *MapFileToMemory(const char *file_name, uptr *buff_size);
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset);
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size);
+
+// Error report formatting.
+const char *StripPathPrefix(const char *filepath,
+                            const char *strip_file_prefix);
+// Strip the directories from the module name.
+const char *StripModuleName(const char *module);
+
+// OS
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len);
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len);
+const char *GetProcessName();
+void UpdateProcessName();
+void CacheBinaryName();
+void DisableCoreDumperIfNecessary();
+void DumpProcessMap();
+bool FileExists(const char *filename);
+const char *GetEnv(const char *name);
+bool SetEnv(const char *name, const char *value);
+const char *GetPwd();
+char *FindPathToBinary(const char *name);
+bool IsPathSeparator(const char c);
+bool IsAbsolutePath(const char *path);
+
+u32 GetUid();
+void ReExec();
+char **GetArgv();
+void PrintCmdline();
+bool StackSizeIsUnlimited();
+void SetStackSizeLimitInBytes(uptr limit);
+bool AddressSpaceIsUnlimited();
+void SetAddressSpaceUnlimited();
+void AdjustStackSize(void *attr);
+void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
+void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
+void SetSandboxingCallback(void (*f)());
+
+void CoverageUpdateMapping();
+void CovBeforeFork();
+void CovAfterFork(int child_pid);
+
+void InitializeCoverage(bool enabled, const char *coverage_dir);
+void ReInitializeCoverage(bool enabled, const char *coverage_dir);
+
+void InitTlsSize();
+uptr GetTlsSize();
+
+// Other
+void SleepForSeconds(int seconds);
+void SleepForMillis(int millis);
+u64 NanoTime();
+int Atexit(void (*function)(void));
+void SortArray(uptr *array, uptr size);
+bool TemplateMatch(const char *templ, const char *str);
+
+// Exit
+void NORETURN Abort();
+void NORETURN Die();
+void NORETURN
+CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2);
+void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
+                                      const char *mmap_type, error_t err);
+
+// Set the name of the current thread to 'name', return true on succees.
+// The name may be truncated to a system-dependent limit.
+bool SanitizerSetThreadName(const char *name);
+// Get the name of the current thread (no more than max_len bytes),
+// return true on succees. name should have space for at least max_len+1 bytes.
+bool SanitizerGetThreadName(char *name, int max_len);
+
+// Specific tools may override behavior of "Die" and "CheckFailed" functions
+// to do tool-specific job.
+typedef void (*DieCallbackType)(void);
+
+// It's possible to add several callbacks that would be run when "Die" is
+// called. The callbacks will be run in the opposite order. The tools are
+// strongly recommended to setup all callbacks during initialization, when there
+// is only a single thread.
+bool AddDieCallback(DieCallbackType callback);
+bool RemoveDieCallback(DieCallbackType callback);
+
+void SetUserDieCallback(DieCallbackType callback);
+
+typedef void (*CheckFailedCallbackType)(const char *, int, const char *,
+                                       u64, u64);
+void SetCheckFailedCallback(CheckFailedCallbackType callback);
+
+// Callback will be called if soft_rss_limit_mb is given and the limit is
+// exceeded (exceeded==true) or if rss went down below the limit
+// (exceeded==false).
+// The callback should be registered once at the tool init time.
+void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded));
+
+// Functions related to signal handling.
+typedef void (*SignalHandlerType)(int, void *, void *);
+bool IsDeadlySignal(int signum);
+void InstallDeadlySignalHandlers(SignalHandlerType handler);
+// Alternative signal stack (POSIX-only).
+void SetAlternateSignalStack();
+void UnsetAlternateSignalStack();
+
+// We don't want a summary too long.
+const int kMaxSummaryLength = 1024;
+// Construct a one-line string:
+//   SUMMARY: SanitizerToolName: error_message
+// and pass it to __sanitizer_report_error_summary.
+void ReportErrorSummary(const char *error_message);
+// Same as above, but construct error_message as:
+//   error_type file:line[:column][ function]
+void ReportErrorSummary(const char *error_type, const AddressInfo &info);
+// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
+void ReportErrorSummary(const char *error_type, StackTrace *trace);
+
+// Math
+#if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__)
+extern "C" {
+unsigned char _BitScanForward(unsigned long *index, unsigned long mask);  // NOLINT
+unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);  // NOLINT
+#if defined(_WIN64)
+unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask);  // NOLINT
+unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask);  // NOLINT
+#endif
+}
+#endif
+
+INLINE uptr MostSignificantSetBitIndex(uptr x) {
+  CHECK_NE(x, 0U);
+  unsigned long up;  // NOLINT
+#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+  up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x);
+# else
+  up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x);
+# endif
+#elif defined(_WIN64)
+  _BitScanReverse64(&up, x);
+#else
+  _BitScanReverse(&up, x);
+#endif
+  return up;
+}
+
+INLINE uptr LeastSignificantSetBitIndex(uptr x) {
+  CHECK_NE(x, 0U);
+  unsigned long up;  // NOLINT
+#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+  up = __builtin_ctzll(x);
+# else
+  up = __builtin_ctzl(x);
+# endif
+#elif defined(_WIN64)
+  _BitScanForward64(&up, x);
+#else
+  _BitScanForward(&up, x);
+#endif
+  return up;
+}
+
+INLINE bool IsPowerOfTwo(uptr x) {
+  return (x & (x - 1)) == 0;
+}
+
+INLINE uptr RoundUpToPowerOfTwo(uptr size) {
+  CHECK(size);
+  if (IsPowerOfTwo(size)) return size;
+
+  uptr up = MostSignificantSetBitIndex(size);
+  CHECK(size < (1ULL << (up + 1)));
+  CHECK(size > (1ULL << up));
+  return 1ULL << (up + 1);
+}
+
+INLINE uptr RoundUpTo(uptr size, uptr boundary) {
+  CHECK(IsPowerOfTwo(boundary));
+  return (size + boundary - 1) & ~(boundary - 1);
+}
+
+INLINE uptr RoundDownTo(uptr x, uptr boundary) {
+  return x & ~(boundary - 1);
+}
+
+INLINE bool IsAligned(uptr a, uptr alignment) {
+  return (a & (alignment - 1)) == 0;
+}
+
+INLINE uptr Log2(uptr x) {
+  CHECK(IsPowerOfTwo(x));
+  return LeastSignificantSetBitIndex(x);
+}
+
+// Don't use std::min, std::max or std::swap, to minimize dependency
+// on libstdc++.
+template<class T> T Min(T a, T b) { return a < b ? a : b; }
+template<class T> T Max(T a, T b) { return a > b ? a : b; }
+template<class T> void Swap(T& a, T& b) {
+  T tmp = a;
+  a = b;
+  b = tmp;
+}
+
+// Char handling
+INLINE bool IsSpace(int c) {
+  return (c == ' ') || (c == '\n') || (c == '\t') ||
+         (c == '\f') || (c == '\r') || (c == '\v');
+}
+INLINE bool IsDigit(int c) {
+  return (c >= '0') && (c <= '9');
+}
+INLINE int ToLower(int c) {
+  return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c;
+}
+
+// A low-level vector based on mmap. May incur a significant memory overhead for
+// small vectors.
+// WARNING: The current implementation supports only POD types.
+template<typename T>
+class InternalMmapVectorNoCtor {
+ public:
+  void Initialize(uptr initial_capacity) {
+    capacity_ = Max(initial_capacity, (uptr)1);
+    size_ = 0;
+    data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVectorNoCtor");
+  }
+  void Destroy() {
+    UnmapOrDie(data_, capacity_ * sizeof(T));
+  }
+  T &operator[](uptr i) {
+    CHECK_LT(i, size_);
+    return data_[i];
+  }
+  const T &operator[](uptr i) const {
+    CHECK_LT(i, size_);
+    return data_[i];
+  }
+  void push_back(const T &element) {
+    CHECK_LE(size_, capacity_);
+    if (size_ == capacity_) {
+      uptr new_capacity = RoundUpToPowerOfTwo(size_ + 1);
+      Resize(new_capacity);
+    }
+    data_[size_++] = element;
+  }
+  T &back() {
+    CHECK_GT(size_, 0);
+    return data_[size_ - 1];
+  }
+  void pop_back() {
+    CHECK_GT(size_, 0);
+    size_--;
+  }
+  uptr size() const {
+    return size_;
+  }
+  const T *data() const {
+    return data_;
+  }
+  T *data() {
+    return data_;
+  }
+  uptr capacity() const {
+    return capacity_;
+  }
+
+  void clear() { size_ = 0; }
+  bool empty() const { return size() == 0; }
+
+ private:
+  void Resize(uptr new_capacity) {
+    CHECK_GT(new_capacity, 0);
+    CHECK_LE(size_, new_capacity);
+    T *new_data = (T *)MmapOrDie(new_capacity * sizeof(T),
+                                 "InternalMmapVector");
+    internal_memcpy(new_data, data_, size_ * sizeof(T));
+    T *old_data = data_;
+    data_ = new_data;
+    UnmapOrDie(old_data, capacity_ * sizeof(T));
+    capacity_ = new_capacity;
+  }
+
+  T *data_;
+  uptr capacity_;
+  uptr size_;
+};
+
+template<typename T>
+class InternalMmapVector : public InternalMmapVectorNoCtor<T> {
+ public:
+  explicit InternalMmapVector(uptr initial_capacity) {
+    InternalMmapVectorNoCtor<T>::Initialize(initial_capacity);
+  }
+  ~InternalMmapVector() { InternalMmapVectorNoCtor<T>::Destroy(); }
+  // Disallow evil constructors.
+  InternalMmapVector(const InternalMmapVector&);
+  void operator=(const InternalMmapVector&);
+};
+
+// HeapSort for arrays and InternalMmapVector.
+template<class Container, class Compare>
+void InternalSort(Container *v, uptr size, Compare comp) {
+  if (size < 2)
+    return;
+  // Stage 1: insert elements to the heap.
+  for (uptr i = 1; i < size; i++) {
+    uptr j, p;
+    for (j = i; j > 0; j = p) {
+      p = (j - 1) / 2;
+      if (comp((*v)[p], (*v)[j]))
+        Swap((*v)[j], (*v)[p]);
+      else
+        break;
+    }
+  }
+  // Stage 2: swap largest element with the last one,
+  // and sink the new top.
+  for (uptr i = size - 1; i > 0; i--) {
+    Swap((*v)[0], (*v)[i]);
+    uptr j, max_ind;
+    for (j = 0; j < i; j = max_ind) {
+      uptr left = 2 * j + 1;
+      uptr right = 2 * j + 2;
+      max_ind = j;
+      if (left < i && comp((*v)[max_ind], (*v)[left]))
+        max_ind = left;
+      if (right < i && comp((*v)[max_ind], (*v)[right]))
+        max_ind = right;
+      if (max_ind != j)
+        Swap((*v)[j], (*v)[max_ind]);
+      else
+        break;
+    }
+  }
+}
+
+template<class Container, class Value, class Compare>
+uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
+                          const Value &val, Compare comp) {
+  uptr not_found = last + 1;
+  while (last >= first) {
+    uptr mid = (first + last) / 2;
+    if (comp(v[mid], val))
+      first = mid + 1;
+    else if (comp(val, v[mid]))
+      last = mid - 1;
+    else
+      return mid;
+  }
+  return not_found;
+}
+
+// Represents a binary loaded into virtual memory (e.g. this can be an
+// executable or a shared object).
+class LoadedModule {
+ public:
+  LoadedModule() : full_name_(nullptr), base_address_(0) { ranges_.clear(); }
+  void set(const char *module_name, uptr base_address);
+  void clear();
+  void addAddressRange(uptr beg, uptr end, bool executable);
+  bool containsAddress(uptr address) const;
+
+  const char *full_name() const { return full_name_; }
+  uptr base_address() const { return base_address_; }
+
+  struct AddressRange {
+    AddressRange *next;
+    uptr beg;
+    uptr end;
+    bool executable;
+
+    AddressRange(uptr beg, uptr end, bool executable)
+        : next(nullptr), beg(beg), end(end), executable(executable) {}
+  };
+
+  typedef IntrusiveList<AddressRange>::ConstIterator Iterator;
+  Iterator ranges() const { return Iterator(&ranges_); }
+
+ private:
+  char *full_name_;  // Owned.
+  uptr base_address_;
+  IntrusiveList<AddressRange> ranges_;
+};
+
+// OS-dependent function that fills array with descriptions of at most
+// "max_modules" currently loaded modules. Returns the number of
+// initialized modules. If filter is nonzero, ignores modules for which
+// filter(full_name) is false.
+typedef bool (*string_predicate_t)(const char *);
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
+                      string_predicate_t filter);
+
+// Callback type for iterating over a set of memory ranges.
+typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
+
+enum AndroidApiLevel {
+  ANDROID_NOT_ANDROID = 0,
+  ANDROID_KITKAT = 19,
+  ANDROID_LOLLIPOP_MR1 = 22,
+  ANDROID_POST_LOLLIPOP = 23
+};
+
+#if SANITIZER_LINUX
+// Initialize Android logging. Any writes before this are silently lost.
+void AndroidLogInit();
+void WriteToSyslog(const char *buffer);
+#else
+INLINE void AndroidLogInit() {}
+INLINE void WriteToSyslog(const char *buffer) {}
+#endif
+
+#if SANITIZER_ANDROID
+void GetExtraActivationFlags(char *buf, uptr size);
+void SanitizerInitializeUnwinder();
+AndroidApiLevel AndroidGetApiLevel();
+#else
+INLINE void AndroidLogWrite(const char *buffer_unused) {}
+INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
+INLINE void SanitizerInitializeUnwinder() {}
+INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; }
+#endif
+
+INLINE uptr GetPthreadDestructorIterations() {
+#if SANITIZER_ANDROID
+  return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4;
+#elif SANITIZER_POSIX
+  return 4;
+#else
+// Unused on Windows.
+  return 0;
+#endif
+}
+
+void *internal_start_thread(void(*func)(void*), void *arg);
+void internal_join_thread(void *th);
+void MaybeStartBackgroudThread();
+
+// Make the compiler think that something is going on there.
+// Use this inside a loop that looks like memset/memcpy/etc to prevent the
+// compiler from recognising it and turning it into an actual call to
+// memset/memcpy/etc.
+static inline void SanitizerBreakOptimization(void *arg) {
+#if _MSC_VER && !defined(__clang__)
+  _ReadWriteBarrier();
+#else
+  __asm__ __volatile__("" : : "r" (arg) : "memory");
+#endif
+}
+
+struct SignalContext {
+  void *context;
+  uptr addr;
+  uptr pc;
+  uptr sp;
+  uptr bp;
+
+  SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
+      context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
+  }
+
+  // Creates signal context in a platform-specific manner.
+  static SignalContext Create(void *siginfo, void *context);
+};
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
+
+}  // namespace __sanitizer
+
+inline void *operator new(__sanitizer::operator_new_size_type size,
+                          __sanitizer::LowLevelAllocator &alloc) {
+  return alloc.Allocate(size);
+}
+
+struct StackDepotStats {
+  uptr n_uniq_ids;
+  uptr allocated;
+};
+
+#endif  // SANITIZER_COMMON_H
diff --git a/lsan/include/sanitizer_common/sanitizer_common_interceptors.inc b/lsan/include/sanitizer_common/sanitizer_common_interceptors.inc
new file mode 100644 (file)
index 0000000..8223778
--- /dev/null
@@ -0,0 +1,5459 @@
+//===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Common function interceptors for tools like AddressSanitizer,
+// ThreadSanitizer, MemorySanitizer, etc.
+//
+// This file should be included into the tool's interceptor file,
+// which has to define it's own macros:
+//   COMMON_INTERCEPTOR_ENTER
+//   COMMON_INTERCEPTOR_ENTER_NOIGNORE
+//   COMMON_INTERCEPTOR_READ_RANGE
+//   COMMON_INTERCEPTOR_WRITE_RANGE
+//   COMMON_INTERCEPTOR_INITIALIZE_RANGE
+//   COMMON_INTERCEPTOR_DIR_ACQUIRE
+//   COMMON_INTERCEPTOR_FD_ACQUIRE
+//   COMMON_INTERCEPTOR_FD_RELEASE
+//   COMMON_INTERCEPTOR_FD_ACCESS
+//   COMMON_INTERCEPTOR_SET_THREAD_NAME
+//   COMMON_INTERCEPTOR_ON_DLOPEN
+//   COMMON_INTERCEPTOR_ON_EXIT
+//   COMMON_INTERCEPTOR_MUTEX_LOCK
+//   COMMON_INTERCEPTOR_MUTEX_UNLOCK
+//   COMMON_INTERCEPTOR_MUTEX_REPAIR
+//   COMMON_INTERCEPTOR_SET_PTHREAD_NAME
+//   COMMON_INTERCEPTOR_HANDLE_RECVMSG
+//   COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_addrhashmap.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_interceptors.h"
+#include "sanitizer_tls_get_addr.h"
+
+#include <stdarg.h>
+
+#if SANITIZER_INTERCEPTOR_HOOKS
+#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...)                                     \
+  do {                                                                         \
+    if (f)                                                                     \
+      f(__VA_ARGS__);                                                          \
+  } while (false);
+#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...)                                  \
+  extern "C" {                                                                 \
+  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void f(__VA_ARGS__);  \
+  } // extern "C"
+#else
+#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...)
+#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...)
+
+#endif  // SANITIZER_INTERCEPTOR_HOOKS
+
+#if SANITIZER_WINDOWS && !defined(va_copy)
+#define va_copy(dst, src) ((dst) = (src))
+#endif // _WIN32
+
+#if SANITIZER_FREEBSD
+#define pthread_setname_np pthread_set_name_np
+#define inet_aton __inet_aton
+#define inet_pton __inet_pton
+#define iconv __bsd_iconv
+#endif
+
+#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
+#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(p, size) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_UNPOISON_PARAM
+#define COMMON_INTERCEPTOR_UNPOISON_PARAM(count) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_FD_ACCESS
+#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_MUTEX_LOCK
+#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK
+#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR
+#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG
+#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg))
+#endif
+
+#ifndef COMMON_INTERCEPTOR_FILE_OPEN
+#define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_FILE_CLOSE
+#define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED
+#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_ENTER_NOIGNORE
+#define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, ...) \
+  COMMON_INTERCEPTOR_ENTER(ctx, __VA_ARGS__)
+#endif
+
+#ifndef COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
+#endif
+
+#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n)       \
+    COMMON_INTERCEPTOR_READ_RANGE((ctx), (s),                       \
+      common_flags()->strict_string_checks ? (len) + 1 : (n) )
+
+#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n)                   \
+    COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
+
+#ifndef COMMON_INTERCEPTOR_ON_DLOPEN
+#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0;
+#endif
+
+#ifndef COMMON_INTERCEPTOR_ACQUIRE
+#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_RELEASE
+#define COMMON_INTERCEPTOR_RELEASE(ctx, u) {}
+#endif
+
+struct FileMetadata {
+  // For open_memstream().
+  char **addr;
+  SIZE_T *size;
+};
+
+struct CommonInterceptorMetadata {
+  enum {
+    CIMT_INVALID = 0,
+    CIMT_FILE
+  } type;
+  union {
+    FileMetadata file;
+  };
+};
+
+typedef AddrHashMap<CommonInterceptorMetadata, 31051> MetadataHashMap;
+
+static MetadataHashMap *interceptor_metadata_map;
+
+#if SI_NOT_WINDOWS
+UNUSED static void SetInterceptorMetadata(__sanitizer_FILE *addr,
+                                          const FileMetadata &file) {
+  MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr);
+  CHECK(h.created());
+  h->type = CommonInterceptorMetadata::CIMT_FILE;
+  h->file = file;
+}
+
+UNUSED static const FileMetadata *GetInterceptorMetadata(
+    __sanitizer_FILE *addr) {
+  MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr,
+                            /* remove */ false,
+                            /* create */ false);
+  if (h.exists()) {
+    CHECK(!h.created());
+    CHECK(h->type == CommonInterceptorMetadata::CIMT_FILE);
+    return &h->file;
+  } else {
+    return 0;
+  }
+}
+
+UNUSED static void DeleteInterceptorMetadata(void *addr) {
+  MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, true);
+  CHECK(h.exists());
+}
+#endif  // SI_NOT_WINDOWS
+
+#if SANITIZER_INTERCEPT_TEXTDOMAIN
+INTERCEPTOR(char*, textdomain, const char *domainname) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0);
+  char *domain = REAL(textdomain)(domainname);
+  if (domain) {
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1);
+  }
+  return domain;
+}
+#define INIT_TEXTDOMAIN COMMON_INTERCEPT_FUNCTION(textdomain)
+#else
+#define INIT_TEXTDOMAIN
+#endif
+
+#if SANITIZER_INTERCEPT_STRCMP
+static inline int CharCmpX(unsigned char c1, unsigned char c2) {
+  return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
+}
+
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc,
+                              const char *s1, const char *s2)
+
+INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2);
+  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1,
+                             s2);
+  unsigned char c1, c2;
+  uptr i;
+  for (i = 0;; i++) {
+    c1 = (unsigned char)s1[i];
+    c2 = (unsigned char)s2[i];
+    if (c1 != c2 || c1 == '\0') break;
+  }
+  COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
+  return CharCmpX(c1, c2);
+}
+
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc,
+                              const char *s1, const char *s2, uptr n)
+
+INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
+  if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+    return internal_strncmp(s1, s2, size);
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size);
+  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1,
+                             s2, size);
+  unsigned char c1 = 0, c2 = 0;
+  uptr i;
+  for (i = 0; i < size; i++) {
+    c1 = (unsigned char)s1[i];
+    c2 = (unsigned char)s2[i];
+    if (c1 != c2 || c1 == '\0') break;
+  }
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size));
+  return CharCmpX(c1, c2);
+}
+
+#define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp)
+#define INIT_STRNCMP COMMON_INTERCEPT_FUNCTION(strncmp)
+#else
+#define INIT_STRCMP
+#define INIT_STRNCMP
+#endif
+
+#if SANITIZER_INTERCEPT_STRCASECMP
+static inline int CharCaseCmp(unsigned char c1, unsigned char c2) {
+  int c1_low = ToLower(c1);
+  int c2_low = ToLower(c2);
+  return c1_low - c2_low;
+}
+
+INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2);
+  unsigned char c1 = 0, c2 = 0;
+  uptr i;
+  for (i = 0;; i++) {
+    c1 = (unsigned char)s1[i];
+    c2 = (unsigned char)s2[i];
+    if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
+  }
+  COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
+  return CharCaseCmp(c1, c2);
+}
+
+INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strncasecmp, s1, s2, n);
+  unsigned char c1 = 0, c2 = 0;
+  uptr i;
+  for (i = 0; i < n; i++) {
+    c1 = (unsigned char)s1[i];
+    c2 = (unsigned char)s2[i];
+    if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
+  }
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, n));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, n));
+  return CharCaseCmp(c1, c2);
+}
+
+#define INIT_STRCASECMP COMMON_INTERCEPT_FUNCTION(strcasecmp)
+#define INIT_STRNCASECMP COMMON_INTERCEPT_FUNCTION(strncasecmp)
+#else
+#define INIT_STRCASECMP
+#define INIT_STRNCASECMP
+#endif
+
+#if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR
+static inline void StrstrCheck(void *ctx, char *r, const char *s1,
+                               const char *s2) {
+    uptr len1 = REAL(strlen)(s1);
+    uptr len2 = REAL(strlen)(s2);
+    COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1,
+                                          r ? r - s1 + len2 : len1 + 1);
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_STRSTR
+INTERCEPTOR(char*, strstr, const char *s1, const char *s2) {
+  if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+    return internal_strstr(s1, s2);
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2);
+  char *r = REAL(strstr)(s1, s2);
+  if (common_flags()->intercept_strstr)
+    StrstrCheck(ctx, r, s1, s2);
+  return r;
+}
+
+#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr);
+#else
+#define INIT_STRSTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRCASESTR
+INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2);
+  char *r = REAL(strcasestr)(s1, s2);
+  if (common_flags()->intercept_strstr)
+    StrstrCheck(ctx, r, s1, s2);
+  return r;
+}
+
+#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr);
+#else
+#define INIT_STRCASESTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRSPN
+INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2);
+  SIZE_T r = REAL(strspn)(s1, s2);
+  if (common_flags()->intercept_strspn) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+    COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+  }
+  return r;
+}
+
+INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2);
+  SIZE_T r = REAL(strcspn)(s1, s2);
+  if (common_flags()->intercept_strspn) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+    COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+  }
+  return r;
+}
+
+#define INIT_STRSPN \
+  COMMON_INTERCEPT_FUNCTION(strspn); \
+  COMMON_INTERCEPT_FUNCTION(strcspn);
+#else
+#define INIT_STRSPN
+#endif
+
+#if SANITIZER_INTERCEPT_STRPBRK
+INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2);
+  char *r = REAL(strpbrk)(s1, s2);
+  if (common_flags()->intercept_strpbrk) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+    COMMON_INTERCEPTOR_READ_STRING(ctx, s1,
+        r ? r - s1 + 1 : REAL(strlen)(s1) + 1);
+  }
+  return r;
+}
+
+#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk);
+#else
+#define INIT_STRPBRK
+#endif
+
+#if SANITIZER_INTERCEPT_MEMCMP
+
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc,
+                              const void *s1, const void *s2, uptr n)
+
+INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size);
+  if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+    return internal_memcmp(a1, a2, size);
+  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1,
+                             a2, size);
+  if (common_flags()->intercept_memcmp) {
+    if (common_flags()->strict_memcmp) {
+      // Check the entire regions even if the first bytes of the buffers are
+      // different.
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, a1, size);
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, a2, size);
+      // Fallthrough to REAL(memcmp) below.
+    } else {
+      unsigned char c1 = 0, c2 = 0;
+      const unsigned char *s1 = (const unsigned char*)a1;
+      const unsigned char *s2 = (const unsigned char*)a2;
+      uptr i;
+      for (i = 0; i < size; i++) {
+        c1 = s1[i];
+        c2 = s2[i];
+        if (c1 != c2) break;
+      }
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size));
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size));
+      return CharCmpX(c1, c2);
+    }
+  }
+  return REAL(memcmp(a1, a2, size));
+}
+
+#define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp)
+#else
+#define INIT_MEMCMP
+#endif
+
+#if SANITIZER_INTERCEPT_MEMCHR
+INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) {
+  if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+    return internal_memchr(s, c, n);
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n);
+  void *res = REAL(memchr)(s, c, n);
+  uptr len = res ? (char *)res - (const char *)s + 1 : n;
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s, len);
+  return res;
+}
+
+#define INIT_MEMCHR COMMON_INTERCEPT_FUNCTION(memchr)
+#else
+#define INIT_MEMCHR
+#endif
+
+#if SANITIZER_INTERCEPT_MEMRCHR
+INTERCEPTOR(void*, memrchr, const void *s, int c, SIZE_T n) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, memrchr, s, c, n);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, s, n);
+  return REAL(memrchr)(s, c, n);
+}
+
+#define INIT_MEMRCHR COMMON_INTERCEPT_FUNCTION(memrchr)
+#else
+#define INIT_MEMRCHR
+#endif
+
+#if SANITIZER_INTERCEPT_FREXP
+INTERCEPTOR(double, frexp, double x, int *exp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, frexp, x, exp);
+  // Assuming frexp() always writes to |exp|.
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
+  double res = REAL(frexp)(x, exp);
+  return res;
+}
+
+#define INIT_FREXP COMMON_INTERCEPT_FUNCTION(frexp);
+#else
+#define INIT_FREXP
+#endif  // SANITIZER_INTERCEPT_FREXP
+
+#if SANITIZER_INTERCEPT_FREXPF_FREXPL
+INTERCEPTOR(float, frexpf, float x, int *exp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  float res = REAL(frexpf)(x, exp);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
+  return res;
+}
+
+INTERCEPTOR(long double, frexpl, long double x, int *exp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  long double res = REAL(frexpl)(x, exp);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
+  return res;
+}
+
+#define INIT_FREXPF_FREXPL           \
+  COMMON_INTERCEPT_FUNCTION(frexpf); \
+  COMMON_INTERCEPT_FUNCTION(frexpl)
+#else
+#define INIT_FREXPF_FREXPL
+#endif  // SANITIZER_INTERCEPT_FREXPF_FREXPL
+
+#if SI_NOT_WINDOWS
+static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec,
+                        SIZE_T iovlen, SIZE_T maxlen) {
+  for (SIZE_T i = 0; i < iovlen && maxlen; ++i) {
+    SSIZE_T sz = Min(iovec[i].iov_len, maxlen);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec[i].iov_base, sz);
+    maxlen -= sz;
+  }
+}
+
+static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec,
+                       SIZE_T iovlen, SIZE_T maxlen) {
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec) * iovlen);
+  for (SIZE_T i = 0; i < iovlen && maxlen; ++i) {
+    SSIZE_T sz = Min(iovec[i].iov_len, maxlen);
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec[i].iov_base, sz);
+    maxlen -= sz;
+  }
+}
+#endif
+
+#if SANITIZER_INTERCEPT_READ
+INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(read)(fd, ptr, count);
+  if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+  if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+  return res;
+}
+#define INIT_READ COMMON_INTERCEPT_FUNCTION(read)
+#else
+#define INIT_READ
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD
+INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
+  if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+  if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+  return res;
+}
+#define INIT_PREAD COMMON_INTERCEPT_FUNCTION(pread)
+#else
+#define INIT_PREAD
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD64
+INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
+  if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+  if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+  return res;
+}
+#define INIT_PREAD64 COMMON_INTERCEPT_FUNCTION(pread64)
+#else
+#define INIT_PREAD64
+#endif
+
+#if SANITIZER_INTERCEPT_READV
+INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov,
+                        int iovcnt) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  SSIZE_T res = REAL(readv)(fd, iov, iovcnt);
+  if (res > 0) write_iovec(ctx, iov, iovcnt, res);
+  if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+  return res;
+}
+#define INIT_READV COMMON_INTERCEPT_FUNCTION(readv)
+#else
+#define INIT_READV
+#endif
+
+#if SANITIZER_INTERCEPT_PREADV
+INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt,
+            OFF_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset);
+  if (res > 0) write_iovec(ctx, iov, iovcnt, res);
+  if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+  return res;
+}
+#define INIT_PREADV COMMON_INTERCEPT_FUNCTION(preadv)
+#else
+#define INIT_PREADV
+#endif
+
+#if SANITIZER_INTERCEPT_PREADV64
+INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt,
+            OFF64_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset);
+  if (res > 0) write_iovec(ctx, iov, iovcnt, res);
+  if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+  return res;
+}
+#define INIT_PREADV64 COMMON_INTERCEPT_FUNCTION(preadv64)
+#else
+#define INIT_PREADV64
+#endif
+
+#if SANITIZER_INTERCEPT_WRITE
+INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+  SSIZE_T res = REAL(write)(fd, ptr, count);
+  // FIXME: this check should be _before_ the call to REAL(write), not after
+  if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+  return res;
+}
+#define INIT_WRITE COMMON_INTERCEPT_FUNCTION(write)
+#else
+#define INIT_WRITE
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITE
+INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+  SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset);
+  if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+  return res;
+}
+#define INIT_PWRITE COMMON_INTERCEPT_FUNCTION(pwrite)
+#else
+#define INIT_PWRITE
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITE64
+INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count,
+            OFF64_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+  SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset);
+  if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+  return res;
+}
+#define INIT_PWRITE64 COMMON_INTERCEPT_FUNCTION(pwrite64)
+#else
+#define INIT_PWRITE64
+#endif
+
+#if SANITIZER_INTERCEPT_WRITEV
+INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov,
+                        int iovcnt) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+  SSIZE_T res = REAL(writev)(fd, iov, iovcnt);
+  if (res > 0) read_iovec(ctx, iov, iovcnt, res);
+  return res;
+}
+#define INIT_WRITEV COMMON_INTERCEPT_FUNCTION(writev)
+#else
+#define INIT_WRITEV
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITEV
+INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt,
+            OFF_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+  SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset);
+  if (res > 0) read_iovec(ctx, iov, iovcnt, res);
+  return res;
+}
+#define INIT_PWRITEV COMMON_INTERCEPT_FUNCTION(pwritev)
+#else
+#define INIT_PWRITEV
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITEV64
+INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt,
+            OFF64_T offset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset);
+  COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+  if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+  SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset);
+  if (res > 0) read_iovec(ctx, iov, iovcnt, res);
+  return res;
+}
+#define INIT_PWRITEV64 COMMON_INTERCEPT_FUNCTION(pwritev64)
+#else
+#define INIT_PWRITEV64
+#endif
+
+#if SANITIZER_INTERCEPT_PRCTL
+INTERCEPTOR(int, prctl, int option, unsigned long arg2,
+            unsigned long arg3,                        // NOLINT
+            unsigned long arg4, unsigned long arg5) {  // NOLINT
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5);
+  static const int PR_SET_NAME = 15;
+  int res = REAL(prctl(option, arg2, arg3, arg4, arg5));
+  if (option == PR_SET_NAME) {
+    char buff[16];
+    internal_strncpy(buff, (char *)arg2, 15);
+    buff[15] = 0;
+    COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff);
+  }
+  return res;
+}
+#define INIT_PRCTL COMMON_INTERCEPT_FUNCTION(prctl)
+#else
+#define INIT_PRCTL
+#endif  // SANITIZER_INTERCEPT_PRCTL
+
+#if SANITIZER_INTERCEPT_TIME
+INTERCEPTOR(unsigned long, time, unsigned long *t) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, time, t);
+  unsigned long local_t;
+  unsigned long res = REAL(time)(&local_t);
+  if (t && res != (unsigned long)-1) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, t, sizeof(*t));
+    *t = local_t;
+  }
+  return res;
+}
+#define INIT_TIME COMMON_INTERCEPT_FUNCTION(time);
+#else
+#define INIT_TIME
+#endif  // SANITIZER_INTERCEPT_TIME
+
+#if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS
+static void unpoison_tm(void *ctx, __sanitizer_tm *tm) {
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
+  if (tm->tm_zone) {
+    // Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone
+    // can point to shared memory and tsan would report a data race.
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(tm->tm_zone,
+                                        REAL(strlen(tm->tm_zone)) + 1);
+  }
+}
+INTERCEPTOR(__sanitizer_tm *, localtime, unsigned long *timep) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, localtime, timep);
+  __sanitizer_tm *res = REAL(localtime)(timep);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
+    unpoison_tm(ctx, res);
+  }
+  return res;
+}
+INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result);
+  __sanitizer_tm *res = REAL(localtime_r)(timep, result);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
+    unpoison_tm(ctx, res);
+  }
+  return res;
+}
+INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep);
+  __sanitizer_tm *res = REAL(gmtime)(timep);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
+    unpoison_tm(ctx, res);
+  }
+  return res;
+}
+INTERCEPTOR(__sanitizer_tm *, gmtime_r, unsigned long *timep, void *result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gmtime_r, timep, result);
+  __sanitizer_tm *res = REAL(gmtime_r)(timep, result);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
+    unpoison_tm(ctx, res);
+  }
+  return res;
+}
+INTERCEPTOR(char *, ctime, unsigned long *timep) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(ctime)(timep);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(ctime_r)(timep, result);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(asctime)(tm);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(asctime_r)(tm, result);
+  if (res) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+INTERCEPTOR(long, mktime, __sanitizer_tm *tm) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, mktime, tm);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_sec, sizeof(tm->tm_sec));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_min, sizeof(tm->tm_min));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_hour, sizeof(tm->tm_hour));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mday, sizeof(tm->tm_mday));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mon, sizeof(tm->tm_mon));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_year, sizeof(tm->tm_year));
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_isdst, sizeof(tm->tm_isdst));
+  long res = REAL(mktime)(tm);
+  if (res != -1) unpoison_tm(ctx, tm);
+  return res;
+}
+#define INIT_LOCALTIME_AND_FRIENDS        \
+  COMMON_INTERCEPT_FUNCTION(localtime);   \
+  COMMON_INTERCEPT_FUNCTION(localtime_r); \
+  COMMON_INTERCEPT_FUNCTION(gmtime);      \
+  COMMON_INTERCEPT_FUNCTION(gmtime_r);    \
+  COMMON_INTERCEPT_FUNCTION(ctime);       \
+  COMMON_INTERCEPT_FUNCTION(ctime_r);     \
+  COMMON_INTERCEPT_FUNCTION(asctime);     \
+  COMMON_INTERCEPT_FUNCTION(asctime_r);   \
+  COMMON_INTERCEPT_FUNCTION(mktime);
+#else
+#define INIT_LOCALTIME_AND_FRIENDS
+#endif  // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS
+
+#if SANITIZER_INTERCEPT_STRPTIME
+INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm);
+  if (format)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(strptime)(s, format, tm);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0);
+  if (res && tm) {
+    // Do not call unpoison_tm here, because strptime does not, in fact,
+    // initialize the entire struct tm. For example, tm_zone pointer is left
+    // uninitialized.
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
+  }
+  return res;
+}
+#define INIT_STRPTIME COMMON_INTERCEPT_FUNCTION(strptime);
+#else
+#define INIT_STRPTIME
+#endif
+
+#if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF
+#include "sanitizer_common_interceptors_format.inc"
+
+#define FORMAT_INTERCEPTOR_IMPL(name, vname, ...)                              \
+  {                                                                            \
+    void *ctx;                                                                 \
+    va_list ap;                                                                \
+    va_start(ap, format);                                                      \
+    COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap);                     \
+    int res = WRAP(vname)(__VA_ARGS__, ap);                                    \
+    va_end(ap);                                                                \
+    return res;                                                                \
+  }
+
+#endif
+
+#if SANITIZER_INTERCEPT_SCANF
+
+#define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...)                    \
+  {                                                                            \
+    void *ctx;                                                                 \
+    COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__);                         \
+    va_list aq;                                                                \
+    va_copy(aq, ap);                                                           \
+    int res = REAL(vname)(__VA_ARGS__);                                        \
+    if (res > 0)                                                               \
+      scanf_common(ctx, res, allowGnuMalloc, format, aq);                      \
+    va_end(aq);                                                                \
+    return res;                                                                \
+  }
+
+INTERCEPTOR(int, vscanf, const char *format, va_list ap)
+VSCANF_INTERCEPTOR_IMPL(vscanf, true, format, ap)
+
+INTERCEPTOR(int, vsscanf, const char *str, const char *format, va_list ap)
+VSCANF_INTERCEPTOR_IMPL(vsscanf, true, str, format, ap)
+
+INTERCEPTOR(int, vfscanf, void *stream, const char *format, va_list ap)
+VSCANF_INTERCEPTOR_IMPL(vfscanf, true, stream, format, ap)
+
+#if SANITIZER_INTERCEPT_ISOC99_SCANF
+INTERCEPTOR(int, __isoc99_vscanf, const char *format, va_list ap)
+VSCANF_INTERCEPTOR_IMPL(__isoc99_vscanf, false, format, ap)
+
+INTERCEPTOR(int, __isoc99_vsscanf, const char *str, const char *format,
+            va_list ap)
+VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap)
+
+INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap)
+VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap)
+#endif  // SANITIZER_INTERCEPT_ISOC99_SCANF
+
+INTERCEPTOR(int, scanf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(scanf, vscanf, format)
+
+INTERCEPTOR(int, fscanf, void *stream, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format)
+
+INTERCEPTOR(int, sscanf, const char *str, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format)
+
+#if SANITIZER_INTERCEPT_ISOC99_SCANF
+INTERCEPTOR(int, __isoc99_scanf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format)
+
+INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format)
+
+INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
+#endif
+
+#endif
+
+#if SANITIZER_INTERCEPT_SCANF
+#define INIT_SCANF                    \
+  COMMON_INTERCEPT_FUNCTION(scanf);   \
+  COMMON_INTERCEPT_FUNCTION(sscanf);  \
+  COMMON_INTERCEPT_FUNCTION(fscanf);  \
+  COMMON_INTERCEPT_FUNCTION(vscanf);  \
+  COMMON_INTERCEPT_FUNCTION(vsscanf); \
+  COMMON_INTERCEPT_FUNCTION(vfscanf);
+#else
+#define INIT_SCANF
+#endif
+
+#if SANITIZER_INTERCEPT_ISOC99_SCANF
+#define INIT_ISOC99_SCANF                      \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_scanf);   \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_sscanf);  \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf);  \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf);  \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf);
+#else
+#define INIT_ISOC99_SCANF
+#endif
+
+#if SANITIZER_INTERCEPT_PRINTF
+
+#define VPRINTF_INTERCEPTOR_ENTER(vname, ...)                                  \
+  void *ctx;                                                                   \
+  COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__);                           \
+  va_list aq;                                                                  \
+  va_copy(aq, ap);
+
+#define VPRINTF_INTERCEPTOR_RETURN()                                           \
+  va_end(aq);
+
+#define VPRINTF_INTERCEPTOR_IMPL(vname, ...)                                   \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__);                             \
+    if (common_flags()->check_printf)                                          \
+      printf_common(ctx, format, aq);                                          \
+    int res = REAL(vname)(__VA_ARGS__);                                        \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+// FIXME: under ASan the REAL() call below may write to freed memory and
+// corrupt its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...)                             \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__)                         \
+    if (common_flags()->check_printf) {                                        \
+      printf_common(ctx, format, aq);                                          \
+    }                                                                          \
+    int res = REAL(vname)(str, __VA_ARGS__);                                   \
+    if (res >= 0) {                                                            \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1);                       \
+    }                                                                          \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+// FIXME: under ASan the REAL() call below may write to freed memory and
+// corrupt its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...)                      \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__)                   \
+    if (common_flags()->check_printf) {                                        \
+      printf_common(ctx, format, aq);                                          \
+    }                                                                          \
+    int res = REAL(vname)(str, size, __VA_ARGS__);                             \
+    if (res >= 0) {                                                            \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, Min(size, (SIZE_T)(res + 1)));  \
+    }                                                                          \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+// FIXME: under ASan the REAL() call below may write to freed memory and
+// corrupt its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...)                           \
+  {                                                                            \
+    VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__)                        \
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, strp, sizeof(char *));                 \
+    if (common_flags()->check_printf) {                                        \
+      printf_common(ctx, format, aq);                                          \
+    }                                                                          \
+    int res = REAL(vname)(strp, __VA_ARGS__);                                  \
+    if (res >= 0) {                                                            \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *strp, res + 1);                     \
+    }                                                                          \
+    VPRINTF_INTERCEPTOR_RETURN();                                              \
+    return res;                                                                \
+  }
+
+INTERCEPTOR(int, vprintf, const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(vprintf, format, ap)
+
+INTERCEPTOR(int, vfprintf, __sanitizer_FILE *stream, const char *format,
+            va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(vfprintf, stream, format, ap)
+
+INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format,
+            va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap)
+
+#if SANITIZER_INTERCEPT_PRINTF_L
+INTERCEPTOR(int, vsnprintf_l, char *str, SIZE_T size, void *loc,
+            const char *format, va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf_l, str, size, loc, format, ap)
+
+INTERCEPTOR(int, snprintf_l, char *str, SIZE_T size, void *loc,
+            const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(snprintf_l, vsnprintf_l, str, size, loc, format)
+#endif  // SANITIZER_INTERCEPT_PRINTF_L
+
+INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap)
+VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap)
+
+INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap)
+VASPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap)
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+INTERCEPTOR(int, __isoc99_vprintf, const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(__isoc99_vprintf, format, ap)
+
+INTERCEPTOR(int, __isoc99_vfprintf, __sanitizer_FILE *stream,
+            const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(__isoc99_vfprintf, stream, format, ap)
+
+INTERCEPTOR(int, __isoc99_vsnprintf, char *str, SIZE_T size, const char *format,
+            va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap)
+
+INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format,
+            va_list ap)
+VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, format,
+                          ap)
+
+#endif  // SANITIZER_INTERCEPT_ISOC99_PRINTF
+
+INTERCEPTOR(int, printf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(printf, vprintf, format)
+
+INTERCEPTOR(int, fprintf, __sanitizer_FILE *stream, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format)
+
+INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT
+FORMAT_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT
+
+INTERCEPTOR(int, snprintf, char *str, SIZE_T size, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format)
+
+INTERCEPTOR(int, asprintf, char **strp, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format)
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+INTERCEPTOR(int, __isoc99_printf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format)
+
+INTERCEPTOR(int, __isoc99_fprintf, __sanitizer_FILE *stream, const char *format,
+            ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format)
+
+INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format)
+
+INTERCEPTOR(int, __isoc99_snprintf, char *str, SIZE_T size,
+            const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
+                        format)
+
+#endif  // SANITIZER_INTERCEPT_ISOC99_PRINTF
+
+#endif  // SANITIZER_INTERCEPT_PRINTF
+
+#if SANITIZER_INTERCEPT_PRINTF
+#define INIT_PRINTF                     \
+  COMMON_INTERCEPT_FUNCTION(printf);    \
+  COMMON_INTERCEPT_FUNCTION(sprintf);   \
+  COMMON_INTERCEPT_FUNCTION(snprintf);  \
+  COMMON_INTERCEPT_FUNCTION(asprintf);  \
+  COMMON_INTERCEPT_FUNCTION(fprintf);   \
+  COMMON_INTERCEPT_FUNCTION(vprintf);   \
+  COMMON_INTERCEPT_FUNCTION(vsprintf);  \
+  COMMON_INTERCEPT_FUNCTION(vsnprintf); \
+  COMMON_INTERCEPT_FUNCTION(vasprintf); \
+  COMMON_INTERCEPT_FUNCTION(vfprintf);
+#else
+#define INIT_PRINTF
+#endif
+
+#if SANITIZER_INTERCEPT_PRINTF_L
+#define INIT_PRINTF_L                     \
+  COMMON_INTERCEPT_FUNCTION(snprintf_l);  \
+  COMMON_INTERCEPT_FUNCTION(vsnprintf_l);
+#else
+#define INIT_PRINTF_L
+#endif
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+#define INIT_ISOC99_PRINTF                       \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_printf);    \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_sprintf);   \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_snprintf);  \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_fprintf);   \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vprintf);   \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vsprintf);  \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vsnprintf); \
+  COMMON_INTERCEPT_FUNCTION(__isoc99_vfprintf);
+#else
+#define INIT_ISOC99_PRINTF
+#endif
+
+#if SANITIZER_INTERCEPT_IOCTL
+#include "sanitizer_common_interceptors_ioctl.inc"
+INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) {
+  // We need a frame pointer, because we call into ioctl_common_[pre|post] which
+  // can trigger a report and we need to be able to unwind through this
+  // function.  On Mac in debug mode we might not have a frame pointer, because
+  // ioctl_common_[pre|post] doesn't get inlined here.
+  ENABLE_FRAME_POINTER;
+
+  void *ctx;
+  va_list ap;
+  va_start(ap, request);
+  void *arg = va_arg(ap, void *);
+  va_end(ap);
+  COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg);
+
+  CHECK(ioctl_initialized);
+
+  // Note: TSan does not use common flags, and they are zero-initialized.
+  // This effectively disables ioctl handling in TSan.
+  if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg);
+
+  // Although request is unsigned long, the rest of the interceptor uses it
+  // as just "unsigned" to save space, because we know that all values fit in
+  // "unsigned" - they are compile-time constants.
+
+  const ioctl_desc *desc = ioctl_lookup(request);
+  ioctl_desc decoded_desc;
+  if (!desc) {
+    VPrintf(2, "Decoding unknown ioctl 0x%x\n", request);
+    if (!ioctl_decode(request, &decoded_desc))
+      Printf("WARNING: failed decoding unknown ioctl 0x%x\n", request);
+    else
+      desc = &decoded_desc;
+  }
+
+  if (desc) ioctl_common_pre(ctx, desc, d, request, arg);
+  int res = REAL(ioctl)(d, request, arg);
+  // FIXME: some ioctls have different return values for success and failure.
+  if (desc && res != -1) ioctl_common_post(ctx, desc, res, d, request, arg);
+  return res;
+}
+#define INIT_IOCTL \
+  ioctl_init();    \
+  COMMON_INTERCEPT_FUNCTION(ioctl);
+#else
+#define INIT_IOCTL
+#endif
+
+#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \
+    SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \
+    SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) {
+  if (pwd) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd));
+    if (pwd->pw_name)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_name,
+                                          REAL(strlen)(pwd->pw_name) + 1);
+    if (pwd->pw_passwd)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_passwd,
+                                          REAL(strlen)(pwd->pw_passwd) + 1);
+#if !SANITIZER_ANDROID
+    if (pwd->pw_gecos)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_gecos,
+                                          REAL(strlen)(pwd->pw_gecos) + 1);
+#endif
+#if SANITIZER_MAC
+    if (pwd->pw_class)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_class,
+                                          REAL(strlen)(pwd->pw_class) + 1);
+#endif
+    if (pwd->pw_dir)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_dir,
+                                          REAL(strlen)(pwd->pw_dir) + 1);
+    if (pwd->pw_shell)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_shell,
+                                          REAL(strlen)(pwd->pw_shell) + 1);
+  }
+}
+
+static void unpoison_group(void *ctx, __sanitizer_group *grp) {
+  if (grp) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, sizeof(*grp));
+    if (grp->gr_name)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_name,
+                                          REAL(strlen)(grp->gr_name) + 1);
+    if (grp->gr_passwd)
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_passwd,
+                                          REAL(strlen)(grp->gr_passwd) + 1);
+    char **p = grp->gr_mem;
+    for (; *p; ++p) {
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(*p, REAL(strlen)(*p) + 1);
+    }
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_mem,
+                                        (p - grp->gr_mem + 1) * sizeof(*p));
+  }
+}
+#endif  // SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS ||
+        // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT ||
+        // SANITIZER_INTERCEPT_GETPWENT_R ||
+        // SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+
+#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
+INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+  __sanitizer_passwd *res = REAL(getpwnam)(name);
+  if (res) unpoison_passwd(ctx, res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid);
+  __sanitizer_passwd *res = REAL(getpwuid)(uid);
+  if (res) unpoison_passwd(ctx, res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+  __sanitizer_group *res = REAL(getgrnam)(name);
+  if (res) unpoison_group(ctx, res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid);
+  __sanitizer_group *res = REAL(getgrgid)(gid);
+  if (res) unpoison_group(ctx, res);
+  return res;
+}
+#define INIT_GETPWNAM_AND_FRIENDS      \
+  COMMON_INTERCEPT_FUNCTION(getpwnam); \
+  COMMON_INTERCEPT_FUNCTION(getpwuid); \
+  COMMON_INTERCEPT_FUNCTION(getgrnam); \
+  COMMON_INTERCEPT_FUNCTION(getgrgid);
+#else
+#define INIT_GETPWNAM_AND_FRIENDS
+#endif
+
+#if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+INTERCEPTOR(int, getpwnam_r, const char *name, __sanitizer_passwd *pwd,
+            char *buf, SIZE_T buflen, __sanitizer_passwd **result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result);
+  if (!res) {
+    if (result && *result) unpoison_passwd(ctx, *result);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+  return res;
+}
+INTERCEPTOR(int, getpwuid_r, u32 uid, __sanitizer_passwd *pwd, char *buf,
+            SIZE_T buflen, __sanitizer_passwd **result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result);
+  if (!res) {
+    if (result && *result) unpoison_passwd(ctx, *result);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+  return res;
+}
+INTERCEPTOR(int, getgrnam_r, const char *name, __sanitizer_group *grp,
+            char *buf, SIZE_T buflen, __sanitizer_group **result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getgrnam_r)(name, grp, buf, buflen, result);
+  if (!res) {
+    if (result && *result) unpoison_group(ctx, *result);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+  return res;
+}
+INTERCEPTOR(int, getgrgid_r, u32 gid, __sanitizer_group *grp, char *buf,
+            SIZE_T buflen, __sanitizer_group **result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result);
+  if (!res) {
+    if (result && *result) unpoison_group(ctx, *result);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+  return res;
+}
+#define INIT_GETPWNAM_R_AND_FRIENDS      \
+  COMMON_INTERCEPT_FUNCTION(getpwnam_r); \
+  COMMON_INTERCEPT_FUNCTION(getpwuid_r); \
+  COMMON_INTERCEPT_FUNCTION(getgrnam_r); \
+  COMMON_INTERCEPT_FUNCTION(getgrgid_r);
+#else
+#define INIT_GETPWNAM_R_AND_FRIENDS
+#endif
+
+#if SANITIZER_INTERCEPT_GETPWENT
+INTERCEPTOR(__sanitizer_passwd *, getpwent, int dummy) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy);
+  __sanitizer_passwd *res = REAL(getpwent)(dummy);
+  if (res) unpoison_passwd(ctx, res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy);
+  __sanitizer_group *res = REAL(getgrent)(dummy);
+  if (res) unpoison_group(ctx, res);;
+  return res;
+}
+#define INIT_GETPWENT                  \
+  COMMON_INTERCEPT_FUNCTION(getpwent); \
+  COMMON_INTERCEPT_FUNCTION(getgrent);
+#else
+#define INIT_GETPWENT
+#endif
+
+#if SANITIZER_INTERCEPT_FGETPWENT
+INTERCEPTOR(__sanitizer_passwd *, fgetpwent, void *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp);
+  __sanitizer_passwd *res = REAL(fgetpwent)(fp);
+  if (res) unpoison_passwd(ctx, res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp);
+  __sanitizer_group *res = REAL(fgetgrent)(fp);
+  if (res) unpoison_group(ctx, res);
+  return res;
+}
+#define INIT_FGETPWENT                  \
+  COMMON_INTERCEPT_FUNCTION(fgetpwent); \
+  COMMON_INTERCEPT_FUNCTION(fgetgrent);
+#else
+#define INIT_FGETPWENT
+#endif
+
+#if SANITIZER_INTERCEPT_GETPWENT_R
+INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf,
+            SIZE_T buflen, __sanitizer_passwd **pwbufp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpwent_r, pwbuf, buf, buflen, pwbufp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp);
+  if (!res) {
+    if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+  return res;
+}
+INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf,
+            SIZE_T buflen, __sanitizer_passwd **pwbufp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp);
+  if (!res) {
+    if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+  return res;
+}
+INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen,
+            __sanitizer_group **pwbufp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp);
+  if (!res) {
+    if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+  return res;
+}
+INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf,
+            SIZE_T buflen, __sanitizer_group **pwbufp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent_r, fp, pwbuf, buf, buflen, pwbufp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp);
+  if (!res) {
+    if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+  }
+  if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+  return res;
+}
+#define INIT_GETPWENT_R                   \
+  COMMON_INTERCEPT_FUNCTION(getpwent_r);  \
+  COMMON_INTERCEPT_FUNCTION(fgetpwent_r); \
+  COMMON_INTERCEPT_FUNCTION(getgrent_r);  \
+  COMMON_INTERCEPT_FUNCTION(fgetgrent_r);
+#else
+#define INIT_GETPWENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_SETPWENT
+// The only thing these interceptors do is disable any nested interceptors.
+// These functions may open nss modules and call uninstrumented functions from
+// them, and we don't want things like strlen() to trigger.
+INTERCEPTOR(void, setpwent, int dummy) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, setpwent, dummy);
+  REAL(setpwent)(dummy);
+}
+INTERCEPTOR(void, endpwent, int dummy) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, endpwent, dummy);
+  REAL(endpwent)(dummy);
+}
+INTERCEPTOR(void, setgrent, int dummy) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, setgrent, dummy);
+  REAL(setgrent)(dummy);
+}
+INTERCEPTOR(void, endgrent, int dummy) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, endgrent, dummy);
+  REAL(endgrent)(dummy);
+}
+#define INIT_SETPWENT                  \
+  COMMON_INTERCEPT_FUNCTION(setpwent); \
+  COMMON_INTERCEPT_FUNCTION(endpwent); \
+  COMMON_INTERCEPT_FUNCTION(setgrent); \
+  COMMON_INTERCEPT_FUNCTION(endgrent);
+#else
+#define INIT_SETPWENT
+#endif
+
+#if SANITIZER_INTERCEPT_CLOCK_GETTIME
+INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(clock_getres)(clk_id, tp);
+  if (!res && tp) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz);
+  }
+  return res;
+}
+INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(clock_gettime)(clk_id, tp);
+  if (!res) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz);
+  }
+  return res;
+}
+INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, clock_settime, clk_id, tp);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz);
+  return REAL(clock_settime)(clk_id, tp);
+}
+#define INIT_CLOCK_GETTIME                  \
+  COMMON_INTERCEPT_FUNCTION(clock_getres);  \
+  COMMON_INTERCEPT_FUNCTION(clock_gettime); \
+  COMMON_INTERCEPT_FUNCTION(clock_settime);
+#else
+#define INIT_CLOCK_GETTIME
+#endif
+
+#if SANITIZER_INTERCEPT_GETITIMER
+INTERCEPTOR(int, getitimer, int which, void *curr_value) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getitimer)(which, curr_value);
+  if (!res && curr_value) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz);
+  }
+  return res;
+}
+INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value);
+  if (new_value)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(setitimer)(which, new_value, old_value);
+  if (!res && old_value) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz);
+  }
+  return res;
+}
+#define INIT_GETITIMER                  \
+  COMMON_INTERCEPT_FUNCTION(getitimer); \
+  COMMON_INTERCEPT_FUNCTION(setitimer);
+#else
+#define INIT_GETITIMER
+#endif
+
+#if SANITIZER_INTERCEPT_GLOB
+static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) {
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob));
+  // +1 for NULL pointer at the end.
+  if (pglob->gl_pathv)
+    COMMON_INTERCEPTOR_WRITE_RANGE(
+        ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv));
+  for (SIZE_T i = 0; i < pglob->gl_pathc; ++i) {
+    char *p = pglob->gl_pathv[i];
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, REAL(strlen)(p) + 1);
+  }
+}
+
+static THREADLOCAL __sanitizer_glob_t *pglob_copy;
+
+static void wrapped_gl_closedir(void *dir) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+  pglob_copy->gl_closedir(dir);
+}
+
+static void *wrapped_gl_readdir(void *dir) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+  return pglob_copy->gl_readdir(dir);
+}
+
+static void *wrapped_gl_opendir(const char *s) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
+  return pglob_copy->gl_opendir(s);
+}
+
+static int wrapped_gl_lstat(const char *s, void *st) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
+  return pglob_copy->gl_lstat(s, st);
+}
+
+static int wrapped_gl_stat(const char *s, void *st) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
+  return pglob_copy->gl_stat(s, st);
+}
+
+INTERCEPTOR(int, glob, const char *pattern, int flags,
+            int (*errfunc)(const char *epath, int eerrno),
+            __sanitizer_glob_t *pglob) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
+  __sanitizer_glob_t glob_copy = {
+      0,                  0,                   0,
+      0,                  wrapped_gl_closedir, wrapped_gl_readdir,
+      wrapped_gl_opendir, wrapped_gl_lstat,    wrapped_gl_stat};
+  if (flags & glob_altdirfunc) {
+    Swap(pglob->gl_closedir, glob_copy.gl_closedir);
+    Swap(pglob->gl_readdir, glob_copy.gl_readdir);
+    Swap(pglob->gl_opendir, glob_copy.gl_opendir);
+    Swap(pglob->gl_lstat, glob_copy.gl_lstat);
+    Swap(pglob->gl_stat, glob_copy.gl_stat);
+    pglob_copy = &glob_copy;
+  }
+  int res = REAL(glob)(pattern, flags, errfunc, pglob);
+  if (flags & glob_altdirfunc) {
+    Swap(pglob->gl_closedir, glob_copy.gl_closedir);
+    Swap(pglob->gl_readdir, glob_copy.gl_readdir);
+    Swap(pglob->gl_opendir, glob_copy.gl_opendir);
+    Swap(pglob->gl_lstat, glob_copy.gl_lstat);
+    Swap(pglob->gl_stat, glob_copy.gl_stat);
+  }
+  pglob_copy = 0;
+  if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob);
+  return res;
+}
+
+INTERCEPTOR(int, glob64, const char *pattern, int flags,
+            int (*errfunc)(const char *epath, int eerrno),
+            __sanitizer_glob_t *pglob) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
+  __sanitizer_glob_t glob_copy = {
+      0,                  0,                   0,
+      0,                  wrapped_gl_closedir, wrapped_gl_readdir,
+      wrapped_gl_opendir, wrapped_gl_lstat,    wrapped_gl_stat};
+  if (flags & glob_altdirfunc) {
+    Swap(pglob->gl_closedir, glob_copy.gl_closedir);
+    Swap(pglob->gl_readdir, glob_copy.gl_readdir);
+    Swap(pglob->gl_opendir, glob_copy.gl_opendir);
+    Swap(pglob->gl_lstat, glob_copy.gl_lstat);
+    Swap(pglob->gl_stat, glob_copy.gl_stat);
+    pglob_copy = &glob_copy;
+  }
+  int res = REAL(glob64)(pattern, flags, errfunc, pglob);
+  if (flags & glob_altdirfunc) {
+    Swap(pglob->gl_closedir, glob_copy.gl_closedir);
+    Swap(pglob->gl_readdir, glob_copy.gl_readdir);
+    Swap(pglob->gl_opendir, glob_copy.gl_opendir);
+    Swap(pglob->gl_lstat, glob_copy.gl_lstat);
+    Swap(pglob->gl_stat, glob_copy.gl_stat);
+  }
+  pglob_copy = 0;
+  if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob);
+  return res;
+}
+#define INIT_GLOB                  \
+  COMMON_INTERCEPT_FUNCTION(glob); \
+  COMMON_INTERCEPT_FUNCTION(glob64);
+#else  // SANITIZER_INTERCEPT_GLOB
+#define INIT_GLOB
+#endif  // SANITIZER_INTERCEPT_GLOB
+
+#if SANITIZER_INTERCEPT_WAIT
+// According to sys/wait.h, wait(), waitid(), waitpid() may have symbol version
+// suffixes on Darwin. See the declaration of INTERCEPTOR_WITH_SUFFIX for
+// details.
+INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wait, status);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(wait)(status);
+  if (res != -1 && status)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
+  return res;
+}
+// On FreeBSD id_t is always 64-bit wide.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, long long id, void *infop,
+                        int options) {
+#else
+INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop,
+                        int options) {
+#endif
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(waitid)(idtype, id, infop, options);
+  if (res != -1 && infop)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz);
+  return res;
+}
+INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(waitpid)(pid, status, options);
+  if (res != -1 && status)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
+  return res;
+}
+INTERCEPTOR(int, wait3, int *status, int options, void *rusage) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(wait3)(status, options, rusage);
+  if (res != -1) {
+    if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
+    if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
+  }
+  return res;
+}
+#if SANITIZER_ANDROID
+INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __wait4, pid, status, options, rusage);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(__wait4)(pid, status, options, rusage);
+  if (res != -1) {
+    if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
+    if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
+  }
+  return res;
+}
+#define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(__wait4);
+#else
+INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(wait4)(pid, status, options, rusage);
+  if (res != -1) {
+    if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
+    if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
+  }
+  return res;
+}
+#define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(wait4);
+#endif  // SANITIZER_ANDROID
+#define INIT_WAIT                     \
+  COMMON_INTERCEPT_FUNCTION(wait);    \
+  COMMON_INTERCEPT_FUNCTION(waitid);  \
+  COMMON_INTERCEPT_FUNCTION(waitpid); \
+  COMMON_INTERCEPT_FUNCTION(wait3);
+#else
+#define INIT_WAIT
+#define INIT_WAIT4
+#endif
+
+#if SANITIZER_INTERCEPT_INET
+INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, inet_ntop, af, src, dst, size);
+  uptr sz = __sanitizer_in_addr_sz(af);
+  if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz);
+  // FIXME: figure out read size based on the address family.
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(inet_ntop)(af, src, dst, size);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0);
+  // FIXME: figure out read size based on the address family.
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(inet_pton)(af, src, dst);
+  if (res == 1) {
+    uptr sz = __sanitizer_in_addr_sz(af);
+    if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz);
+  }
+  return res;
+}
+#define INIT_INET                       \
+  COMMON_INTERCEPT_FUNCTION(inet_ntop); \
+  COMMON_INTERCEPT_FUNCTION(inet_pton);
+#else
+#define INIT_INET
+#endif
+
+#if SANITIZER_INTERCEPT_INET
+INTERCEPTOR(int, inet_aton, const char *cp, void *dst) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst);
+  if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(inet_aton)(cp, dst);
+  if (res != 0) {
+    uptr sz = __sanitizer_in_addr_sz(af_inet);
+    if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz);
+  }
+  return res;
+}
+#define INIT_INET_ATON COMMON_INTERCEPT_FUNCTION(inet_aton);
+#else
+#define INIT_INET_ATON
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM
+INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(pthread_getschedparam)(thread, policy, param);
+  if (res == 0) {
+    if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy));
+    if (param) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, sizeof(*param));
+  }
+  return res;
+}
+#define INIT_PTHREAD_GETSCHEDPARAM \
+  COMMON_INTERCEPT_FUNCTION(pthread_getschedparam);
+#else
+#define INIT_PTHREAD_GETSCHEDPARAM
+#endif
+
+#if SANITIZER_INTERCEPT_GETADDRINFO
+INTERCEPTOR(int, getaddrinfo, char *node, char *service,
+            struct __sanitizer_addrinfo *hints,
+            struct __sanitizer_addrinfo **out) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getaddrinfo, node, service, hints, out);
+  if (node) COMMON_INTERCEPTOR_READ_RANGE(ctx, node, REAL(strlen)(node) + 1);
+  if (service)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, service, REAL(strlen)(service) + 1);
+  if (hints)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo));
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getaddrinfo)(node, service, hints, out);
+  if (res == 0 && out) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out));
+    struct __sanitizer_addrinfo *p = *out;
+    while (p) {
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
+      if (p->ai_addr)
+        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, p->ai_addrlen);
+      if (p->ai_canonname)
+        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_canonname,
+                                       REAL(strlen)(p->ai_canonname) + 1);
+      p = p->ai_next;
+    }
+  }
+  return res;
+}
+#define INIT_GETADDRINFO COMMON_INTERCEPT_FUNCTION(getaddrinfo);
+#else
+#define INIT_GETADDRINFO
+#endif
+
+#if SANITIZER_INTERCEPT_GETNAMEINFO
+INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host,
+            unsigned hostlen, char *serv, unsigned servlen, int flags) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getnameinfo, sockaddr, salen, host, hostlen,
+                           serv, servlen, flags);
+  // FIXME: consider adding READ_RANGE(sockaddr, salen)
+  // There is padding in in_addr that may make this too noisy
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res =
+      REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags);
+  if (res == 0) {
+    if (host && hostlen)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, host, REAL(strlen)(host) + 1);
+    if (serv && servlen)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, serv, REAL(strlen)(serv) + 1);
+  }
+  return res;
+}
+#define INIT_GETNAMEINFO COMMON_INTERCEPT_FUNCTION(getnameinfo);
+#else
+#define INIT_GETNAMEINFO
+#endif
+
+#if SANITIZER_INTERCEPT_GETSOCKNAME
+INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
+  int addrlen_in = *addrlen;
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getsockname)(sock_fd, addr, addrlen);
+  if (res == 0) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen));
+  }
+  return res;
+}
+#define INIT_GETSOCKNAME COMMON_INTERCEPT_FUNCTION(getsockname);
+#else
+#define INIT_GETSOCKNAME
+#endif
+
+#if SANITIZER_INTERCEPT_GETHOSTBYNAME || SANITIZER_INTERCEPT_GETHOSTBYNAME_R
+static void write_hostent(void *ctx, struct __sanitizer_hostent *h) {
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h, sizeof(__sanitizer_hostent));
+  if (h->h_name)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h->h_name, REAL(strlen)(h->h_name) + 1);
+  char **p = h->h_aliases;
+  while (*p) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1);
+    ++p;
+  }
+  COMMON_INTERCEPTOR_WRITE_RANGE(
+      ctx, h->h_aliases, (p - h->h_aliases + 1) * sizeof(*h->h_aliases));
+  p = h->h_addr_list;
+  while (*p) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, h->h_length);
+    ++p;
+  }
+  COMMON_INTERCEPTOR_WRITE_RANGE(
+      ctx, h->h_addr_list, (p - h->h_addr_list + 1) * sizeof(*h->h_addr_list));
+}
+#endif
+
+#if SANITIZER_INTERCEPT_GETHOSTBYNAME
+INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname, char *name) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname, name);
+  struct __sanitizer_hostent *res = REAL(gethostbyname)(name);
+  if (res) write_hostent(ctx, res);
+  return res;
+}
+
+INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len,
+            int type) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr, addr, len, type);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len);
+  struct __sanitizer_hostent *res = REAL(gethostbyaddr)(addr, len, type);
+  if (res) write_hostent(ctx, res);
+  return res;
+}
+
+INTERCEPTOR(struct __sanitizer_hostent *, gethostent, int fake) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostent, fake);
+  struct __sanitizer_hostent *res = REAL(gethostent)(fake);
+  if (res) write_hostent(ctx, res);
+  return res;
+}
+
+INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2, name, af);
+  struct __sanitizer_hostent *res = REAL(gethostbyname2)(name, af);
+  if (res) write_hostent(ctx, res);
+  return res;
+}
+#define INIT_GETHOSTBYNAME                  \
+  COMMON_INTERCEPT_FUNCTION(gethostent);    \
+  COMMON_INTERCEPT_FUNCTION(gethostbyaddr); \
+  COMMON_INTERCEPT_FUNCTION(gethostbyname); \
+  COMMON_INTERCEPT_FUNCTION(gethostbyname2);
+#else
+#define INIT_GETHOSTBYNAME
+#endif
+
+#if SANITIZER_INTERCEPT_GETHOSTBYNAME_R
+INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret,
+            char *buf, SIZE_T buflen, __sanitizer_hostent **result,
+            int *h_errnop) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname_r, name, ret, buf, buflen, result,
+                           h_errnop);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop);
+  if (result) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+    if (res == 0 && *result) write_hostent(ctx, *result);
+  }
+  if (h_errnop)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+  return res;
+}
+#define INIT_GETHOSTBYNAME_R COMMON_INTERCEPT_FUNCTION(gethostbyname_r);
+#else
+#define INIT_GETHOSTBYNAME_R
+#endif
+
+#if SANITIZER_INTERCEPT_GETHOSTENT_R
+INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf,
+            SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostent_r, ret, buf, buflen, result,
+                           h_errnop);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop);
+  if (result) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+    if (res == 0 && *result) write_hostent(ctx, *result);
+  }
+  if (h_errnop)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+  return res;
+}
+#define INIT_GETHOSTENT_R                  \
+  COMMON_INTERCEPT_FUNCTION(gethostent_r);
+#else
+#define INIT_GETHOSTENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_GETHOSTBYADDR_R
+INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type,
+            struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen,
+            __sanitizer_hostent **result, int *h_errnop) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr_r, addr, len, type, ret, buf,
+                           buflen, result, h_errnop);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result,
+                                  h_errnop);
+  if (result) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+    if (res == 0 && *result) write_hostent(ctx, *result);
+  }
+  if (h_errnop)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+  return res;
+}
+#define INIT_GETHOSTBYADDR_R                  \
+  COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r);
+#else
+#define INIT_GETHOSTBYADDR_R
+#endif
+
+#if SANITIZER_INTERCEPT_GETHOSTBYNAME2_R
+INTERCEPTOR(int, gethostbyname2_r, char *name, int af,
+            struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen,
+            __sanitizer_hostent **result, int *h_errnop) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2_r, name, af, ret, buf, buflen,
+                           result, h_errnop);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res =
+      REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop);
+  if (result) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+    if (res == 0 && *result) write_hostent(ctx, *result);
+  }
+  if (h_errnop)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+  return res;
+}
+#define INIT_GETHOSTBYNAME2_R                  \
+  COMMON_INTERCEPT_FUNCTION(gethostbyname2_r);
+#else
+#define INIT_GETHOSTBYNAME2_R
+#endif
+
+#if SANITIZER_INTERCEPT_GETSOCKOPT
+INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval,
+            int *optlen) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getsockopt, sockfd, level, optname, optval,
+                           optlen);
+  if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen));
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen);
+  if (res == 0)
+    if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen);
+  return res;
+}
+#define INIT_GETSOCKOPT COMMON_INTERCEPT_FUNCTION(getsockopt);
+#else
+#define INIT_GETSOCKOPT
+#endif
+
+#if SANITIZER_INTERCEPT_ACCEPT
+INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen);
+  unsigned addrlen0 = 0;
+  if (addrlen) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
+    addrlen0 = *addrlen;
+  }
+  int fd2 = REAL(accept)(fd, addr, addrlen);
+  if (fd2 >= 0) {
+    if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
+    if (addr && addrlen)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0));
+  }
+  return fd2;
+}
+#define INIT_ACCEPT COMMON_INTERCEPT_FUNCTION(accept);
+#else
+#define INIT_ACCEPT
+#endif
+
+#if SANITIZER_INTERCEPT_ACCEPT4
+INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f);
+  unsigned addrlen0 = 0;
+  if (addrlen) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
+    addrlen0 = *addrlen;
+  }
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int fd2 = REAL(accept4)(fd, addr, addrlen, f);
+  if (fd2 >= 0) {
+    if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
+    if (addr && addrlen)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0));
+  }
+  return fd2;
+}
+#define INIT_ACCEPT4 COMMON_INTERCEPT_FUNCTION(accept4);
+#else
+#define INIT_ACCEPT4
+#endif
+
+#if SANITIZER_INTERCEPT_MODF
+INTERCEPTOR(double, modf, double x, double *iptr) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  double res = REAL(modf)(x, iptr);
+  if (iptr) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
+  }
+  return res;
+}
+INTERCEPTOR(float, modff, float x, float *iptr) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  float res = REAL(modff)(x, iptr);
+  if (iptr) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
+  }
+  return res;
+}
+INTERCEPTOR(long double, modfl, long double x, long double *iptr) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  long double res = REAL(modfl)(x, iptr);
+  if (iptr) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
+  }
+  return res;
+}
+#define INIT_MODF                   \
+  COMMON_INTERCEPT_FUNCTION(modf);  \
+  COMMON_INTERCEPT_FUNCTION(modff); \
+  COMMON_INTERCEPT_FUNCTION(modfl);
+#else
+#define INIT_MODF
+#endif
+
+#if SANITIZER_INTERCEPT_RECVMSG
+static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg,
+                         SSIZE_T maxlen) {
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg));
+  if (msg->msg_name && msg->msg_namelen)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, msg->msg_namelen);
+  if (msg->msg_iov && msg->msg_iovlen)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov,
+                                   sizeof(*msg->msg_iov) * msg->msg_iovlen);
+  write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen);
+  if (msg->msg_control && msg->msg_controllen)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen);
+}
+
+INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg,
+            int flags) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(recvmsg)(fd, msg, flags);
+  if (res >= 0) {
+    if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+    if (msg) {
+      write_msghdr(ctx, msg, res);
+      COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg);
+    }
+  }
+  return res;
+}
+#define INIT_RECVMSG COMMON_INTERCEPT_FUNCTION(recvmsg);
+#else
+#define INIT_RECVMSG
+#endif
+
+#if SANITIZER_INTERCEPT_GETPEERNAME
+INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen);
+  unsigned addr_sz;
+  if (addrlen) addr_sz = *addrlen;
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getpeername)(sockfd, addr, addrlen);
+  if (!res && addr && addrlen)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen));
+  return res;
+}
+#define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername);
+#else
+#define INIT_GETPEERNAME
+#endif
+
+#if SANITIZER_INTERCEPT_SYSINFO
+INTERCEPTOR(int, sysinfo, void *info) {
+  void *ctx;
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info);
+  int res = REAL(sysinfo)(info);
+  if (!res && info)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz);
+  return res;
+}
+#define INIT_SYSINFO COMMON_INTERCEPT_FUNCTION(sysinfo);
+#else
+#define INIT_SYSINFO
+#endif
+
+#if SANITIZER_INTERCEPT_READDIR
+INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, opendir, path);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  __sanitizer_dirent *res = REAL(opendir)(path);
+  if (res)
+    COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path);
+  return res;
+}
+
+INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  __sanitizer_dirent *res = REAL(readdir)(dirp);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen);
+  return res;
+}
+
+INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry,
+            __sanitizer_dirent **result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(readdir_r)(dirp, entry, result);
+  if (!res) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+    if (*result)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen);
+  }
+  return res;
+}
+
+#define INIT_READDIR                  \
+  COMMON_INTERCEPT_FUNCTION(opendir); \
+  COMMON_INTERCEPT_FUNCTION(readdir); \
+  COMMON_INTERCEPT_FUNCTION(readdir_r);
+#else
+#define INIT_READDIR
+#endif
+
+#if SANITIZER_INTERCEPT_READDIR64
+INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  __sanitizer_dirent64 *res = REAL(readdir64)(dirp);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen);
+  return res;
+}
+
+INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry,
+            __sanitizer_dirent64 **result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(readdir64_r)(dirp, entry, result);
+  if (!res) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+    if (*result)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen);
+  }
+  return res;
+}
+#define INIT_READDIR64                  \
+  COMMON_INTERCEPT_FUNCTION(readdir64); \
+  COMMON_INTERCEPT_FUNCTION(readdir64_r);
+#else
+#define INIT_READDIR64
+#endif
+
+#if SANITIZER_INTERCEPT_PTRACE
+INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data);
+  __sanitizer_iovec local_iovec;
+
+  if (data) {
+    if (request == ptrace_setregs)
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz);
+    else if (request == ptrace_setfpregs)
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz);
+    else if (request == ptrace_setfpxregs)
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
+    else if (request == ptrace_setvfpregs)
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
+    else if (request == ptrace_setsiginfo)
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz);
+    // Some kernel might zero the iovec::iov_base in case of invalid
+    // write access.  In this case copy the invalid address for further
+    // inspection.
+    else if (request == ptrace_setregset || request == ptrace_getregset) {
+      __sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec));
+      local_iovec = *iovec;
+      if (request == ptrace_setregset)
+        COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec->iov_base, iovec->iov_len);
+    }
+  }
+
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  uptr res = REAL(ptrace)(request, pid, addr, data);
+
+  if (!res && data) {
+    // Note that PEEK* requests assign different meaning to the return value.
+    // This function does not handle them (nor does it need to).
+    if (request == ptrace_getregs)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz);
+    else if (request == ptrace_getfpregs)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz);
+    else if (request == ptrace_getfpxregs)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
+    else if (request == ptrace_getvfpregs)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
+    else if (request == ptrace_getsiginfo)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz);
+    else if (request == ptrace_geteventmsg)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long));
+    else if (request == ptrace_getregset) {
+      __sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec));
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base,
+                                     local_iovec.iov_len);
+    }
+  }
+  return res;
+}
+
+#define INIT_PTRACE COMMON_INTERCEPT_FUNCTION(ptrace);
+#else
+#define INIT_PTRACE
+#endif
+
+#if SANITIZER_INTERCEPT_SETLOCALE
+INTERCEPTOR(char *, setlocale, int category, char *locale) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, setlocale, category, locale);
+  if (locale)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1);
+  char *res = REAL(setlocale)(category, locale);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+
+#define INIT_SETLOCALE COMMON_INTERCEPT_FUNCTION(setlocale);
+#else
+#define INIT_SETLOCALE
+#endif
+
+#if SANITIZER_INTERCEPT_GETCWD
+INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(getcwd)(buf, size);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+#define INIT_GETCWD COMMON_INTERCEPT_FUNCTION(getcwd);
+#else
+#define INIT_GETCWD
+#endif
+
+#if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME
+INTERCEPTOR(char *, get_current_dir_name, int fake) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(get_current_dir_name)(fake);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+
+#define INIT_GET_CURRENT_DIR_NAME \
+  COMMON_INTERCEPT_FUNCTION(get_current_dir_name);
+#else
+#define INIT_GET_CURRENT_DIR_NAME
+#endif
+
+UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
+  CHECK(endptr);
+  if (nptr == *endptr) {
+    // No digits were found at strtol call, we need to find out the last
+    // symbol accessed by strtoll on our own.
+    // We get this symbol by skipping leading blanks and optional +/- sign.
+    while (IsSpace(*nptr)) nptr++;
+    if (*nptr == '+' || *nptr == '-') nptr++;
+    *endptr = const_cast<char *>(nptr);
+  }
+  CHECK(*endptr >= nptr);
+}
+
+UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr,
+                             char **endptr, char *real_endptr, int base) {
+  if (endptr) {
+    *endptr = real_endptr;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+  }
+  // If base has unsupported value, strtol can exit with EINVAL
+  // without reading any characters. So do additional checks only
+  // if base is valid.
+  bool is_valid_base = (base == 0) || (2 <= base && base <= 36);
+  if (is_valid_base) {
+    FixRealStrtolEndptr(nptr, &real_endptr);
+  }
+  COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ?
+                                 (real_endptr - nptr) + 1 : 0);
+}
+
+
+#if SANITIZER_INTERCEPT_STRTOIMAX
+INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *real_endptr;
+  INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
+  StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+  return res;
+}
+
+INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *real_endptr;
+  INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
+  StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+  return res;
+}
+
+#define INIT_STRTOIMAX                  \
+  COMMON_INTERCEPT_FUNCTION(strtoimax); \
+  COMMON_INTERCEPT_FUNCTION(strtoumax);
+#else
+#define INIT_STRTOIMAX
+#endif
+
+#if SANITIZER_INTERCEPT_MBSTOWCS
+INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(mbstowcs)(dest, src, len);
+  if (res != (SIZE_T) - 1 && dest) {
+    SIZE_T write_cnt = res + (res < len);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t));
+  }
+  return res;
+}
+
+INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len,
+            void *ps) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, mbsrtowcs, dest, src, len, ps);
+  if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+  if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps);
+  if (res != (SIZE_T)(-1) && dest && src) {
+    // This function, and several others, may or may not write the terminating
+    // \0 character. They write it iff they clear *src.
+    SIZE_T write_cnt = res + !*src;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t));
+  }
+  return res;
+}
+
+#define INIT_MBSTOWCS                  \
+  COMMON_INTERCEPT_FUNCTION(mbstowcs); \
+  COMMON_INTERCEPT_FUNCTION(mbsrtowcs);
+#else
+#define INIT_MBSTOWCS
+#endif
+
+#if SANITIZER_INTERCEPT_MBSNRTOWCS
+INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms,
+            SIZE_T len, void *ps) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, mbsnrtowcs, dest, src, nms, len, ps);
+  if (src) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+    if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms);
+  }
+  if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps);
+  if (res != (SIZE_T)(-1) && dest && src) {
+    SIZE_T write_cnt = res + !*src;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t));
+  }
+  return res;
+}
+
+#define INIT_MBSNRTOWCS COMMON_INTERCEPT_FUNCTION(mbsnrtowcs);
+#else
+#define INIT_MBSNRTOWCS
+#endif
+
+#if SANITIZER_INTERCEPT_WCSTOMBS
+INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(wcstombs)(dest, src, len);
+  if (res != (SIZE_T) - 1 && dest) {
+    SIZE_T write_cnt = res + (res < len);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
+  }
+  return res;
+}
+
+INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len,
+            void *ps) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcsrtombs, dest, src, len, ps);
+  if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+  if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps);
+  if (res != (SIZE_T) - 1 && dest && src) {
+    SIZE_T write_cnt = res + !*src;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
+  }
+  return res;
+}
+
+#define INIT_WCSTOMBS                  \
+  COMMON_INTERCEPT_FUNCTION(wcstombs); \
+  COMMON_INTERCEPT_FUNCTION(wcsrtombs);
+#else
+#define INIT_WCSTOMBS
+#endif
+
+#if SANITIZER_INTERCEPT_WCSNRTOMBS
+INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms,
+            SIZE_T len, void *ps) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcsnrtombs, dest, src, nms, len, ps);
+  if (src) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+    if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms);
+  }
+  if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps);
+  if (res != ((SIZE_T)-1) && dest && src) {
+    SIZE_T write_cnt = res + !*src;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
+  }
+  return res;
+}
+
+#define INIT_WCSNRTOMBS COMMON_INTERCEPT_FUNCTION(wcsnrtombs);
+#else
+#define INIT_WCSNRTOMBS
+#endif
+
+
+#if SANITIZER_INTERCEPT_WCRTOMB
+INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps);
+  if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(wcrtomb)(dest, src, ps);
+  if (res != ((SIZE_T)-1) && dest) {
+    SIZE_T write_cnt = res;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
+  }
+  return res;
+}
+
+#define INIT_WCRTOMB COMMON_INTERCEPT_FUNCTION(wcrtomb);
+#else
+#define INIT_WCRTOMB
+#endif
+
+#if SANITIZER_INTERCEPT_TCGETATTR
+INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(tcgetattr)(fd, termios_p);
+  if (!res && termios_p)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz);
+  return res;
+}
+
+#define INIT_TCGETATTR COMMON_INTERCEPT_FUNCTION(tcgetattr);
+#else
+#define INIT_TCGETATTR
+#endif
+
+#if SANITIZER_INTERCEPT_REALPATH
+INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+
+  // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest
+  // version of a versioned symbol. For realpath(), this gives us something
+  // (called __old_realpath) that does not handle NULL in the second argument.
+  // Handle it as part of the interceptor.
+  char *allocated_path = nullptr;
+  if (!resolved_path)
+    allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1);
+
+  char *res = REAL(realpath)(path, resolved_path);
+  if (allocated_path && !res) WRAP(free)(allocated_path);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+#define INIT_REALPATH COMMON_INTERCEPT_FUNCTION(realpath);
+#else
+#define INIT_REALPATH
+#endif
+
+#if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME
+INTERCEPTOR(char *, canonicalize_file_name, const char *path) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  char *res = REAL(canonicalize_file_name)(path);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+#define INIT_CANONICALIZE_FILE_NAME \
+  COMMON_INTERCEPT_FUNCTION(canonicalize_file_name);
+#else
+#define INIT_CANONICALIZE_FILE_NAME
+#endif
+
+#if SANITIZER_INTERCEPT_CONFSTR
+INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(confstr)(name, buf, len);
+  if (buf && res)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len);
+  return res;
+}
+#define INIT_CONFSTR COMMON_INTERCEPT_FUNCTION(confstr);
+#else
+#define INIT_CONFSTR
+#endif
+
+#if SANITIZER_INTERCEPT_SCHED_GETAFFINITY
+INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sched_getaffinity)(pid, cpusetsize, mask);
+  if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize);
+  return res;
+}
+#define INIT_SCHED_GETAFFINITY COMMON_INTERCEPT_FUNCTION(sched_getaffinity);
+#else
+#define INIT_SCHED_GETAFFINITY
+#endif
+
+#if SANITIZER_INTERCEPT_SCHED_GETPARAM
+INTERCEPTOR(int, sched_getparam, int pid, void *param) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sched_getparam, pid, param);
+  int res = REAL(sched_getparam)(pid, param);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, struct_sched_param_sz);
+  return res;
+}
+#define INIT_SCHED_GETPARAM COMMON_INTERCEPT_FUNCTION(sched_getparam);
+#else
+#define INIT_SCHED_GETPARAM
+#endif
+
+#if SANITIZER_INTERCEPT_STRERROR
+INTERCEPTOR(char *, strerror, int errnum) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum);
+  char *res = REAL(strerror)(errnum);
+  if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
+  return res;
+}
+#define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror);
+#else
+#define INIT_STRERROR
+#endif
+
+#if SANITIZER_INTERCEPT_STRERROR_R
+INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(strerror_r)(errnum, buf, buflen);
+  // There are 2 versions of strerror_r:
+  //  * POSIX version returns 0 on success, negative error code on failure,
+  //    writes message to buf.
+  //  * GNU version returns message pointer, which points to either buf or some
+  //    static storage.
+  SIZE_T posix_res = (SIZE_T)res;
+  if (posix_res < 1024 || posix_res > (SIZE_T) - 1024) {
+    // POSIX version. Spec is not clear on whether buf is NULL-terminated.
+    // At least on OSX, buf contents are valid even when the call fails.
+    SIZE_T sz = internal_strnlen(buf, buflen);
+    if (sz < buflen) ++sz;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz);
+  } else {
+    // GNU version.
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+#define INIT_STRERROR_R COMMON_INTERCEPT_FUNCTION(strerror_r);
+#else
+#define INIT_STRERROR_R
+#endif
+
+#if SANITIZER_INTERCEPT_XPG_STRERROR_R
+INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(__xpg_strerror_r)(errnum, buf, buflen);
+  // This version always returns a null-terminated string.
+  if (buf && buflen)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+  return res;
+}
+#define INIT_XPG_STRERROR_R COMMON_INTERCEPT_FUNCTION(__xpg_strerror_r);
+#else
+#define INIT_XPG_STRERROR_R
+#endif
+
+#if SANITIZER_INTERCEPT_SCANDIR
+typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *);
+typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **,
+                                const struct __sanitizer_dirent **);
+
+static THREADLOCAL scandir_filter_f scandir_filter;
+static THREADLOCAL scandir_compar_f scandir_compar;
+
+static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
+  return scandir_filter(dir);
+}
+
+static int wrapped_scandir_compar(const struct __sanitizer_dirent **a,
+                                  const struct __sanitizer_dirent **b) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a));
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
+  return scandir_compar(a, b);
+}
+
+INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist,
+            scandir_filter_f filter, scandir_compar_f compar) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, scandir, dirp, namelist, filter, compar);
+  if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1);
+  scandir_filter = filter;
+  scandir_compar = compar;
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(scandir)(dirp, namelist,
+                          filter ? wrapped_scandir_filter : nullptr,
+                          compar ? wrapped_scandir_compar : nullptr);
+  scandir_filter = nullptr;
+  scandir_compar = nullptr;
+  if (namelist && res > 0) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
+    for (int i = 0; i < res; ++i)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i],
+                                     (*namelist)[i]->d_reclen);
+  }
+  return res;
+}
+#define INIT_SCANDIR COMMON_INTERCEPT_FUNCTION(scandir);
+#else
+#define INIT_SCANDIR
+#endif
+
+#if SANITIZER_INTERCEPT_SCANDIR64
+typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *);
+typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **,
+                                  const struct __sanitizer_dirent64 **);
+
+static THREADLOCAL scandir64_filter_f scandir64_filter;
+static THREADLOCAL scandir64_compar_f scandir64_compar;
+
+static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
+  return scandir64_filter(dir);
+}
+
+static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a,
+                                    const struct __sanitizer_dirent64 **b) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a));
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
+  return scandir64_compar(a, b);
+}
+
+INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist,
+            scandir64_filter_f filter, scandir64_compar_f compar) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, scandir64, dirp, namelist, filter, compar);
+  if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1);
+  scandir64_filter = filter;
+  scandir64_compar = compar;
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res =
+      REAL(scandir64)(dirp, namelist,
+                      filter ? wrapped_scandir64_filter : nullptr,
+                      compar ? wrapped_scandir64_compar : nullptr);
+  scandir64_filter = nullptr;
+  scandir64_compar = nullptr;
+  if (namelist && res > 0) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
+    for (int i = 0; i < res; ++i)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i],
+                                     (*namelist)[i]->d_reclen);
+  }
+  return res;
+}
+#define INIT_SCANDIR64 COMMON_INTERCEPT_FUNCTION(scandir64);
+#else
+#define INIT_SCANDIR64
+#endif
+
+#if SANITIZER_INTERCEPT_GETGROUPS
+INTERCEPTOR(int, getgroups, int size, u32 *lst) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getgroups)(size, lst);
+  if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst));
+  return res;
+}
+#define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups);
+#else
+#define INIT_GETGROUPS
+#endif
+
+#if SANITIZER_INTERCEPT_POLL
+static void read_pollfd(void *ctx, __sanitizer_pollfd *fds,
+                        __sanitizer_nfds_t nfds) {
+  for (unsigned i = 0; i < nfds; ++i) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].fd, sizeof(fds[i].fd));
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].events, sizeof(fds[i].events));
+  }
+}
+
+static void write_pollfd(void *ctx, __sanitizer_pollfd *fds,
+                         __sanitizer_nfds_t nfds) {
+  for (unsigned i = 0; i < nfds; ++i)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &fds[i].revents,
+                                   sizeof(fds[i].revents));
+}
+
+INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds,
+            int timeout) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, poll, fds, nfds, timeout);
+  if (fds && nfds) read_pollfd(ctx, fds, nfds);
+  int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout);
+  if (fds && nfds) write_pollfd(ctx, fds, nfds);
+  return res;
+}
+#define INIT_POLL COMMON_INTERCEPT_FUNCTION(poll);
+#else
+#define INIT_POLL
+#endif
+
+#if SANITIZER_INTERCEPT_PPOLL
+INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds,
+            void *timeout_ts, __sanitizer_sigset_t *sigmask) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ppoll, fds, nfds, timeout_ts, sigmask);
+  if (fds && nfds) read_pollfd(ctx, fds, nfds);
+  if (timeout_ts)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz);
+  // FIXME: read sigmask when all of sigemptyset, etc are intercepted.
+  int res =
+      COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask);
+  if (fds && nfds) write_pollfd(ctx, fds, nfds);
+  return res;
+}
+#define INIT_PPOLL COMMON_INTERCEPT_FUNCTION(ppoll);
+#else
+#define INIT_PPOLL
+#endif
+
+#if SANITIZER_INTERCEPT_WORDEXP
+INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags);
+  if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(wordexp)(s, p, flags);
+  if (!res && p) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
+    if (p->we_wordc)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->we_wordv,
+                                     sizeof(*p->we_wordv) * p->we_wordc);
+    for (uptr i = 0; i < p->we_wordc; ++i) {
+      char *w = p->we_wordv[i];
+      if (w) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, w, REAL(strlen)(w) + 1);
+    }
+  }
+  return res;
+}
+#define INIT_WORDEXP COMMON_INTERCEPT_FUNCTION(wordexp);
+#else
+#define INIT_WORDEXP
+#endif
+
+#if SANITIZER_INTERCEPT_SIGWAIT
+INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig);
+  // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sigwait)(set, sig);
+  if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig));
+  return res;
+}
+#define INIT_SIGWAIT COMMON_INTERCEPT_FUNCTION(sigwait);
+#else
+#define INIT_SIGWAIT
+#endif
+
+#if SANITIZER_INTERCEPT_SIGWAITINFO
+INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info);
+  // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sigwaitinfo)(set, info);
+  if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz);
+  return res;
+}
+#define INIT_SIGWAITINFO COMMON_INTERCEPT_FUNCTION(sigwaitinfo);
+#else
+#define INIT_SIGWAITINFO
+#endif
+
+#if SANITIZER_INTERCEPT_SIGTIMEDWAIT
+INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info,
+            void *timeout) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sigtimedwait, set, info, timeout);
+  if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz);
+  // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sigtimedwait)(set, info, timeout);
+  if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz);
+  return res;
+}
+#define INIT_SIGTIMEDWAIT COMMON_INTERCEPT_FUNCTION(sigtimedwait);
+#else
+#define INIT_SIGTIMEDWAIT
+#endif
+
+#if SANITIZER_INTERCEPT_SIGSETOPS
+INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sigemptyset)(set);
+  if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
+  return res;
+}
+
+INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sigfillset)(set);
+  if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
+  return res;
+}
+#define INIT_SIGSETOPS                    \
+  COMMON_INTERCEPT_FUNCTION(sigemptyset); \
+  COMMON_INTERCEPT_FUNCTION(sigfillset);
+#else
+#define INIT_SIGSETOPS
+#endif
+
+#if SANITIZER_INTERCEPT_SIGPENDING
+INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sigpending)(set);
+  if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
+  return res;
+}
+#define INIT_SIGPENDING COMMON_INTERCEPT_FUNCTION(sigpending);
+#else
+#define INIT_SIGPENDING
+#endif
+
+#if SANITIZER_INTERCEPT_SIGPROCMASK
+INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
+            __sanitizer_sigset_t *oldset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset);
+  // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(sigprocmask)(how, set, oldset);
+  if (!res && oldset)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
+  return res;
+}
+#define INIT_SIGPROCMASK COMMON_INTERCEPT_FUNCTION(sigprocmask);
+#else
+#define INIT_SIGPROCMASK
+#endif
+
+#if SANITIZER_INTERCEPT_BACKTRACE
+INTERCEPTOR(int, backtrace, void **buffer, int size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(backtrace)(buffer, size);
+  if (res && buffer)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer));
+  return res;
+}
+
+INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size);
+  if (buffer && size)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer));
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char **res = REAL(backtrace_symbols)(buffer, size);
+  if (res && size) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res));
+    for (int i = 0; i < size; ++i)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res[i], REAL(strlen(res[i])) + 1);
+  }
+  return res;
+}
+#define INIT_BACKTRACE                  \
+  COMMON_INTERCEPT_FUNCTION(backtrace); \
+  COMMON_INTERCEPT_FUNCTION(backtrace_symbols);
+#else
+#define INIT_BACKTRACE
+#endif
+
+#if SANITIZER_INTERCEPT__EXIT
+INTERCEPTOR(void, _exit, int status) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, _exit, status);
+  int status1 = COMMON_INTERCEPTOR_ON_EXIT(ctx);
+  if (status == 0) status = status1;
+  REAL(_exit)(status);
+}
+#define INIT__EXIT COMMON_INTERCEPT_FUNCTION(_exit);
+#else
+#define INIT__EXIT
+#endif
+
+#if SANITIZER_INTERCEPT_PHTREAD_MUTEX
+INTERCEPTOR(int, pthread_mutex_lock, void *m) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m);
+  int res = REAL(pthread_mutex_lock)(m);
+  if (res == errno_EOWNERDEAD)
+    COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m);
+  if (res == 0 || res == errno_EOWNERDEAD)
+    COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
+  return res;
+}
+
+INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m);
+  COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m);
+  return REAL(pthread_mutex_unlock)(m);
+}
+
+#define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock)
+#define INIT_PTHREAD_MUTEX_UNLOCK \
+  COMMON_INTERCEPT_FUNCTION(pthread_mutex_unlock)
+#else
+#define INIT_PTHREAD_MUTEX_LOCK
+#define INIT_PTHREAD_MUTEX_UNLOCK
+#endif
+
+#if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R
+static void write_mntent(void *ctx, __sanitizer_mntent *mnt) {
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt));
+  if (mnt->mnt_fsname)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_fsname,
+                                   REAL(strlen)(mnt->mnt_fsname) + 1);
+  if (mnt->mnt_dir)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_dir,
+                                   REAL(strlen)(mnt->mnt_dir) + 1);
+  if (mnt->mnt_type)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_type,
+                                   REAL(strlen)(mnt->mnt_type) + 1);
+  if (mnt->mnt_opts)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_opts,
+                                   REAL(strlen)(mnt->mnt_opts) + 1);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_GETMNTENT
+INTERCEPTOR(__sanitizer_mntent *, getmntent, void *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getmntent, fp);
+  __sanitizer_mntent *res = REAL(getmntent)(fp);
+  if (res) write_mntent(ctx, res);
+  return res;
+}
+#define INIT_GETMNTENT COMMON_INTERCEPT_FUNCTION(getmntent);
+#else
+#define INIT_GETMNTENT
+#endif
+
+#if SANITIZER_INTERCEPT_GETMNTENT_R
+INTERCEPTOR(__sanitizer_mntent *, getmntent_r, void *fp,
+            __sanitizer_mntent *mntbuf, char *buf, int buflen) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getmntent_r, fp, mntbuf, buf, buflen);
+  __sanitizer_mntent *res = REAL(getmntent_r)(fp, mntbuf, buf, buflen);
+  if (res) write_mntent(ctx, res);
+  return res;
+}
+#define INIT_GETMNTENT_R COMMON_INTERCEPT_FUNCTION(getmntent_r);
+#else
+#define INIT_GETMNTENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_STATFS
+INTERCEPTOR(int, statfs, char *path, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(statfs)(path, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz);
+  return res;
+}
+INTERCEPTOR(int, fstatfs, int fd, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(fstatfs)(fd, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz);
+  return res;
+}
+#define INIT_STATFS                  \
+  COMMON_INTERCEPT_FUNCTION(statfs); \
+  COMMON_INTERCEPT_FUNCTION(fstatfs);
+#else
+#define INIT_STATFS
+#endif
+
+#if SANITIZER_INTERCEPT_STATFS64
+INTERCEPTOR(int, statfs64, char *path, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(statfs64)(path, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz);
+  return res;
+}
+INTERCEPTOR(int, fstatfs64, int fd, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(fstatfs64)(fd, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz);
+  return res;
+}
+#define INIT_STATFS64                  \
+  COMMON_INTERCEPT_FUNCTION(statfs64); \
+  COMMON_INTERCEPT_FUNCTION(fstatfs64);
+#else
+#define INIT_STATFS64
+#endif
+
+#if SANITIZER_INTERCEPT_STATVFS
+INTERCEPTOR(int, statvfs, char *path, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(statvfs)(path, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+  return res;
+}
+INTERCEPTOR(int, fstatvfs, int fd, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(fstatvfs)(fd, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+  return res;
+}
+#define INIT_STATVFS                  \
+  COMMON_INTERCEPT_FUNCTION(statvfs); \
+  COMMON_INTERCEPT_FUNCTION(fstatvfs);
+#else
+#define INIT_STATVFS
+#endif
+
+#if SANITIZER_INTERCEPT_STATVFS64
+INTERCEPTOR(int, statvfs64, char *path, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(statvfs64)(path, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz);
+  return res;
+}
+INTERCEPTOR(int, fstatvfs64, int fd, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(fstatvfs64)(fd, buf);
+  if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz);
+  return res;
+}
+#define INIT_STATVFS64                  \
+  COMMON_INTERCEPT_FUNCTION(statvfs64); \
+  COMMON_INTERCEPT_FUNCTION(fstatvfs64);
+#else
+#define INIT_STATVFS64
+#endif
+
+#if SANITIZER_INTERCEPT_INITGROUPS
+INTERCEPTOR(int, initgroups, char *user, u32 group) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, initgroups, user, group);
+  if (user) COMMON_INTERCEPTOR_READ_RANGE(ctx, user, REAL(strlen)(user) + 1);
+  int res = REAL(initgroups)(user, group);
+  return res;
+}
+#define INIT_INITGROUPS COMMON_INTERCEPT_FUNCTION(initgroups);
+#else
+#define INIT_INITGROUPS
+#endif
+
+#if SANITIZER_INTERCEPT_ETHER_NTOA_ATON
+INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr);
+  if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
+  char *res = REAL(ether_ntoa)(addr);
+  if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
+  return res;
+}
+INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf);
+  if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+  __sanitizer_ether_addr *res = REAL(ether_aton)(buf);
+  if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, sizeof(*res));
+  return res;
+}
+#define INIT_ETHER_NTOA_ATON             \
+  COMMON_INTERCEPT_FUNCTION(ether_ntoa); \
+  COMMON_INTERCEPT_FUNCTION(ether_aton);
+#else
+#define INIT_ETHER_NTOA_ATON
+#endif
+
+#if SANITIZER_INTERCEPT_ETHER_HOST
+INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr);
+  if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(ether_ntohost)(hostname, addr);
+  if (!res && hostname)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1);
+  return res;
+}
+INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr);
+  if (hostname)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(ether_hostton)(hostname, addr);
+  if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
+  return res;
+}
+INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr,
+            char *hostname) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname);
+  if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(ether_line)(line, addr, hostname);
+  if (!res) {
+    if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
+    if (hostname)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1);
+  }
+  return res;
+}
+#define INIT_ETHER_HOST                     \
+  COMMON_INTERCEPT_FUNCTION(ether_ntohost); \
+  COMMON_INTERCEPT_FUNCTION(ether_hostton); \
+  COMMON_INTERCEPT_FUNCTION(ether_line);
+#else
+#define INIT_ETHER_HOST
+#endif
+
+#if SANITIZER_INTERCEPT_ETHER_R
+INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf);
+  if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(ether_ntoa_r)(addr, buf);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+  return res;
+}
+INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf,
+            __sanitizer_ether_addr *addr) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr);
+  if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr);
+  if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res));
+  return res;
+}
+#define INIT_ETHER_R                       \
+  COMMON_INTERCEPT_FUNCTION(ether_ntoa_r); \
+  COMMON_INTERCEPT_FUNCTION(ether_aton_r);
+#else
+#define INIT_ETHER_R
+#endif
+
+#if SANITIZER_INTERCEPT_SHMCTL
+INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(shmctl)(shmid, cmd, buf);
+  if (res >= 0) {
+    unsigned sz = 0;
+    if (cmd == shmctl_ipc_stat || cmd == shmctl_shm_stat)
+      sz = sizeof(__sanitizer_shmid_ds);
+    else if (cmd == shmctl_ipc_info)
+      sz = struct_shminfo_sz;
+    else if (cmd == shmctl_shm_info)
+      sz = struct_shm_info_sz;
+    if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz);
+  }
+  return res;
+}
+#define INIT_SHMCTL COMMON_INTERCEPT_FUNCTION(shmctl);
+#else
+#define INIT_SHMCTL
+#endif
+
+#if SANITIZER_INTERCEPT_RANDOM_R
+INTERCEPTOR(int, random_r, void *buf, u32 *result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(random_r)(buf, result);
+  if (!res && result)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+  return res;
+}
+#define INIT_RANDOM_R COMMON_INTERCEPT_FUNCTION(random_r);
+#else
+#define INIT_RANDOM_R
+#endif
+
+// FIXME: under ASan the REAL() call below may write to freed memory and corrupt
+// its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET ||              \
+    SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \
+    SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET ||         \
+    SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GET ||        \
+    SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GET ||          \
+    SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GET
+#define INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(fn, sz)            \
+  INTERCEPTOR(int, fn, void *attr, void *r) {                  \
+    void *ctx;                                                 \
+    COMMON_INTERCEPTOR_ENTER(ctx, fn, attr, r);                \
+    int res = REAL(fn)(attr, r);                               \
+    if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \
+    return res;                                                \
+  }
+#define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \
+  INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_attr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_MUTEXATTR_GET(what, sz) \
+  INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_mutexattr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(what, sz) \
+  INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_rwlockattr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_CONDATTR_GET(what, sz) \
+  INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_condattr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_BARRIERATTR_GET(what, sz) \
+  INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_barrierattr_get##what, sz)
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET
+INTERCEPTOR_PTHREAD_ATTR_GET(detachstate, sizeof(int))
+INTERCEPTOR_PTHREAD_ATTR_GET(guardsize, sizeof(SIZE_T))
+INTERCEPTOR_PTHREAD_ATTR_GET(schedparam, struct_sched_param_sz)
+INTERCEPTOR_PTHREAD_ATTR_GET(schedpolicy, sizeof(int))
+INTERCEPTOR_PTHREAD_ATTR_GET(scope, sizeof(int))
+INTERCEPTOR_PTHREAD_ATTR_GET(stacksize, sizeof(SIZE_T))
+INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(pthread_attr_getstack)(attr, addr, size);
+  if (!res) {
+    if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
+    if (size) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, size, sizeof(*size));
+  }
+  return res;
+}
+
+// We may need to call the real pthread_attr_getstack from the run-time
+// in sanitizer_common, but we don't want to include the interception headers
+// there. So, just define this function here.
+namespace __sanitizer {
+extern "C" {
+int real_pthread_attr_getstack(void *attr, void **addr, SIZE_T *size) {
+  return REAL(pthread_attr_getstack)(attr, addr, size);
+}
+}  // extern "C"
+}  // namespace __sanitizer
+
+#define INIT_PTHREAD_ATTR_GET                             \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getguardsize);   \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedparam);  \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedpolicy); \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getscope);       \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getstacksize);   \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getstack);
+#else
+#define INIT_PTHREAD_ATTR_GET
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED
+INTERCEPTOR_PTHREAD_ATTR_GET(inheritsched, sizeof(int))
+
+#define INIT_PTHREAD_ATTR_GETINHERITSCHED \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getinheritsched);
+#else
+#define INIT_PTHREAD_ATTR_GETINHERITSCHED
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP
+INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize,
+            void *cpuset) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize,
+                           cpuset);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset);
+  if (!res && cpusetsize && cpuset)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize);
+  return res;
+}
+
+#define INIT_PTHREAD_ATTR_GETAFFINITY_NP \
+  COMMON_INTERCEPT_FUNCTION(pthread_attr_getaffinity_np);
+#else
+#define INIT_PTHREAD_ATTR_GETAFFINITY_NP
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(pshared, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETPSHARED \
+  COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getpshared);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETPSHARED
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(type, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETTYPE \
+  COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_gettype);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETTYPE
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(protocol, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL \
+  COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprotocol);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(prioceiling, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING \
+  COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprioceiling);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST \
+  COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust_np, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP \
+  COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust_np);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(pshared, sizeof(int))
+#define INIT_PTHREAD_RWLOCKATTR_GETPSHARED \
+  COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getpshared);
+#else
+#define INIT_PTHREAD_RWLOCKATTR_GETPSHARED
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP
+INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(kind_np, sizeof(int))
+#define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP \
+  COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getkind_np);
+#else
+#define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_CONDATTR_GET(pshared, sizeof(int))
+#define INIT_PTHREAD_CONDATTR_GETPSHARED \
+  COMMON_INTERCEPT_FUNCTION(pthread_condattr_getpshared);
+#else
+#define INIT_PTHREAD_CONDATTR_GETPSHARED
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK
+INTERCEPTOR_PTHREAD_CONDATTR_GET(clock, sizeof(int))
+#define INIT_PTHREAD_CONDATTR_GETCLOCK \
+  COMMON_INTERCEPT_FUNCTION(pthread_condattr_getclock);
+#else
+#define INIT_PTHREAD_CONDATTR_GETCLOCK
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_BARRIERATTR_GET(pshared, sizeof(int)) // !mac !android
+#define INIT_PTHREAD_BARRIERATTR_GETPSHARED \
+  COMMON_INTERCEPT_FUNCTION(pthread_barrierattr_getpshared);
+#else
+#define INIT_PTHREAD_BARRIERATTR_GETPSHARED
+#endif
+
+#if SANITIZER_INTERCEPT_TMPNAM
+INTERCEPTOR(char *, tmpnam, char *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, tmpnam, s);
+  char *res = REAL(tmpnam)(s);
+  if (res) {
+    if (s)
+      // FIXME: under ASan the call below may write to freed memory and corrupt
+      // its metadata. See
+      // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1);
+    else
+      COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+#define INIT_TMPNAM COMMON_INTERCEPT_FUNCTION(tmpnam);
+#else
+#define INIT_TMPNAM
+#endif
+
+#if SANITIZER_INTERCEPT_TMPNAM_R
+INTERCEPTOR(char *, tmpnam_r, char *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(tmpnam_r)(s);
+  if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1);
+  return res;
+}
+#define INIT_TMPNAM_R COMMON_INTERCEPT_FUNCTION(tmpnam_r);
+#else
+#define INIT_TMPNAM_R
+#endif
+
+#if SANITIZER_INTERCEPT_TEMPNAM
+INTERCEPTOR(char *, tempnam, char *dir, char *pfx) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, tempnam, dir, pfx);
+  if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1);
+  if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1);
+  char *res = REAL(tempnam)(dir, pfx);
+  if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
+  return res;
+}
+#define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam);
+#else
+#define INIT_TEMPNAM
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP
+INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name);
+  COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
+  COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
+  return REAL(pthread_setname_np)(thread, name);
+}
+#define INIT_PTHREAD_SETNAME_NP COMMON_INTERCEPT_FUNCTION(pthread_setname_np);
+#else
+#define INIT_PTHREAD_SETNAME_NP
+#endif
+
+#if SANITIZER_INTERCEPT_SINCOS
+INTERCEPTOR(void, sincos, double x, double *sin, double *cos) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  REAL(sincos)(x, sin, cos);
+  if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
+  if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
+}
+INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  REAL(sincosf)(x, sin, cos);
+  if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
+  if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
+}
+INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  REAL(sincosl)(x, sin, cos);
+  if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
+  if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
+}
+#define INIT_SINCOS                   \
+  COMMON_INTERCEPT_FUNCTION(sincos);  \
+  COMMON_INTERCEPT_FUNCTION(sincosf); \
+  COMMON_INTERCEPT_FUNCTION(sincosl);
+#else
+#define INIT_SINCOS
+#endif
+
+#if SANITIZER_INTERCEPT_REMQUO
+INTERCEPTOR(double, remquo, double x, double y, int *quo) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  double res = REAL(remquo)(x, y, quo);
+  if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
+  return res;
+}
+INTERCEPTOR(float, remquof, float x, float y, int *quo) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  float res = REAL(remquof)(x, y, quo);
+  if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
+  return res;
+}
+INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  long double res = REAL(remquol)(x, y, quo);
+  if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
+  return res;
+}
+#define INIT_REMQUO                   \
+  COMMON_INTERCEPT_FUNCTION(remquo);  \
+  COMMON_INTERCEPT_FUNCTION(remquof); \
+  COMMON_INTERCEPT_FUNCTION(remquol);
+#else
+#define INIT_REMQUO
+#endif
+
+#if SANITIZER_INTERCEPT_LGAMMA
+extern int signgam;
+INTERCEPTOR(double, lgamma, double x) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lgamma, x);
+  double res = REAL(lgamma)(x);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam));
+  return res;
+}
+INTERCEPTOR(float, lgammaf, float x) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lgammaf, x);
+  float res = REAL(lgammaf)(x);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam));
+  return res;
+}
+INTERCEPTOR(long double, lgammal, long double x) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x);
+  long double res = REAL(lgammal)(x);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam));
+  return res;
+}
+#define INIT_LGAMMA                   \
+  COMMON_INTERCEPT_FUNCTION(lgamma);  \
+  COMMON_INTERCEPT_FUNCTION(lgammaf); \
+  COMMON_INTERCEPT_FUNCTION(lgammal);
+#else
+#define INIT_LGAMMA
+#endif
+
+#if SANITIZER_INTERCEPT_LGAMMA_R
+INTERCEPTOR(double, lgamma_r, double x, int *signp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  double res = REAL(lgamma_r)(x, signp);
+  if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
+  return res;
+}
+INTERCEPTOR(float, lgammaf_r, float x, int *signp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  float res = REAL(lgammaf_r)(x, signp);
+  if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
+  return res;
+}
+#define INIT_LGAMMA_R                   \
+  COMMON_INTERCEPT_FUNCTION(lgamma_r);  \
+  COMMON_INTERCEPT_FUNCTION(lgammaf_r);
+#else
+#define INIT_LGAMMA_R
+#endif
+
+#if SANITIZER_INTERCEPT_LGAMMAL_R
+INTERCEPTOR(long double, lgammal_r, long double x, int *signp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  long double res = REAL(lgammal_r)(x, signp);
+  if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
+  return res;
+}
+#define INIT_LGAMMAL_R COMMON_INTERCEPT_FUNCTION(lgammal_r);
+#else
+#define INIT_LGAMMAL_R
+#endif
+
+#if SANITIZER_INTERCEPT_DRAND48_R
+INTERCEPTOR(int, drand48_r, void *buffer, double *result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(drand48_r)(buffer, result);
+  if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+  return res;
+}
+INTERCEPTOR(int, lrand48_r, void *buffer, long *result) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(lrand48_r)(buffer, result);
+  if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+  return res;
+}
+#define INIT_DRAND48_R                  \
+  COMMON_INTERCEPT_FUNCTION(drand48_r); \
+  COMMON_INTERCEPT_FUNCTION(lrand48_r);
+#else
+#define INIT_DRAND48_R
+#endif
+
+#if SANITIZER_INTERCEPT_RAND_R
+INTERCEPTOR(int, rand_r, unsigned *seedp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, rand_r, seedp);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, seedp, sizeof(*seedp));
+  return REAL(rand_r)(seedp);
+}
+#define INIT_RAND_R COMMON_INTERCEPT_FUNCTION(rand_r);
+#else
+#define INIT_RAND_R
+#endif
+
+#if SANITIZER_INTERCEPT_GETLINE
+INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(getline)(lineptr, n, stream);
+  if (res > 0) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1);
+  }
+  return res;
+}
+
+// FIXME: under ASan the call below may write to freed memory and corrupt its
+// metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define GETDELIM_INTERCEPTOR_IMPL(vname)                                       \
+  {                                                                            \
+    void *ctx;                                                                 \
+    COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream);           \
+    SSIZE_T res = REAL(vname)(lineptr, n, delim, stream);                      \
+    if (res > 0) {                                                             \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));          \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));                      \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1);                  \
+    }                                                                          \
+    return res;                                                                \
+  }
+
+INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
+            void *stream)
+GETDELIM_INTERCEPTOR_IMPL(__getdelim)
+
+// There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor
+// with its own body.
+INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim,
+            void *stream)
+GETDELIM_INTERCEPTOR_IMPL(getdelim)
+
+#define INIT_GETLINE                     \
+  COMMON_INTERCEPT_FUNCTION(getline);    \
+  COMMON_INTERCEPT_FUNCTION(__getdelim); \
+  COMMON_INTERCEPT_FUNCTION(getdelim);
+#else
+#define INIT_GETLINE
+#endif
+
+#if SANITIZER_INTERCEPT_ICONV
+INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft,
+            char **outbuf, SIZE_T *outbytesleft) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, iconv, cd, inbuf, inbytesleft, outbuf,
+                           outbytesleft);
+  if (inbytesleft)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, inbytesleft, sizeof(*inbytesleft));
+  if (inbuf && inbytesleft)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft);
+  if (outbytesleft)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft));
+  void *outbuf_orig = outbuf ? *outbuf : nullptr;
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft);
+  if (res != (SIZE_T) - 1 && outbuf && *outbuf > outbuf_orig) {
+    SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, outbuf_orig, sz);
+  }
+  return res;
+}
+#define INIT_ICONV COMMON_INTERCEPT_FUNCTION(iconv);
+#else
+#define INIT_ICONV
+#endif
+
+#if SANITIZER_INTERCEPT_TIMES
+INTERCEPTOR(__sanitizer_clock_t, times, void *tms) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, times, tms);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  __sanitizer_clock_t res = REAL(times)(tms);
+  if (res != (__sanitizer_clock_t)-1 && tms)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz);
+  return res;
+}
+#define INIT_TIMES COMMON_INTERCEPT_FUNCTION(times);
+#else
+#define INIT_TIMES
+#endif
+
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+#define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr)
+// If you see any crashes around this functions, there are 2 known issues with
+// it: 1. __tls_get_addr can be called with mis-aligned stack due to:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066
+// 2. It can be called recursively if sanitizer code uses __tls_get_addr
+// to access thread local variables (it should not happen normally,
+// because sanitizers use initial-exec tls model).
+INTERCEPTOR(void *, __tls_get_addr, void *arg) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg);
+  void *res = REAL(__tls_get_addr)(arg);
+  uptr tls_begin, tls_end;
+  COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end);
+  DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end);
+  if (dtv) {
+    // New DTLS block has been allocated.
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size);
+  }
+  return res;
+}
+#else
+#define INIT_TLS_GET_ADDR
+#endif
+
+#if SANITIZER_INTERCEPT_LISTXATTR
+INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, listxattr, path, list, size);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(listxattr)(path, list, size);
+  // Here and below, size == 0 is a special case where nothing is written to the
+  // buffer, and res contains the desired buffer size.
+  if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
+  return res;
+}
+INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, llistxattr, path, list, size);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(llistxattr)(path, list, size);
+  if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
+  return res;
+}
+INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(flistxattr)(fd, list, size);
+  if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
+  return res;
+}
+#define INIT_LISTXATTR                   \
+  COMMON_INTERCEPT_FUNCTION(listxattr);  \
+  COMMON_INTERCEPT_FUNCTION(llistxattr); \
+  COMMON_INTERCEPT_FUNCTION(flistxattr);
+#else
+#define INIT_LISTXATTR
+#endif
+
+#if SANITIZER_INTERCEPT_GETXATTR
+INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value,
+            SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getxattr, path, name, value, size);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(getxattr)(path, name, value, size);
+  if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
+  return res;
+}
+INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value,
+            SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, lgetxattr, path, name, value, size);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(lgetxattr)(path, name, value, size);
+  if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
+  return res;
+}
+INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value,
+            SIZE_T size) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fgetxattr, fd, name, value, size);
+  if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  SSIZE_T res = REAL(fgetxattr)(fd, name, value, size);
+  if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
+  return res;
+}
+#define INIT_GETXATTR                   \
+  COMMON_INTERCEPT_FUNCTION(getxattr);  \
+  COMMON_INTERCEPT_FUNCTION(lgetxattr); \
+  COMMON_INTERCEPT_FUNCTION(fgetxattr);
+#else
+#define INIT_GETXATTR
+#endif
+
+#if SANITIZER_INTERCEPT_GETRESID
+INTERCEPTOR(int, getresuid, void *ruid, void *euid, void *suid) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getresuid, ruid, euid, suid);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getresuid)(ruid, euid, suid);
+  if (res >= 0) {
+    if (ruid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ruid, uid_t_sz);
+    if (euid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, euid, uid_t_sz);
+    if (suid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, suid, uid_t_sz);
+  }
+  return res;
+}
+INTERCEPTOR(int, getresgid, void *rgid, void *egid, void *sgid) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getresgid, rgid, egid, sgid);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getresgid)(rgid, egid, sgid);
+  if (res >= 0) {
+    if (rgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rgid, gid_t_sz);
+    if (egid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, egid, gid_t_sz);
+    if (sgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sgid, gid_t_sz);
+  }
+  return res;
+}
+#define INIT_GETRESID                   \
+  COMMON_INTERCEPT_FUNCTION(getresuid); \
+  COMMON_INTERCEPT_FUNCTION(getresgid);
+#else
+#define INIT_GETRESID
+#endif
+
+#if SANITIZER_INTERCEPT_GETIFADDRS
+// As long as getifaddrs()/freeifaddrs() use calloc()/free(), we don't need to
+// intercept freeifaddrs(). If that ceases to be the case, we might need to
+// intercept it to poison the memory again.
+INTERCEPTOR(int, getifaddrs, __sanitizer_ifaddrs **ifap) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getifaddrs, ifap);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(getifaddrs)(ifap);
+  if (res == 0 && ifap) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifap, sizeof(void *));
+    __sanitizer_ifaddrs *p = *ifap;
+    while (p) {
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(__sanitizer_ifaddrs));
+      if (p->ifa_name)
+        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_name,
+                                       REAL(strlen)(p->ifa_name) + 1);
+      if (p->ifa_addr)
+        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_addr, struct_sockaddr_sz);
+      if (p->ifa_netmask)
+        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_netmask, struct_sockaddr_sz);
+      // On Linux this is a union, but the other member also points to a
+      // struct sockaddr, so the following is sufficient.
+      if (p->ifa_dstaddr)
+        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_dstaddr, struct_sockaddr_sz);
+      // FIXME(smatveev): Unpoison p->ifa_data as well.
+      p = p->ifa_next;
+    }
+  }
+  return res;
+}
+#define INIT_GETIFADDRS                  \
+  COMMON_INTERCEPT_FUNCTION(getifaddrs);
+#else
+#define INIT_GETIFADDRS
+#endif
+
+#if SANITIZER_INTERCEPT_IF_INDEXTONAME
+INTERCEPTOR(char *, if_indextoname, unsigned int ifindex, char* ifname) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, if_indextoname, ifindex, ifname);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  char *res = REAL(if_indextoname)(ifindex, ifname);
+  if (res && ifname)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1);
+  return res;
+}
+INTERCEPTOR(unsigned int, if_nametoindex, const char* ifname) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, if_nametoindex, ifname);
+  if (ifname)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1);
+  return REAL(if_nametoindex)(ifname);
+}
+#define INIT_IF_INDEXTONAME                  \
+  COMMON_INTERCEPT_FUNCTION(if_indextoname); \
+  COMMON_INTERCEPT_FUNCTION(if_nametoindex);
+#else
+#define INIT_IF_INDEXTONAME
+#endif
+
+#if SANITIZER_INTERCEPT_CAPGET
+INTERCEPTOR(int, capget, void *hdrp, void *datap) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, capget, hdrp, datap);
+  if (hdrp)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(capget)(hdrp, datap);
+  if (res == 0 && datap)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz);
+  // We can also return -1 and write to hdrp->version if the version passed in
+  // hdrp->version is unsupported. But that's not a trivial condition to check,
+  // and anyway COMMON_INTERCEPTOR_READ_RANGE protects us to some extent.
+  return res;
+}
+INTERCEPTOR(int, capset, void *hdrp, const void *datap) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, capset, hdrp, datap);
+  if (hdrp)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz);
+  if (datap)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, datap, __user_cap_data_struct_sz);
+  return REAL(capset)(hdrp, datap);
+}
+#define INIT_CAPGET                  \
+  COMMON_INTERCEPT_FUNCTION(capget); \
+  COMMON_INTERCEPT_FUNCTION(capset);
+#else
+#define INIT_CAPGET
+#endif
+
+#if SANITIZER_INTERCEPT_AEABI_MEM
+DECLARE_REAL_AND_INTERCEPTOR(void *, memmove, void *, const void *, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void *, memcpy, void *, const void *, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr)
+
+INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) {
+  return WRAP(memmove)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, uptr size) {
+  return WRAP(memmove)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, uptr size) {
+  return WRAP(memmove)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, uptr size) {
+  return WRAP(memcpy)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, uptr size) {
+  return WRAP(memcpy)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, uptr size) {
+  return WRAP(memcpy)(to, from, size);
+}
+// Note the argument order.
+INTERCEPTOR(void *, __aeabi_memset, void *block, uptr size, int c) {
+  return WRAP(memset)(block, c, size);
+}
+INTERCEPTOR(void *, __aeabi_memset4, void *block, uptr size, int c) {
+  return WRAP(memset)(block, c, size);
+}
+INTERCEPTOR(void *, __aeabi_memset8, void *block, uptr size, int c) {
+  return WRAP(memset)(block, c, size);
+}
+INTERCEPTOR(void *, __aeabi_memclr, void *block, uptr size) {
+  return WRAP(memset)(block, 0, size);
+}
+INTERCEPTOR(void *, __aeabi_memclr4, void *block, uptr size) {
+  return WRAP(memset)(block, 0, size);
+}
+INTERCEPTOR(void *, __aeabi_memclr8, void *block, uptr size) {
+  return WRAP(memset)(block, 0, size);
+}
+#define INIT_AEABI_MEM                         \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memmove);  \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memmove4); \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memmove8); \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy);   \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy4);  \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy8);  \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memset);   \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memset4);  \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memset8);  \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memclr);   \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memclr4);  \
+  COMMON_INTERCEPT_FUNCTION(__aeabi_memclr8);
+#else
+#define INIT_AEABI_MEM
+#endif  // SANITIZER_INTERCEPT_AEABI_MEM
+
+#if SANITIZER_INTERCEPT___BZERO
+DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr);
+
+INTERCEPTOR(void *, __bzero, void *block, uptr size) {
+  return WRAP(memset)(block, 0, size);
+}
+#define INIT___BZERO COMMON_INTERCEPT_FUNCTION(__bzero);
+#else
+#define INIT___BZERO
+#endif  // SANITIZER_INTERCEPT___BZERO
+
+#if SANITIZER_INTERCEPT_FTIME
+INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ftime, tp);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(ftime)(tp);
+  if (tp)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, sizeof(*tp));
+  return res;
+}
+#define INIT_FTIME COMMON_INTERCEPT_FUNCTION(ftime);
+#else
+#define INIT_FTIME
+#endif  // SANITIZER_INTERCEPT_FTIME
+
+#if SANITIZER_INTERCEPT_XDR
+INTERCEPTOR(void, xdrmem_create, __sanitizer_XDR *xdrs, uptr addr,
+            unsigned size, int op) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, xdrmem_create, xdrs, addr, size, op);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  REAL(xdrmem_create)(xdrs, addr, size, op);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs));
+  if (op == __sanitizer_XDR_ENCODE) {
+    // It's not obvious how much data individual xdr_ routines write.
+    // Simply unpoison the entire target buffer in advance.
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (void *)addr, size);
+  }
+}
+
+INTERCEPTOR(void, xdrstdio_create, __sanitizer_XDR *xdrs, void *file, int op) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, xdrstdio_create, xdrs, file, op);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  REAL(xdrstdio_create)(xdrs, file, op);
+  COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs));
+}
+
+// FIXME: under ASan the call below may write to freed memory and corrupt
+// its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define XDR_INTERCEPTOR(F, T)                             \
+  INTERCEPTOR(int, F, __sanitizer_XDR *xdrs, T *p) {      \
+    void *ctx;                                            \
+    COMMON_INTERCEPTOR_ENTER(ctx, F, xdrs, p);            \
+    if (p && xdrs->x_op == __sanitizer_XDR_ENCODE)        \
+      COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p));  \
+    int res = REAL(F)(xdrs, p);                           \
+    if (res && p && xdrs->x_op == __sanitizer_XDR_DECODE) \
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); \
+    return res;                                           \
+  }
+
+XDR_INTERCEPTOR(xdr_short, short)
+XDR_INTERCEPTOR(xdr_u_short, unsigned short)
+XDR_INTERCEPTOR(xdr_int, int)
+XDR_INTERCEPTOR(xdr_u_int, unsigned)
+XDR_INTERCEPTOR(xdr_long, long)
+XDR_INTERCEPTOR(xdr_u_long, unsigned long)
+XDR_INTERCEPTOR(xdr_hyper, long long)
+XDR_INTERCEPTOR(xdr_u_hyper, unsigned long long)
+XDR_INTERCEPTOR(xdr_longlong_t, long long)
+XDR_INTERCEPTOR(xdr_u_longlong_t, unsigned long long)
+XDR_INTERCEPTOR(xdr_int8_t, u8)
+XDR_INTERCEPTOR(xdr_uint8_t, u8)
+XDR_INTERCEPTOR(xdr_int16_t, u16)
+XDR_INTERCEPTOR(xdr_uint16_t, u16)
+XDR_INTERCEPTOR(xdr_int32_t, u32)
+XDR_INTERCEPTOR(xdr_uint32_t, u32)
+XDR_INTERCEPTOR(xdr_int64_t, u64)
+XDR_INTERCEPTOR(xdr_uint64_t, u64)
+XDR_INTERCEPTOR(xdr_quad_t, long long)
+XDR_INTERCEPTOR(xdr_u_quad_t, unsigned long long)
+XDR_INTERCEPTOR(xdr_bool, bool)
+XDR_INTERCEPTOR(xdr_enum, int)
+XDR_INTERCEPTOR(xdr_char, char)
+XDR_INTERCEPTOR(xdr_u_char, unsigned char)
+XDR_INTERCEPTOR(xdr_float, float)
+XDR_INTERCEPTOR(xdr_double, double)
+
+// FIXME: intercept xdr_array, opaque, union, vector, reference, pointer,
+// wrapstring, sizeof
+
+INTERCEPTOR(int, xdr_bytes, __sanitizer_XDR *xdrs, char **p, unsigned *sizep,
+            unsigned maxsize) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, xdr_bytes, xdrs, p, sizep, maxsize);
+  if (p && sizep && xdrs->x_op == __sanitizer_XDR_ENCODE) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p));
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, sizep, sizeof(*sizep));
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, *sizep);
+  }
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(xdr_bytes)(xdrs, p, sizep, maxsize);
+  if (p && sizep && xdrs->x_op == __sanitizer_XDR_DECODE) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizep, sizeof(*sizep));
+    if (res && *p && *sizep) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, *sizep);
+  }
+  return res;
+}
+
+INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p,
+            unsigned maxsize) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, xdr_string, xdrs, p, maxsize);
+  if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) {
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p));
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, REAL(strlen)(*p) + 1);
+  }
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  int res = REAL(xdr_string)(xdrs, p, maxsize);
+  if (p && xdrs->x_op == __sanitizer_XDR_DECODE) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
+    if (res && *p)
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1);
+  }
+  return res;
+}
+
+#define INIT_XDR                               \
+  COMMON_INTERCEPT_FUNCTION(xdrmem_create);    \
+  COMMON_INTERCEPT_FUNCTION(xdrstdio_create);  \
+  COMMON_INTERCEPT_FUNCTION(xdr_short);        \
+  COMMON_INTERCEPT_FUNCTION(xdr_u_short);      \
+  COMMON_INTERCEPT_FUNCTION(xdr_int);          \
+  COMMON_INTERCEPT_FUNCTION(xdr_u_int);        \
+  COMMON_INTERCEPT_FUNCTION(xdr_long);         \
+  COMMON_INTERCEPT_FUNCTION(xdr_u_long);       \
+  COMMON_INTERCEPT_FUNCTION(xdr_hyper);        \
+  COMMON_INTERCEPT_FUNCTION(xdr_u_hyper);      \
+  COMMON_INTERCEPT_FUNCTION(xdr_longlong_t);   \
+  COMMON_INTERCEPT_FUNCTION(xdr_u_longlong_t); \
+  COMMON_INTERCEPT_FUNCTION(xdr_int8_t);       \
+  COMMON_INTERCEPT_FUNCTION(xdr_uint8_t);      \
+  COMMON_INTERCEPT_FUNCTION(xdr_int16_t);      \
+  COMMON_INTERCEPT_FUNCTION(xdr_uint16_t);     \
+  COMMON_INTERCEPT_FUNCTION(xdr_int32_t);      \
+  COMMON_INTERCEPT_FUNCTION(xdr_uint32_t);     \
+  COMMON_INTERCEPT_FUNCTION(xdr_int64_t);      \
+  COMMON_INTERCEPT_FUNCTION(xdr_uint64_t);     \
+  COMMON_INTERCEPT_FUNCTION(xdr_quad_t);       \
+  COMMON_INTERCEPT_FUNCTION(xdr_u_quad_t);     \
+  COMMON_INTERCEPT_FUNCTION(xdr_bool);         \
+  COMMON_INTERCEPT_FUNCTION(xdr_enum);         \
+  COMMON_INTERCEPT_FUNCTION(xdr_char);         \
+  COMMON_INTERCEPT_FUNCTION(xdr_u_char);       \
+  COMMON_INTERCEPT_FUNCTION(xdr_float);        \
+  COMMON_INTERCEPT_FUNCTION(xdr_double);       \
+  COMMON_INTERCEPT_FUNCTION(xdr_bytes);        \
+  COMMON_INTERCEPT_FUNCTION(xdr_string);
+#else
+#define INIT_XDR
+#endif  // SANITIZER_INTERCEPT_XDR
+
+#if SANITIZER_INTERCEPT_TSEARCH
+INTERCEPTOR(void *, tsearch, void *key, void **rootp,
+            int (*compar)(const void *, const void *)) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, tsearch, key, rootp, compar);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  void *res = REAL(tsearch)(key, rootp, compar);
+  if (res && *(void **)res == key)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(void *));
+  return res;
+}
+#define INIT_TSEARCH COMMON_INTERCEPT_FUNCTION(tsearch);
+#else
+#define INIT_TSEARCH
+#endif
+
+#if SANITIZER_INTERCEPT_LIBIO_INTERNALS || SANITIZER_INTERCEPT_FOPEN || \
+    SANITIZER_INTERCEPT_OPEN_MEMSTREAM
+void unpoison_file(__sanitizer_FILE *fp) {
+#if SANITIZER_HAS_STRUCT_FILE
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp, sizeof(*fp));
+  if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end)
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base,
+                                        fp->_IO_read_end - fp->_IO_read_base);
+#endif  // SANITIZER_HAS_STRUCT_FILE
+}
+#endif
+
+#if SANITIZER_INTERCEPT_LIBIO_INTERNALS
+// These guys are called when a .c source is built with -O2.
+INTERCEPTOR(int, __uflow, __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __uflow, fp);
+  int res = REAL(__uflow)(fp);
+  unpoison_file(fp);
+  return res;
+}
+INTERCEPTOR(int, __underflow, __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __underflow, fp);
+  int res = REAL(__underflow)(fp);
+  unpoison_file(fp);
+  return res;
+}
+INTERCEPTOR(int, __overflow, __sanitizer_FILE *fp, int ch) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __overflow, fp, ch);
+  int res = REAL(__overflow)(fp, ch);
+  unpoison_file(fp);
+  return res;
+}
+INTERCEPTOR(int, __wuflow, __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __wuflow, fp);
+  int res = REAL(__wuflow)(fp);
+  unpoison_file(fp);
+  return res;
+}
+INTERCEPTOR(int, __wunderflow, __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __wunderflow, fp);
+  int res = REAL(__wunderflow)(fp);
+  unpoison_file(fp);
+  return res;
+}
+INTERCEPTOR(int, __woverflow, __sanitizer_FILE *fp, int ch) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, __woverflow, fp, ch);
+  int res = REAL(__woverflow)(fp, ch);
+  unpoison_file(fp);
+  return res;
+}
+#define INIT_LIBIO_INTERNALS               \
+  COMMON_INTERCEPT_FUNCTION(__uflow);      \
+  COMMON_INTERCEPT_FUNCTION(__underflow);  \
+  COMMON_INTERCEPT_FUNCTION(__overflow);   \
+  COMMON_INTERCEPT_FUNCTION(__wuflow);     \
+  COMMON_INTERCEPT_FUNCTION(__wunderflow); \
+  COMMON_INTERCEPT_FUNCTION(__woverflow);
+#else
+#define INIT_LIBIO_INTERNALS
+#endif
+
+#if SANITIZER_INTERCEPT_FOPEN
+INTERCEPTOR(__sanitizer_FILE *, fopen, const char *path, const char *mode) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fopen, path, mode);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+  __sanitizer_FILE *res = REAL(fopen)(path, mode);
+  COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+  if (res) unpoison_file(res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, fdopen, int fd, const char *mode) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fdopen, fd, mode);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+  __sanitizer_FILE *res = REAL(fdopen)(fd, mode);
+  if (res) unpoison_file(res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode,
+            __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, freopen, path, mode, fp);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+  COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+  __sanitizer_FILE *res = REAL(freopen)(path, mode, fp);
+  COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+  if (res) unpoison_file(res);
+  return res;
+}
+#define INIT_FOPEN                   \
+  COMMON_INTERCEPT_FUNCTION(fopen);  \
+  COMMON_INTERCEPT_FUNCTION(fdopen); \
+  COMMON_INTERCEPT_FUNCTION(freopen);
+#else
+#define INIT_FOPEN
+#endif
+
+#if SANITIZER_INTERCEPT_FOPEN64
+INTERCEPTOR(__sanitizer_FILE *, fopen64, const char *path, const char *mode) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fopen64, path, mode);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+  __sanitizer_FILE *res = REAL(fopen64)(path, mode);
+  COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+  if (res) unpoison_file(res);
+  return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, freopen64, const char *path, const char *mode,
+            __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, freopen64, path, mode, fp);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+  COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+  __sanitizer_FILE *res = REAL(freopen64)(path, mode, fp);
+  COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+  if (res) unpoison_file(res);
+  return res;
+}
+#define INIT_FOPEN64                  \
+  COMMON_INTERCEPT_FUNCTION(fopen64); \
+  COMMON_INTERCEPT_FUNCTION(freopen64);
+#else
+#define INIT_FOPEN64
+#endif
+
+#if SANITIZER_INTERCEPT_OPEN_MEMSTREAM
+INTERCEPTOR(__sanitizer_FILE *, open_memstream, char **ptr, SIZE_T *sizeloc) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, open_memstream, ptr, sizeloc);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  __sanitizer_FILE *res = REAL(open_memstream)(ptr, sizeloc);
+  if (res) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc));
+    unpoison_file(res);
+    FileMetadata file = {ptr, sizeloc};
+    SetInterceptorMetadata(res, file);
+  }
+  return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, open_wmemstream, wchar_t **ptr,
+            SIZE_T *sizeloc) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, open_wmemstream, ptr, sizeloc);
+  __sanitizer_FILE *res = REAL(open_wmemstream)(ptr, sizeloc);
+  if (res) {
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr));
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc));
+    unpoison_file(res);
+    FileMetadata file = {(char **)ptr, sizeloc};
+    SetInterceptorMetadata(res, file);
+  }
+  return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, fmemopen, void *buf, SIZE_T size,
+            const char *mode) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fmemopen, buf, size, mode);
+  // FIXME: under ASan the call below may write to freed memory and corrupt
+  // its metadata. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  __sanitizer_FILE *res = REAL(fmemopen)(buf, size, mode);
+  if (res) unpoison_file(res);
+  return res;
+}
+#define INIT_OPEN_MEMSTREAM                   \
+  COMMON_INTERCEPT_FUNCTION(open_memstream);  \
+  COMMON_INTERCEPT_FUNCTION(open_wmemstream); \
+  COMMON_INTERCEPT_FUNCTION(fmemopen);
+#else
+#define INIT_OPEN_MEMSTREAM
+#endif
+
+#if SANITIZER_INTERCEPT_OBSTACK
+static void initialize_obstack(__sanitizer_obstack *obstack) {
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack, sizeof(*obstack));
+  if (obstack->chunk)
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack->chunk,
+                                        sizeof(*obstack->chunk));
+}
+
+INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack, int sz,
+            int align, void *(*alloc_fn)(uptr arg, uptr sz),
+            void (*free_fn)(uptr arg, void *p)) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin_1, obstack, sz, align, alloc_fn,
+                           free_fn);
+  int res = REAL(_obstack_begin_1)(obstack, sz, align, alloc_fn, free_fn);
+  if (res) initialize_obstack(obstack);
+  return res;
+}
+INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack, int sz,
+            int align, void *(*alloc_fn)(uptr sz), void (*free_fn)(void *p)) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin, obstack, sz, align, alloc_fn,
+                           free_fn);
+  int res = REAL(_obstack_begin)(obstack, sz, align, alloc_fn, free_fn);
+  if (res) initialize_obstack(obstack);
+  return res;
+}
+INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, int length) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, _obstack_newchunk, obstack, length);
+  REAL(_obstack_newchunk)(obstack, length);
+  if (obstack->chunk)
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(
+        obstack->chunk, obstack->next_free - (char *)obstack->chunk);
+}
+#define INIT_OBSTACK                           \
+  COMMON_INTERCEPT_FUNCTION(_obstack_begin_1); \
+  COMMON_INTERCEPT_FUNCTION(_obstack_begin);   \
+  COMMON_INTERCEPT_FUNCTION(_obstack_newchunk);
+#else
+#define INIT_OBSTACK
+#endif
+
+#if SANITIZER_INTERCEPT_FFLUSH
+INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fflush, fp);
+  int res = REAL(fflush)(fp);
+  // FIXME: handle fp == NULL
+  if (fp) {
+    const FileMetadata *m = GetInterceptorMetadata(fp);
+    if (m) COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
+  }
+  return res;
+}
+#define INIT_FFLUSH COMMON_INTERCEPT_FUNCTION(fflush);
+#else
+#define INIT_FFLUSH
+#endif
+
+#if SANITIZER_INTERCEPT_FCLOSE
+INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp);
+  COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+  const FileMetadata *m = GetInterceptorMetadata(fp);
+  int res = REAL(fclose)(fp);
+  if (m) {
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
+    DeleteInterceptorMetadata(fp);
+  }
+  return res;
+}
+#define INIT_FCLOSE COMMON_INTERCEPT_FUNCTION(fclose);
+#else
+#define INIT_FCLOSE
+#endif
+
+#if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
+INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
+  if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
+  COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag);
+  void *res = REAL(dlopen)(filename, flag);
+  COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
+  return res;
+}
+
+INTERCEPTOR(int, dlclose, void *handle) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlclose, handle);
+  int res = REAL(dlclose)(handle);
+  COMMON_INTERCEPTOR_LIBRARY_UNLOADED();
+  return res;
+}
+#define INIT_DLOPEN_DLCLOSE          \
+  COMMON_INTERCEPT_FUNCTION(dlopen); \
+  COMMON_INTERCEPT_FUNCTION(dlclose);
+#else
+#define INIT_DLOPEN_DLCLOSE
+#endif
+
+#if SANITIZER_INTERCEPT_GETPASS
+INTERCEPTOR(char *, getpass, const char *prompt) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, getpass, prompt);
+  if (prompt)
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, prompt, REAL(strlen)(prompt)+1);
+  char *res = REAL(getpass)(prompt);
+  if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res)+1);
+  return res;
+}
+
+#define INIT_GETPASS COMMON_INTERCEPT_FUNCTION(getpass);
+#else
+#define INIT_GETPASS
+#endif
+
+#if SANITIZER_INTERCEPT_TIMERFD
+INTERCEPTOR(int, timerfd_settime, int fd, int flags, void *new_value,
+            void *old_value) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, timerfd_settime, fd, flags, new_value,
+                           old_value);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerspec_sz);
+  int res = REAL(timerfd_settime)(fd, flags, new_value, old_value);
+  if (res != -1 && old_value)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerspec_sz);
+  return res;
+}
+
+INTERCEPTOR(int, timerfd_gettime, int fd, void *curr_value) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, timerfd_gettime, fd, curr_value);
+  int res = REAL(timerfd_gettime)(fd, curr_value);
+  if (res != -1 && curr_value)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerspec_sz);
+  return res;
+}
+#define INIT_TIMERFD                          \
+  COMMON_INTERCEPT_FUNCTION(timerfd_settime); \
+  COMMON_INTERCEPT_FUNCTION(timerfd_gettime);
+#else
+#define INIT_TIMERFD
+#endif
+
+#if SANITIZER_INTERCEPT_MLOCKX
+// Linux kernel has a bug that leads to kernel deadlock if a process
+// maps TBs of memory and then calls mlock().
+static void MlockIsUnsupported() {
+  static atomic_uint8_t printed;
+  if (atomic_exchange(&printed, 1, memory_order_relaxed))
+    return;
+  VPrintf(1, "%s ignores mlock/mlockall/munlock/munlockall\n",
+          SanitizerToolName);
+}
+
+INTERCEPTOR(int, mlock, const void *addr, uptr len) {
+  MlockIsUnsupported();
+  return 0;
+}
+
+INTERCEPTOR(int, munlock, const void *addr, uptr len) {
+  MlockIsUnsupported();
+  return 0;
+}
+
+INTERCEPTOR(int, mlockall, int flags) {
+  MlockIsUnsupported();
+  return 0;
+}
+
+INTERCEPTOR(int, munlockall, void) {
+  MlockIsUnsupported();
+  return 0;
+}
+
+#define INIT_MLOCKX                                                            \
+  COMMON_INTERCEPT_FUNCTION(mlock);                                            \
+  COMMON_INTERCEPT_FUNCTION(munlock);                                          \
+  COMMON_INTERCEPT_FUNCTION(mlockall);                                         \
+  COMMON_INTERCEPT_FUNCTION(munlockall);
+
+#else
+#define INIT_MLOCKX
+#endif  // SANITIZER_INTERCEPT_MLOCKX
+
+#if SANITIZER_INTERCEPT_FOPENCOOKIE
+struct WrappedCookie {
+  void *real_cookie;
+  __sanitizer_cookie_io_functions_t real_io_funcs;
+};
+
+static uptr wrapped_read(void *cookie, char *buf, uptr size) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+  WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+  __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read;
+  return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0;
+}
+
+static uptr wrapped_write(void *cookie, const char *buf, uptr size) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+  WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+  __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write;
+  return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size;
+}
+
+static int wrapped_seek(void *cookie, u64 *offset, int whence) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+  COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset));
+  WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+  __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek;
+  return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence)
+                   : -1;
+}
+
+static int wrapped_close(void *cookie) {
+  COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+  WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+  __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close;
+  int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0;
+  InternalFree(wrapped_cookie);
+  return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode,
+            __sanitizer_cookie_io_functions_t io_funcs) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs);
+  WrappedCookie *wrapped_cookie =
+      (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie));
+  wrapped_cookie->real_cookie = cookie;
+  wrapped_cookie->real_io_funcs = io_funcs;
+  __sanitizer_FILE *res =
+      REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write,
+                                               wrapped_seek, wrapped_close});
+  return res;
+}
+
+#define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie);
+#else
+#define INIT_FOPENCOOKIE
+#endif  // SANITIZER_INTERCEPT_FOPENCOOKIE
+
+#if SANITIZER_INTERCEPT_SEM
+INTERCEPTOR(int, sem_init, __sanitizer_sem_t *s, int pshared, unsigned value) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sem_init, s, pshared, value);
+  // Workaround a bug in glibc's "old" semaphore implementation by
+  // zero-initializing the sem_t contents. This has to be done here because
+  // interceptors bind to the lowest symbols version by default, hitting the
+  // buggy code path while the non-sanitized build of the same code works fine.
+  REAL(memset)(s, 0, sizeof(*s));
+  int res = REAL(sem_init)(s, pshared, value);
+  return res;
+}
+
+INTERCEPTOR(int, sem_destroy, __sanitizer_sem_t *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sem_destroy, s);
+  int res = REAL(sem_destroy)(s);
+  return res;
+}
+
+INTERCEPTOR(int, sem_wait, __sanitizer_sem_t *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sem_wait, s);
+  int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_wait)(s);
+  if (res == 0) {
+    COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+  }
+  return res;
+}
+
+INTERCEPTOR(int, sem_trywait, __sanitizer_sem_t *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sem_trywait, s);
+  int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_trywait)(s);
+  if (res == 0) {
+    COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+  }
+  return res;
+}
+
+INTERCEPTOR(int, sem_timedwait, __sanitizer_sem_t *s, void *abstime) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sem_timedwait, s, abstime);
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, abstime, struct_timespec_sz);
+  int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_timedwait)(s, abstime);
+  if (res == 0) {
+    COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+  }
+  return res;
+}
+
+INTERCEPTOR(int, sem_post, __sanitizer_sem_t *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sem_post, s);
+  COMMON_INTERCEPTOR_RELEASE(ctx, (uptr)s);
+  int res = REAL(sem_post)(s);
+  return res;
+}
+
+INTERCEPTOR(int, sem_getvalue, __sanitizer_sem_t *s, int *sval) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, sem_getvalue, s, sval);
+  int res = REAL(sem_getvalue)(s, sval);
+  if (res == 0) {
+    COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sval, sizeof(*sval));
+  }
+  return res;
+}
+#define INIT_SEM                                                               \
+  COMMON_INTERCEPT_FUNCTION(sem_init);                                         \
+  COMMON_INTERCEPT_FUNCTION(sem_destroy);                                      \
+  COMMON_INTERCEPT_FUNCTION(sem_wait);                                         \
+  COMMON_INTERCEPT_FUNCTION(sem_trywait);                                      \
+  COMMON_INTERCEPT_FUNCTION(sem_timedwait);                                    \
+  COMMON_INTERCEPT_FUNCTION(sem_post);                                         \
+  COMMON_INTERCEPT_FUNCTION(sem_getvalue);
+#else
+#define INIT_SEM
+#endif // SANITIZER_INTERCEPT_SEM
+
+#if SANITIZER_INTERCEPT_PTHREAD_SETCANCEL
+INTERCEPTOR(int, pthread_setcancelstate, int state, int *oldstate) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcancelstate, state, oldstate);
+  int res = REAL(pthread_setcancelstate)(state, oldstate);
+  if (res == 0)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldstate, sizeof(*oldstate));
+  return res;
+}
+
+INTERCEPTOR(int, pthread_setcanceltype, int type, int *oldtype) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcanceltype, type, oldtype);
+  int res = REAL(pthread_setcanceltype)(type, oldtype);
+  if (res == 0)
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldtype, sizeof(*oldtype));
+  return res;
+}
+#define INIT_PTHREAD_SETCANCEL                                                 \
+  COMMON_INTERCEPT_FUNCTION(pthread_setcancelstate);                           \
+  COMMON_INTERCEPT_FUNCTION(pthread_setcanceltype);
+#else
+#define INIT_PTHREAD_SETCANCEL
+#endif
+
+#if SANITIZER_INTERCEPT_MINCORE
+INTERCEPTOR(int, mincore, void *addr, uptr length, unsigned char *vec) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, mincore, addr, length, vec);
+  int res = REAL(mincore)(addr, length, vec);
+  if (res == 0) {
+    uptr page_size = GetPageSizeCached();
+    uptr vec_size = ((length + page_size - 1) & (~(page_size - 1))) / page_size;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, vec, vec_size);
+  }
+  return res;
+}
+#define INIT_MINCORE COMMON_INTERCEPT_FUNCTION(mincore);
+#else
+#define INIT_MINCORE
+#endif
+
+#if SANITIZER_INTERCEPT_PROCESS_VM_READV
+INTERCEPTOR(SSIZE_T, process_vm_readv, int pid, __sanitizer_iovec *local_iov,
+            uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt,
+            uptr flags) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, process_vm_readv, pid, local_iov, liovcnt,
+                           remote_iov, riovcnt, flags);
+  SSIZE_T res = REAL(process_vm_readv)(pid, local_iov, liovcnt, remote_iov,
+                                       riovcnt, flags);
+  if (res > 0)
+    write_iovec(ctx, local_iov, liovcnt, res);
+  return res;
+}
+
+INTERCEPTOR(SSIZE_T, process_vm_writev, int pid, __sanitizer_iovec *local_iov,
+            uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt,
+            uptr flags) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, process_vm_writev, pid, local_iov, liovcnt,
+                           remote_iov, riovcnt, flags);
+  SSIZE_T res = REAL(process_vm_writev)(pid, local_iov, liovcnt, remote_iov,
+                                        riovcnt, flags);
+  if (res > 0)
+    read_iovec(ctx, local_iov, liovcnt, res);
+  return res;
+}
+#define INIT_PROCESS_VM_READV                                                  \
+  COMMON_INTERCEPT_FUNCTION(process_vm_readv);                                 \
+  COMMON_INTERCEPT_FUNCTION(process_vm_writev);
+#else
+#define INIT_PROCESS_VM_READV
+#endif
+
+static void InitializeCommonInterceptors() {
+  static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
+  interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
+
+  INIT_TEXTDOMAIN;
+  INIT_STRCMP;
+  INIT_STRNCMP;
+  INIT_STRCASECMP;
+  INIT_STRNCASECMP;
+  INIT_STRSTR;
+  INIT_STRCASESTR;
+  INIT_STRSPN;
+  INIT_STRPBRK;
+  INIT_MEMCHR;
+  INIT_MEMCMP;
+  INIT_MEMRCHR;
+  INIT_READ;
+  INIT_PREAD;
+  INIT_PREAD64;
+  INIT_READV;
+  INIT_PREADV;
+  INIT_PREADV64;
+  INIT_WRITE;
+  INIT_PWRITE;
+  INIT_PWRITE64;
+  INIT_WRITEV;
+  INIT_PWRITEV;
+  INIT_PWRITEV64;
+  INIT_PRCTL;
+  INIT_LOCALTIME_AND_FRIENDS;
+  INIT_STRPTIME;
+  INIT_SCANF;
+  INIT_ISOC99_SCANF;
+  INIT_PRINTF;
+  INIT_PRINTF_L;
+  INIT_ISOC99_PRINTF;
+  INIT_FREXP;
+  INIT_FREXPF_FREXPL;
+  INIT_GETPWNAM_AND_FRIENDS;
+  INIT_GETPWNAM_R_AND_FRIENDS;
+  INIT_GETPWENT;
+  INIT_FGETPWENT;
+  INIT_GETPWENT_R;
+  INIT_SETPWENT;
+  INIT_CLOCK_GETTIME;
+  INIT_GETITIMER;
+  INIT_TIME;
+  INIT_GLOB;
+  INIT_WAIT;
+  INIT_WAIT4;
+  INIT_INET;
+  INIT_PTHREAD_GETSCHEDPARAM;
+  INIT_GETADDRINFO;
+  INIT_GETNAMEINFO;
+  INIT_GETSOCKNAME;
+  INIT_GETHOSTBYNAME;
+  INIT_GETHOSTBYNAME_R;
+  INIT_GETHOSTBYNAME2_R;
+  INIT_GETHOSTBYADDR_R;
+  INIT_GETHOSTENT_R;
+  INIT_GETSOCKOPT;
+  INIT_ACCEPT;
+  INIT_ACCEPT4;
+  INIT_MODF;
+  INIT_RECVMSG;
+  INIT_GETPEERNAME;
+  INIT_IOCTL;
+  INIT_INET_ATON;
+  INIT_SYSINFO;
+  INIT_READDIR;
+  INIT_READDIR64;
+  INIT_PTRACE;
+  INIT_SETLOCALE;
+  INIT_GETCWD;
+  INIT_GET_CURRENT_DIR_NAME;
+  INIT_STRTOIMAX;
+  INIT_MBSTOWCS;
+  INIT_MBSNRTOWCS;
+  INIT_WCSTOMBS;
+  INIT_WCSNRTOMBS;
+  INIT_WCRTOMB;
+  INIT_TCGETATTR;
+  INIT_REALPATH;
+  INIT_CANONICALIZE_FILE_NAME;
+  INIT_CONFSTR;
+  INIT_SCHED_GETAFFINITY;
+  INIT_SCHED_GETPARAM;
+  INIT_STRERROR;
+  INIT_STRERROR_R;
+  INIT_XPG_STRERROR_R;
+  INIT_SCANDIR;
+  INIT_SCANDIR64;
+  INIT_GETGROUPS;
+  INIT_POLL;
+  INIT_PPOLL;
+  INIT_WORDEXP;
+  INIT_SIGWAIT;
+  INIT_SIGWAITINFO;
+  INIT_SIGTIMEDWAIT;
+  INIT_SIGSETOPS;
+  INIT_SIGPENDING;
+  INIT_SIGPROCMASK;
+  INIT_BACKTRACE;
+  INIT__EXIT;
+  INIT_PTHREAD_MUTEX_LOCK;
+  INIT_PTHREAD_MUTEX_UNLOCK;
+  INIT_GETMNTENT;
+  INIT_GETMNTENT_R;
+  INIT_STATFS;
+  INIT_STATFS64;
+  INIT_STATVFS;
+  INIT_STATVFS64;
+  INIT_INITGROUPS;
+  INIT_ETHER_NTOA_ATON;
+  INIT_ETHER_HOST;
+  INIT_ETHER_R;
+  INIT_SHMCTL;
+  INIT_RANDOM_R;
+  INIT_PTHREAD_ATTR_GET;
+  INIT_PTHREAD_ATTR_GETINHERITSCHED;
+  INIT_PTHREAD_ATTR_GETAFFINITY_NP;
+  INIT_PTHREAD_MUTEXATTR_GETPSHARED;
+  INIT_PTHREAD_MUTEXATTR_GETTYPE;
+  INIT_PTHREAD_MUTEXATTR_GETPROTOCOL;
+  INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING;
+  INIT_PTHREAD_MUTEXATTR_GETROBUST;
+  INIT_PTHREAD_MUTEXATTR_GETROBUST_NP;
+  INIT_PTHREAD_RWLOCKATTR_GETPSHARED;
+  INIT_PTHREAD_RWLOCKATTR_GETKIND_NP;
+  INIT_PTHREAD_CONDATTR_GETPSHARED;
+  INIT_PTHREAD_CONDATTR_GETCLOCK;
+  INIT_PTHREAD_BARRIERATTR_GETPSHARED;
+  INIT_TMPNAM;
+  INIT_TMPNAM_R;
+  INIT_TEMPNAM;
+  INIT_PTHREAD_SETNAME_NP;
+  INIT_SINCOS;
+  INIT_REMQUO;
+  INIT_LGAMMA;
+  INIT_LGAMMA_R;
+  INIT_LGAMMAL_R;
+  INIT_DRAND48_R;
+  INIT_RAND_R;
+  INIT_GETLINE;
+  INIT_ICONV;
+  INIT_TIMES;
+  INIT_TLS_GET_ADDR;
+  INIT_LISTXATTR;
+  INIT_GETXATTR;
+  INIT_GETRESID;
+  INIT_GETIFADDRS;
+  INIT_IF_INDEXTONAME;
+  INIT_CAPGET;
+  INIT_AEABI_MEM;
+  INIT___BZERO;
+  INIT_FTIME;
+  INIT_XDR;
+  INIT_TSEARCH;
+  INIT_LIBIO_INTERNALS;
+  INIT_FOPEN;
+  INIT_FOPEN64;
+  INIT_OPEN_MEMSTREAM;
+  INIT_OBSTACK;
+  INIT_FFLUSH;
+  INIT_FCLOSE;
+  INIT_DLOPEN_DLCLOSE;
+  INIT_GETPASS;
+  INIT_TIMERFD;
+  INIT_MLOCKX;
+  INIT_FOPENCOOKIE;
+  INIT_SEM;
+  INIT_PTHREAD_SETCANCEL;
+  INIT_MINCORE;
+  INIT_PROCESS_VM_READV;
+}
diff --git a/lsan/include/sanitizer_common/sanitizer_common_interceptors_format.inc b/lsan/include/sanitizer_common/sanitizer_common_interceptors_format.inc
new file mode 100644 (file)
index 0000000..9133be7
--- /dev/null
@@ -0,0 +1,558 @@
+//===-- sanitizer_common_interceptors_format.inc ----------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Scanf/printf implementation for use in *Sanitizer interceptors.
+// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html
+// and http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
+// with a few common GNU extensions.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdarg.h>
+
+static const char *parse_number(const char *p, int *out) {
+  *out = internal_atoll(p);
+  while (*p >= '0' && *p <= '9')
+    ++p;
+  return p;
+}
+
+static const char *maybe_parse_param_index(const char *p, int *out) {
+  // n$
+  if (*p >= '0' && *p <= '9') {
+    int number;
+    const char *q = parse_number(p, &number);
+    CHECK(q);
+    if (*q == '$') {
+      *out = number;
+      p = q + 1;
+    }
+  }
+
+  // Otherwise, do not change p. This will be re-parsed later as the field
+  // width.
+  return p;
+}
+
+static bool char_is_one_of(char c, const char *s) {
+  return !!internal_strchr(s, c);
+}
+
+static const char *maybe_parse_length_modifier(const char *p, char ll[2]) {
+  if (char_is_one_of(*p, "jztLq")) {
+    ll[0] = *p;
+    ++p;
+  } else if (*p == 'h') {
+    ll[0] = 'h';
+    ++p;
+    if (*p == 'h') {
+      ll[1] = 'h';
+      ++p;
+    }
+  } else if (*p == 'l') {
+    ll[0] = 'l';
+    ++p;
+    if (*p == 'l') {
+      ll[1] = 'l';
+      ++p;
+    }
+  }
+  return p;
+}
+
+// Returns true if the character is an integer conversion specifier.
+static bool format_is_integer_conv(char c) {
+  return char_is_one_of(c, "diouxXn");
+}
+
+// Returns true if the character is an floating point conversion specifier.
+static bool format_is_float_conv(char c) {
+  return char_is_one_of(c, "aAeEfFgG");
+}
+
+// Returns string output character size for string-like conversions,
+// or 0 if the conversion is invalid.
+static int format_get_char_size(char convSpecifier,
+                                const char lengthModifier[2]) {
+  if (char_is_one_of(convSpecifier, "CS")) {
+    return sizeof(wchar_t);
+  }
+
+  if (char_is_one_of(convSpecifier, "cs[")) {
+    if (lengthModifier[0] == 'l' && lengthModifier[1] == '\0')
+      return sizeof(wchar_t);
+    else if (lengthModifier[0] == '\0')
+      return sizeof(char);
+  }
+
+  return 0;
+}
+
+enum FormatStoreSize {
+  // Store size not known in advance; can be calculated as wcslen() of the
+  // destination buffer.
+  FSS_WCSLEN = -2,
+  // Store size not known in advance; can be calculated as strlen() of the
+  // destination buffer.
+  FSS_STRLEN = -1,
+  // Invalid conversion specifier.
+  FSS_INVALID = 0
+};
+
+// Returns the memory size of a format directive (if >0), or a value of
+// FormatStoreSize.
+static int format_get_value_size(char convSpecifier,
+                                 const char lengthModifier[2],
+                                 bool promote_float) {
+  if (format_is_integer_conv(convSpecifier)) {
+    switch (lengthModifier[0]) {
+    case 'h':
+      return lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short);
+    case 'l':
+      return lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long);
+    case 'q':
+      return sizeof(long long);
+    case 'L':
+      return sizeof(long long);
+    case 'j':
+      return sizeof(INTMAX_T);
+    case 'z':
+      return sizeof(SIZE_T);
+    case 't':
+      return sizeof(PTRDIFF_T);
+    case 0:
+      return sizeof(int);
+    default:
+      return FSS_INVALID;
+    }
+  }
+
+  if (format_is_float_conv(convSpecifier)) {
+    switch (lengthModifier[0]) {
+    case 'L':
+    case 'q':
+      return sizeof(long double);
+    case 'l':
+      return lengthModifier[1] == 'l' ? sizeof(long double)
+                                           : sizeof(double);
+    case 0:
+      // Printf promotes floats to doubles but scanf does not
+      return promote_float ? sizeof(double) : sizeof(float);
+    default:
+      return FSS_INVALID;
+    }
+  }
+
+  if (convSpecifier == 'p') {
+    if (lengthModifier[0] != 0)
+      return FSS_INVALID;
+    return sizeof(void *);
+  }
+
+  return FSS_INVALID;
+}
+
+struct ScanfDirective {
+  int argIdx; // argument index, or -1 if not specified ("%n$")
+  int fieldWidth;
+  const char *begin;
+  const char *end;
+  bool suppressed; // suppress assignment ("*")
+  bool allocate;   // allocate space ("m")
+  char lengthModifier[2];
+  char convSpecifier;
+  bool maybeGnuMalloc;
+};
+
+// Parse scanf format string. If a valid directive in encountered, it is
+// returned in dir. This function returns the pointer to the first
+// unprocessed character, or 0 in case of error.
+// In case of the end-of-string, a pointer to the closing \0 is returned.
+static const char *scanf_parse_next(const char *p, bool allowGnuMalloc,
+                                    ScanfDirective *dir) {
+  internal_memset(dir, 0, sizeof(*dir));
+  dir->argIdx = -1;
+
+  while (*p) {
+    if (*p != '%') {
+      ++p;
+      continue;
+    }
+    dir->begin = p;
+    ++p;
+    // %%
+    if (*p == '%') {
+      ++p;
+      continue;
+    }
+    if (*p == '\0') {
+      return nullptr;
+    }
+    // %n$
+    p = maybe_parse_param_index(p, &dir->argIdx);
+    CHECK(p);
+    // *
+    if (*p == '*') {
+      dir->suppressed = true;
+      ++p;
+    }
+    // Field width
+    if (*p >= '0' && *p <= '9') {
+      p = parse_number(p, &dir->fieldWidth);
+      CHECK(p);
+      if (dir->fieldWidth <= 0)  // Width if at all must be non-zero
+        return nullptr;
+    }
+    // m
+    if (*p == 'm') {
+      dir->allocate = true;
+      ++p;
+    }
+    // Length modifier.
+    p = maybe_parse_length_modifier(p, dir->lengthModifier);
+    // Conversion specifier.
+    dir->convSpecifier = *p++;
+    // Consume %[...] expression.
+    if (dir->convSpecifier == '[') {
+      if (*p == '^')
+        ++p;
+      if (*p == ']')
+        ++p;
+      while (*p && *p != ']')
+        ++p;
+      if (*p == 0)
+        return nullptr; // unexpected end of string
+                        // Consume the closing ']'.
+      ++p;
+    }
+    // This is unfortunately ambiguous between old GNU extension
+    // of %as, %aS and %a[...] and newer POSIX %a followed by
+    // letters s, S or [.
+    if (allowGnuMalloc && dir->convSpecifier == 'a' &&
+        !dir->lengthModifier[0]) {
+      if (*p == 's' || *p == 'S') {
+        dir->maybeGnuMalloc = true;
+        ++p;
+      } else if (*p == '[') {
+        // Watch for %a[h-j%d], if % appears in the
+        // [...] range, then we need to give up, we don't know
+        // if scanf will parse it as POSIX %a [h-j %d ] or
+        // GNU allocation of string with range dh-j plus %.
+        const char *q = p + 1;
+        if (*q == '^')
+          ++q;
+        if (*q == ']')
+          ++q;
+        while (*q && *q != ']' && *q != '%')
+          ++q;
+        if (*q == 0 || *q == '%')
+          return nullptr;
+        p = q + 1; // Consume the closing ']'.
+        dir->maybeGnuMalloc = true;
+      }
+    }
+    dir->end = p;
+    break;
+  }
+  return p;
+}
+
+static int scanf_get_value_size(ScanfDirective *dir) {
+  if (dir->allocate) {
+    if (!char_is_one_of(dir->convSpecifier, "cCsS["))
+      return FSS_INVALID;
+    return sizeof(char *);
+  }
+
+  if (dir->maybeGnuMalloc) {
+    if (dir->convSpecifier != 'a' || dir->lengthModifier[0])
+      return FSS_INVALID;
+    // This is ambiguous, so check the smaller size of char * (if it is
+    // a GNU extension of %as, %aS or %a[...]) and float (if it is
+    // POSIX %a followed by s, S or [ letters).
+    return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float);
+  }
+
+  if (char_is_one_of(dir->convSpecifier, "cCsS[")) {
+    bool needsTerminator = char_is_one_of(dir->convSpecifier, "sS[");
+    unsigned charSize =
+        format_get_char_size(dir->convSpecifier, dir->lengthModifier);
+    if (charSize == 0)
+      return FSS_INVALID;
+    if (dir->fieldWidth == 0) {
+      if (!needsTerminator)
+        return charSize;
+      return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN;
+    }
+    return (dir->fieldWidth + needsTerminator) * charSize;
+  }
+
+  return format_get_value_size(dir->convSpecifier, dir->lengthModifier, false);
+}
+
+// Common part of *scanf interceptors.
+// Process format string and va_list, and report all store ranges.
+// Stops when "consuming" n_inputs input items.
+static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc,
+                         const char *format, va_list aq) {
+  CHECK_GT(n_inputs, 0);
+  const char *p = format;
+
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
+
+  while (*p) {
+    ScanfDirective dir;
+    p = scanf_parse_next(p, allowGnuMalloc, &dir);
+    if (!p)
+      break;
+    if (dir.convSpecifier == 0) {
+      // This can only happen at the end of the format string.
+      CHECK_EQ(*p, 0);
+      break;
+    }
+    // Here the directive is valid. Do what it says.
+    if (dir.argIdx != -1) {
+      // Unsupported.
+      break;
+    }
+    if (dir.suppressed)
+      continue;
+    int size = scanf_get_value_size(&dir);
+    if (size == FSS_INVALID) {
+      Report("WARNING: unexpected format specifier in scanf interceptor: "
+        "%.*s\n", dir.end - dir.begin, dir.begin);
+      break;
+    }
+    void *argp = va_arg(aq, void *);
+    if (dir.convSpecifier != 'n')
+      --n_inputs;
+    if (n_inputs < 0)
+      break;
+    if (size == FSS_STRLEN) {
+      size = internal_strlen((const char *)argp) + 1;
+    } else if (size == FSS_WCSLEN) {
+      // FIXME: actually use wcslen() to calculate it.
+      size = 0;
+    }
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
+  }
+}
+
+#if SANITIZER_INTERCEPT_PRINTF
+
+struct PrintfDirective {
+  int fieldWidth;
+  int fieldPrecision;
+  int argIdx; // width argument index, or -1 if not specified ("%*n$")
+  int precisionIdx; // precision argument index, or -1 if not specified (".*n$")
+  const char *begin;
+  const char *end;
+  bool starredWidth;
+  bool starredPrecision;
+  char lengthModifier[2];
+  char convSpecifier;
+};
+
+static const char *maybe_parse_number(const char *p, int *out) {
+  if (*p >= '0' && *p <= '9')
+    p = parse_number(p, out);
+  return p;
+}
+
+static const char *maybe_parse_number_or_star(const char *p, int *out,
+                                              bool *star) {
+  if (*p == '*') {
+    *star = true;
+    ++p;
+  } else {
+    *star = false;
+    p = maybe_parse_number(p, out);
+  }
+  return p;
+}
+
+// Parse printf format string. Same as scanf_parse_next.
+static const char *printf_parse_next(const char *p, PrintfDirective *dir) {
+  internal_memset(dir, 0, sizeof(*dir));
+  dir->argIdx = -1;
+  dir->precisionIdx = -1;
+
+  while (*p) {
+    if (*p != '%') {
+      ++p;
+      continue;
+    }
+    dir->begin = p;
+    ++p;
+    // %%
+    if (*p == '%') {
+      ++p;
+      continue;
+    }
+    if (*p == '\0') {
+      return nullptr;
+    }
+    // %n$
+    p = maybe_parse_param_index(p, &dir->precisionIdx);
+    CHECK(p);
+    // Flags
+    while (char_is_one_of(*p, "'-+ #0")) {
+      ++p;
+    }
+    // Field width
+    p = maybe_parse_number_or_star(p, &dir->fieldWidth,
+                                   &dir->starredWidth);
+    if (!p)
+      return nullptr;
+    // Precision
+    if (*p == '.') {
+      ++p;
+      // Actual precision is optional (surprise!)
+      p = maybe_parse_number_or_star(p, &dir->fieldPrecision,
+                                     &dir->starredPrecision);
+      if (!p)
+        return nullptr;
+      // m$
+      if (dir->starredPrecision) {
+        p = maybe_parse_param_index(p, &dir->precisionIdx);
+        CHECK(p);
+      }
+    }
+    // Length modifier.
+    p = maybe_parse_length_modifier(p, dir->lengthModifier);
+    // Conversion specifier.
+    dir->convSpecifier = *p++;
+    dir->end = p;
+    break;
+  }
+  return p;
+}
+
+static int printf_get_value_size(PrintfDirective *dir) {
+  if (dir->convSpecifier == 'm') {
+    return sizeof(char *);
+  }
+
+  if (char_is_one_of(dir->convSpecifier, "cCsS")) {
+    unsigned charSize =
+        format_get_char_size(dir->convSpecifier, dir->lengthModifier);
+    if (charSize == 0)
+      return FSS_INVALID;
+    if (char_is_one_of(dir->convSpecifier, "sS")) {
+      return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN;
+    }
+    return charSize;
+  }
+
+  return format_get_value_size(dir->convSpecifier, dir->lengthModifier, true);
+}
+
+#define SKIP_SCALAR_ARG(aq, convSpecifier, size)                   \
+  do {                                                             \
+    if (format_is_float_conv(convSpecifier)) {                     \
+      switch (size) {                                              \
+      case 8:                                                      \
+        va_arg(*aq, double);                                       \
+        break;                                                     \
+      case 12:                                                     \
+        va_arg(*aq, long double);                                  \
+        break;                                                     \
+      case 16:                                                     \
+        va_arg(*aq, long double);                                  \
+        break;                                                     \
+      default:                                                     \
+        Report("WARNING: unexpected floating-point arg size"       \
+               " in printf interceptor: %d\n", size);              \
+        return;                                                    \
+      }                                                            \
+    } else {                                                       \
+      switch (size) {                                              \
+      case 1:                                                      \
+      case 2:                                                      \
+      case 4:                                                      \
+        va_arg(*aq, u32);                                          \
+        break;                                                     \
+      case 8:                                                      \
+        va_arg(*aq, u64);                                          \
+        break;                                                     \
+      default:                                                     \
+        Report("WARNING: unexpected arg size"                      \
+               " in printf interceptor: %d\n", size);              \
+        return;                                                    \
+      }                                                            \
+    }                                                              \
+  } while (0)
+
+// Common part of *printf interceptors.
+// Process format string and va_list, and report all load ranges.
+static void printf_common(void *ctx, const char *format, va_list aq) {
+  COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
+
+  const char *p = format;
+
+  while (*p) {
+    PrintfDirective dir;
+    p = printf_parse_next(p, &dir);
+    if (!p)
+      break;
+    if (dir.convSpecifier == 0) {
+      // This can only happen at the end of the format string.
+      CHECK_EQ(*p, 0);
+      break;
+    }
+    // Here the directive is valid. Do what it says.
+    if (dir.argIdx != -1 || dir.precisionIdx != -1) {
+      // Unsupported.
+      break;
+    }
+    if (dir.starredWidth) {
+      // Dynamic width
+      SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
+    }
+    if (dir.starredPrecision) {
+      // Dynamic precision
+      SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
+    }
+    int size = printf_get_value_size(&dir);
+    if (size == FSS_INVALID) {
+      Report("WARNING: unexpected format specifier in printf "
+             "interceptor: %.*s\n", dir.end - dir.begin, dir.begin);
+      break;
+    }
+    if (dir.convSpecifier == 'n') {
+      void *argp = va_arg(aq, void *);
+      COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
+      continue;
+    } else if (size == FSS_STRLEN) {
+      if (void *argp = va_arg(aq, void *)) {
+        if (dir.starredPrecision) {
+          // FIXME: properly support starred precision for strings.
+          size = 0;
+        } else if (dir.fieldPrecision > 0) {
+          // Won't read more than "precision" symbols.
+          size = internal_strnlen((const char *)argp, dir.fieldPrecision);
+          if (size < dir.fieldPrecision) size++;
+        } else {
+          // Whole string will be accessed.
+          size = internal_strlen((const char *)argp) + 1;
+        }
+        COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
+      }
+    } else if (size == FSS_WCSLEN) {
+      if (void *argp = va_arg(aq, void *)) {
+        // FIXME: Properly support wide-character strings (via wcsrtombs).
+        size = 0;
+        COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
+      }
+    } else {
+      // Skip non-pointer args
+      SKIP_SCALAR_ARG(&aq, dir.convSpecifier, size);
+    }
+  }
+}
+
+#endif // SANITIZER_INTERCEPT_PRINTF
diff --git a/lsan/include/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/lsan/include/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
new file mode 100755 (executable)
index 0000000..6c5fda0
--- /dev/null
@@ -0,0 +1,598 @@
+//===-- sanitizer_common_interceptors_ioctl.inc -----------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Ioctl handling in common sanitizer interceptors.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_flags.h"
+
+struct ioctl_desc {
+  unsigned req;
+  // FIXME: support read+write arguments. Currently READWRITE and WRITE do the
+  // same thing.
+  // XXX: The declarations below may use WRITE instead of READWRITE, unless
+  // explicitly noted.
+  enum {
+    NONE,
+    READ,
+    WRITE,
+    READWRITE,
+    CUSTOM
+  } type : 3;
+  unsigned size : 29;
+  const char* name;
+};
+
+const unsigned ioctl_table_max = 500;
+static ioctl_desc ioctl_table[ioctl_table_max];
+static unsigned ioctl_table_size = 0;
+
+// This can not be declared as a global, because references to struct_*_sz
+// require a global initializer. And this table must be available before global
+// initializers are run.
+static void ioctl_table_fill() {
+#define _(rq, tp, sz)                                    \
+  if (IOCTL_##rq != IOCTL_NOT_PRESENT) {                 \
+    CHECK(ioctl_table_size < ioctl_table_max);           \
+    ioctl_table[ioctl_table_size].req = IOCTL_##rq;      \
+    ioctl_table[ioctl_table_size].type = ioctl_desc::tp; \
+    ioctl_table[ioctl_table_size].size = sz;             \
+    ioctl_table[ioctl_table_size].name = #rq;            \
+    ++ioctl_table_size;                                  \
+  }
+
+  _(FIOASYNC, READ, sizeof(int));
+  _(FIOCLEX, NONE, 0);
+  _(FIOGETOWN, WRITE, sizeof(int));
+  _(FIONBIO, READ, sizeof(int));
+  _(FIONCLEX, NONE, 0);
+  _(FIOSETOWN, READ, sizeof(int));
+  _(SIOCADDMULTI, READ, struct_ifreq_sz);
+  _(SIOCATMARK, WRITE, sizeof(int));
+  _(SIOCDELMULTI, READ, struct_ifreq_sz);
+  _(SIOCGIFADDR, WRITE, struct_ifreq_sz);
+  _(SIOCGIFBRDADDR, WRITE, struct_ifreq_sz);
+  _(SIOCGIFCONF, CUSTOM, 0);
+  _(SIOCGIFDSTADDR, WRITE, struct_ifreq_sz);
+  _(SIOCGIFFLAGS, WRITE, struct_ifreq_sz);
+  _(SIOCGIFMETRIC, WRITE, struct_ifreq_sz);
+  _(SIOCGIFMTU, WRITE, struct_ifreq_sz);
+  _(SIOCGIFNETMASK, WRITE, struct_ifreq_sz);
+  _(SIOCGPGRP, WRITE, sizeof(int));
+  _(SIOCSIFADDR, READ, struct_ifreq_sz);
+  _(SIOCSIFBRDADDR, READ, struct_ifreq_sz);
+  _(SIOCSIFDSTADDR, READ, struct_ifreq_sz);
+  _(SIOCSIFFLAGS, READ, struct_ifreq_sz);
+  _(SIOCSIFMETRIC, READ, struct_ifreq_sz);
+  _(SIOCSIFMTU, READ, struct_ifreq_sz);
+  _(SIOCSIFNETMASK, READ, struct_ifreq_sz);
+  _(SIOCSPGRP, READ, sizeof(int));
+  _(TIOCCONS, NONE, 0);
+  _(TIOCEXCL, NONE, 0);
+  _(TIOCGETD, WRITE, sizeof(int));
+  _(TIOCGPGRP, WRITE, pid_t_sz);
+  _(TIOCGWINSZ, WRITE, struct_winsize_sz);
+  _(TIOCMBIC, READ, sizeof(int));
+  _(TIOCMBIS, READ, sizeof(int));
+  _(TIOCMGET, WRITE, sizeof(int));
+  _(TIOCMSET, READ, sizeof(int));
+  _(TIOCNOTTY, NONE, 0);
+  _(TIOCNXCL, NONE, 0);
+  _(TIOCOUTQ, WRITE, sizeof(int));
+  _(TIOCPKT, READ, sizeof(int));
+  _(TIOCSCTTY, NONE, 0);
+  _(TIOCSETD, READ, sizeof(int));
+  _(TIOCSPGRP, READ, pid_t_sz);
+  _(TIOCSTI, READ, sizeof(char));
+  _(TIOCSWINSZ, READ, struct_winsize_sz);
+
+#if (SANITIZER_LINUX && !SANITIZER_ANDROID)
+  _(SIOCGETSGCNT, WRITE, struct_sioc_sg_req_sz);
+  _(SIOCGETVIFCNT, WRITE, struct_sioc_vif_req_sz);
+#endif
+
+#if SANITIZER_LINUX
+  // Conflicting request ids.
+  // _(CDROMAUDIOBUFSIZ, NONE, 0);
+  // _(SNDCTL_TMR_CONTINUE, NONE, 0);
+  // _(SNDCTL_TMR_START, NONE, 0);
+  // _(SNDCTL_TMR_STOP, NONE, 0);
+  // _(SOUND_MIXER_READ_LOUD, WRITE, sizeof(int)); // same as ...READ_ENHANCE
+  // _(SOUND_MIXER_READ_MUTE, WRITE, sizeof(int)); // same as ...READ_ENHANCE
+  // _(SOUND_MIXER_WRITE_LOUD, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE
+  // _(SOUND_MIXER_WRITE_MUTE, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE
+  _(BLKFLSBUF, NONE, 0);
+  _(BLKGETSIZE, WRITE, sizeof(uptr));
+  _(BLKRAGET, WRITE, sizeof(int));
+  _(BLKRASET, NONE, 0);
+  _(BLKROGET, WRITE, sizeof(int));
+  _(BLKROSET, READ, sizeof(int));
+  _(BLKRRPART, NONE, 0);
+  _(CDROMEJECT, NONE, 0);
+  _(CDROMEJECT_SW, NONE, 0);
+  _(CDROMMULTISESSION, WRITE, struct_cdrom_multisession_sz);
+  _(CDROMPAUSE, NONE, 0);
+  _(CDROMPLAYMSF, READ, struct_cdrom_msf_sz);
+  _(CDROMPLAYTRKIND, READ, struct_cdrom_ti_sz);
+  _(CDROMREADAUDIO, READ, struct_cdrom_read_audio_sz);
+  _(CDROMREADCOOKED, READ, struct_cdrom_msf_sz);
+  _(CDROMREADMODE1, READ, struct_cdrom_msf_sz);
+  _(CDROMREADMODE2, READ, struct_cdrom_msf_sz);
+  _(CDROMREADRAW, READ, struct_cdrom_msf_sz);
+  _(CDROMREADTOCENTRY, WRITE, struct_cdrom_tocentry_sz);
+  _(CDROMREADTOCHDR, WRITE, struct_cdrom_tochdr_sz);
+  _(CDROMRESET, NONE, 0);
+  _(CDROMRESUME, NONE, 0);
+  _(CDROMSEEK, READ, struct_cdrom_msf_sz);
+  _(CDROMSTART, NONE, 0);
+  _(CDROMSTOP, NONE, 0);
+  _(CDROMSUBCHNL, WRITE, struct_cdrom_subchnl_sz);
+  _(CDROMVOLCTRL, READ, struct_cdrom_volctrl_sz);
+  _(CDROMVOLREAD, WRITE, struct_cdrom_volctrl_sz);
+  _(CDROM_GET_UPC, WRITE, 8);
+  _(EVIOCGABS, WRITE, struct_input_absinfo_sz); // fixup
+  _(EVIOCGBIT, WRITE, struct_input_id_sz); // fixup
+  _(EVIOCGEFFECTS, WRITE, sizeof(int));
+  _(EVIOCGID, WRITE, struct_input_id_sz);
+  _(EVIOCGKEY, WRITE, 0);
+  _(EVIOCGKEYCODE, WRITE, sizeof(int) * 2);
+  _(EVIOCGLED, WRITE, 0);
+  _(EVIOCGNAME, WRITE, 0);
+  _(EVIOCGPHYS, WRITE, 0);
+  _(EVIOCGRAB, READ, sizeof(int));
+  _(EVIOCGREP, WRITE, sizeof(int) * 2);
+  _(EVIOCGSND, WRITE, 0);
+  _(EVIOCGSW, WRITE, 0);
+  _(EVIOCGUNIQ, WRITE, 0);
+  _(EVIOCGVERSION, WRITE, sizeof(int));
+  _(EVIOCRMFF, READ, sizeof(int));
+  _(EVIOCSABS, READ, struct_input_absinfo_sz); // fixup
+  _(EVIOCSFF, READ, struct_ff_effect_sz);
+  _(EVIOCSKEYCODE, READ, sizeof(int) * 2);
+  _(EVIOCSREP, READ, sizeof(int) * 2);
+  _(FDCLRPRM, NONE, 0);
+  _(FDDEFPRM, READ, struct_floppy_struct_sz);
+  _(FDFLUSH, NONE, 0);
+  _(FDFMTBEG, NONE, 0);
+  _(FDFMTEND, NONE, 0);
+  _(FDFMTTRK, READ, struct_format_descr_sz);
+  _(FDGETDRVPRM, WRITE, struct_floppy_drive_params_sz);
+  _(FDGETDRVSTAT, WRITE, struct_floppy_drive_struct_sz);
+  _(FDGETDRVTYP, WRITE, 16);
+  _(FDGETFDCSTAT, WRITE, struct_floppy_fdc_state_sz);
+  _(FDGETMAXERRS, WRITE, struct_floppy_max_errors_sz);
+  _(FDGETPRM, WRITE, struct_floppy_struct_sz);
+  _(FDMSGOFF, NONE, 0);
+  _(FDMSGON, NONE, 0);
+  _(FDPOLLDRVSTAT, WRITE, struct_floppy_drive_struct_sz);
+  _(FDRAWCMD, WRITE, struct_floppy_raw_cmd_sz);
+  _(FDRESET, NONE, 0);
+  _(FDSETDRVPRM, READ, struct_floppy_drive_params_sz);
+  _(FDSETEMSGTRESH, NONE, 0);
+  _(FDSETMAXERRS, READ, struct_floppy_max_errors_sz);
+  _(FDSETPRM, READ, struct_floppy_struct_sz);
+  _(FDTWADDLE, NONE, 0);
+  _(FDWERRORCLR, NONE, 0);
+  _(FDWERRORGET, WRITE, struct_floppy_write_errors_sz);
+  _(HDIO_DRIVE_CMD, WRITE, sizeof(int));
+  _(HDIO_GETGEO, WRITE, struct_hd_geometry_sz);
+  _(HDIO_GET_32BIT, WRITE, sizeof(int));
+  _(HDIO_GET_DMA, WRITE, sizeof(int));
+  _(HDIO_GET_IDENTITY, WRITE, struct_hd_driveid_sz);
+  _(HDIO_GET_KEEPSETTINGS, WRITE, sizeof(int));
+  _(HDIO_GET_MULTCOUNT, WRITE, sizeof(int));
+  _(HDIO_GET_NOWERR, WRITE, sizeof(int));
+  _(HDIO_GET_UNMASKINTR, WRITE, sizeof(int));
+  _(HDIO_SET_32BIT, NONE, 0);
+  _(HDIO_SET_DMA, NONE, 0);
+  _(HDIO_SET_KEEPSETTINGS, NONE, 0);
+  _(HDIO_SET_MULTCOUNT, NONE, 0);
+  _(HDIO_SET_NOWERR, NONE, 0);
+  _(HDIO_SET_UNMASKINTR, NONE, 0);
+  _(MTIOCGET, WRITE, struct_mtget_sz);
+  _(MTIOCPOS, WRITE, struct_mtpos_sz);
+  _(MTIOCTOP, READ, struct_mtop_sz);
+  _(PPPIOCGASYNCMAP, WRITE, sizeof(int));
+  _(PPPIOCGDEBUG, WRITE, sizeof(int));
+  _(PPPIOCGFLAGS, WRITE, sizeof(int));
+  _(PPPIOCGUNIT, WRITE, sizeof(int));
+  _(PPPIOCGXASYNCMAP, WRITE, sizeof(int) * 8);
+  _(PPPIOCSASYNCMAP, READ, sizeof(int));
+  _(PPPIOCSDEBUG, READ, sizeof(int));
+  _(PPPIOCSFLAGS, READ, sizeof(int));
+  _(PPPIOCSMAXCID, READ, sizeof(int));
+  _(PPPIOCSMRU, READ, sizeof(int));
+  _(PPPIOCSXASYNCMAP, READ, sizeof(int) * 8);
+  _(SIOCADDRT, READ, struct_rtentry_sz);
+  _(SIOCDARP, READ, struct_arpreq_sz);
+  _(SIOCDELRT, READ, struct_rtentry_sz);
+  _(SIOCDRARP, READ, struct_arpreq_sz);
+  _(SIOCGARP, WRITE, struct_arpreq_sz);
+  _(SIOCGIFENCAP, WRITE, sizeof(int));
+  _(SIOCGIFHWADDR, WRITE, struct_ifreq_sz);
+  _(SIOCGIFMAP, WRITE, struct_ifreq_sz);
+  _(SIOCGIFMEM, WRITE, struct_ifreq_sz);
+  _(SIOCGIFNAME, NONE, 0);
+  _(SIOCGIFSLAVE, NONE, 0);
+  _(SIOCGRARP, WRITE, struct_arpreq_sz);
+  _(SIOCGSTAMP, WRITE, timeval_sz);
+  _(SIOCSARP, READ, struct_arpreq_sz);
+  _(SIOCSIFENCAP, READ, sizeof(int));
+  _(SIOCSIFHWADDR, READ, struct_ifreq_sz);
+  _(SIOCSIFLINK, NONE, 0);
+  _(SIOCSIFMAP, READ, struct_ifreq_sz);
+  _(SIOCSIFMEM, READ, struct_ifreq_sz);
+  _(SIOCSIFSLAVE, NONE, 0);
+  _(SIOCSRARP, READ, struct_arpreq_sz);
+  _(SNDCTL_COPR_HALT, WRITE, struct_copr_debug_buf_sz);
+  _(SNDCTL_COPR_LOAD, READ, struct_copr_buffer_sz);
+  _(SNDCTL_COPR_RCODE, WRITE, struct_copr_debug_buf_sz);
+  _(SNDCTL_COPR_RCVMSG, WRITE, struct_copr_msg_sz);
+  _(SNDCTL_COPR_RDATA, WRITE, struct_copr_debug_buf_sz);
+  _(SNDCTL_COPR_RESET, NONE, 0);
+  _(SNDCTL_COPR_RUN, WRITE, struct_copr_debug_buf_sz);
+  _(SNDCTL_COPR_SENDMSG, READ, struct_copr_msg_sz);
+  _(SNDCTL_COPR_WCODE, READ, struct_copr_debug_buf_sz);
+  _(SNDCTL_COPR_WDATA, READ, struct_copr_debug_buf_sz);
+  _(SNDCTL_DSP_GETBLKSIZE, WRITE, sizeof(int));
+  _(SNDCTL_DSP_GETFMTS, WRITE, sizeof(int));
+  _(SNDCTL_DSP_NONBLOCK, NONE, 0);
+  _(SNDCTL_DSP_POST, NONE, 0);
+  _(SNDCTL_DSP_RESET, NONE, 0);
+  _(SNDCTL_DSP_SETFMT, WRITE, sizeof(int));
+  _(SNDCTL_DSP_SETFRAGMENT, WRITE, sizeof(int));
+  _(SNDCTL_DSP_SPEED, WRITE, sizeof(int));
+  _(SNDCTL_DSP_STEREO, WRITE, sizeof(int));
+  _(SNDCTL_DSP_SUBDIVIDE, WRITE, sizeof(int));
+  _(SNDCTL_DSP_SYNC, NONE, 0);
+  _(SNDCTL_FM_4OP_ENABLE, READ, sizeof(int));
+  _(SNDCTL_FM_LOAD_INSTR, READ, struct_sbi_instrument_sz);
+  _(SNDCTL_MIDI_INFO, WRITE, struct_midi_info_sz);
+  _(SNDCTL_MIDI_PRETIME, WRITE, sizeof(int));
+  _(SNDCTL_SEQ_CTRLRATE, WRITE, sizeof(int));
+  _(SNDCTL_SEQ_GETINCOUNT, WRITE, sizeof(int));
+  _(SNDCTL_SEQ_GETOUTCOUNT, WRITE, sizeof(int));
+  _(SNDCTL_SEQ_NRMIDIS, WRITE, sizeof(int));
+  _(SNDCTL_SEQ_NRSYNTHS, WRITE, sizeof(int));
+  _(SNDCTL_SEQ_OUTOFBAND, READ, struct_seq_event_rec_sz);
+  _(SNDCTL_SEQ_PANIC, NONE, 0);
+  _(SNDCTL_SEQ_PERCMODE, NONE, 0);
+  _(SNDCTL_SEQ_RESET, NONE, 0);
+  _(SNDCTL_SEQ_RESETSAMPLES, READ, sizeof(int));
+  _(SNDCTL_SEQ_SYNC, NONE, 0);
+  _(SNDCTL_SEQ_TESTMIDI, READ, sizeof(int));
+  _(SNDCTL_SEQ_THRESHOLD, READ, sizeof(int));
+  _(SNDCTL_SYNTH_INFO, WRITE, struct_synth_info_sz);
+  _(SNDCTL_SYNTH_MEMAVL, WRITE, sizeof(int));
+  _(SNDCTL_TMR_METRONOME, READ, sizeof(int));
+  _(SNDCTL_TMR_SELECT, WRITE, sizeof(int));
+  _(SNDCTL_TMR_SOURCE, WRITE, sizeof(int));
+  _(SNDCTL_TMR_TEMPO, WRITE, sizeof(int));
+  _(SNDCTL_TMR_TIMEBASE, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_ALTPCM, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_BASS, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_CAPS, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_CD, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_DEVMASK, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_ENHANCE, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_IGAIN, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_IMIX, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_LINE, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_LINE1, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_LINE2, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_LINE3, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_MIC, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_OGAIN, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_PCM, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_RECLEV, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_RECMASK, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_RECSRC, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_SPEAKER, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_STEREODEVS, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_SYNTH, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_TREBLE, WRITE, sizeof(int));
+  _(SOUND_MIXER_READ_VOLUME, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_ALTPCM, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_BASS, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_CD, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_ENHANCE, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_IGAIN, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_IMIX, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_LINE, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_LINE1, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_LINE2, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_LINE3, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_MIC, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_OGAIN, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_PCM, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_RECLEV, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_RECSRC, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_SPEAKER, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_SYNTH, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_TREBLE, WRITE, sizeof(int));
+  _(SOUND_MIXER_WRITE_VOLUME, WRITE, sizeof(int));
+  _(SOUND_PCM_READ_BITS, WRITE, sizeof(int));
+  _(SOUND_PCM_READ_CHANNELS, WRITE, sizeof(int));
+  _(SOUND_PCM_READ_FILTER, WRITE, sizeof(int));
+  _(SOUND_PCM_READ_RATE, WRITE, sizeof(int));
+  _(SOUND_PCM_WRITE_CHANNELS, WRITE, sizeof(int));
+  _(SOUND_PCM_WRITE_FILTER, WRITE, sizeof(int));
+  _(TCFLSH, NONE, 0);
+  _(TCGETA, WRITE, struct_termio_sz);
+  _(TCGETS, WRITE, struct_termios_sz);
+  _(TCSBRK, NONE, 0);
+  _(TCSBRKP, NONE, 0);
+  _(TCSETA, READ, struct_termio_sz);
+  _(TCSETAF, READ, struct_termio_sz);
+  _(TCSETAW, READ, struct_termio_sz);
+  _(TCSETS, READ, struct_termios_sz);
+  _(TCSETSF, READ, struct_termios_sz);
+  _(TCSETSW, READ, struct_termios_sz);
+  _(TCXONC, NONE, 0);
+  _(TIOCGLCKTRMIOS, WRITE, struct_termios_sz);
+  _(TIOCGSOFTCAR, WRITE, sizeof(int));
+  _(TIOCINQ, WRITE, sizeof(int));
+  _(TIOCLINUX, READ, sizeof(char));
+  _(TIOCSERCONFIG, NONE, 0);
+  _(TIOCSERGETLSR, WRITE, sizeof(int));
+  _(TIOCSERGWILD, WRITE, sizeof(int));
+  _(TIOCSERSWILD, READ, sizeof(int));
+  _(TIOCSLCKTRMIOS, READ, struct_termios_sz);
+  _(TIOCSSOFTCAR, READ, sizeof(int));
+  _(VT_ACTIVATE, NONE, 0);
+  _(VT_DISALLOCATE, NONE, 0);
+  _(VT_GETMODE, WRITE, struct_vt_mode_sz);
+  _(VT_GETSTATE, WRITE, struct_vt_stat_sz);
+  _(VT_OPENQRY, WRITE, sizeof(int));
+  _(VT_RELDISP, NONE, 0);
+  _(VT_RESIZE, READ, struct_vt_sizes_sz);
+  _(VT_RESIZEX, READ, struct_vt_consize_sz);
+  _(VT_SENDSIG, NONE, 0);
+  _(VT_SETMODE, READ, struct_vt_mode_sz);
+  _(VT_WAITACTIVE, NONE, 0);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  // _(SIOCDEVPLIP, WRITE, struct_ifreq_sz); // the same as EQL_ENSLAVE
+  _(CYGETDEFTHRESH, WRITE, sizeof(int));
+  _(CYGETDEFTIMEOUT, WRITE, sizeof(int));
+  _(CYGETMON, WRITE, struct_cyclades_monitor_sz);
+  _(CYGETTHRESH, WRITE, sizeof(int));
+  _(CYGETTIMEOUT, WRITE, sizeof(int));
+  _(CYSETDEFTHRESH, NONE, 0);
+  _(CYSETDEFTIMEOUT, NONE, 0);
+  _(CYSETTHRESH, NONE, 0);
+  _(CYSETTIMEOUT, NONE, 0);
+  _(EQL_EMANCIPATE, WRITE, struct_ifreq_sz);
+  _(EQL_ENSLAVE, WRITE, struct_ifreq_sz);
+  _(EQL_GETMASTRCFG, WRITE, struct_ifreq_sz);
+  _(EQL_GETSLAVECFG, WRITE, struct_ifreq_sz);
+  _(EQL_SETMASTRCFG, WRITE, struct_ifreq_sz);
+  _(EQL_SETSLAVECFG, WRITE, struct_ifreq_sz);
+  _(EVIOCGKEYCODE_V2, WRITE, struct_input_keymap_entry_sz);
+  _(EVIOCGPROP, WRITE, 0);
+  _(EVIOCSKEYCODE_V2, READ, struct_input_keymap_entry_sz);
+  _(FS_IOC_GETFLAGS, WRITE, sizeof(int));
+  _(FS_IOC_GETVERSION, WRITE, sizeof(int));
+  _(FS_IOC_SETFLAGS, READ, sizeof(int));
+  _(FS_IOC_SETVERSION, READ, sizeof(int));
+  _(GIO_CMAP, WRITE, 48);
+  _(GIO_FONT, WRITE, 8192);
+  _(GIO_SCRNMAP, WRITE, e_tabsz);
+  _(GIO_UNIMAP, WRITE, struct_unimapdesc_sz);
+  _(GIO_UNISCRNMAP, WRITE, sizeof(short) * e_tabsz);
+  _(KDADDIO, NONE, 0);
+  _(KDDELIO, NONE, 0);
+  _(KDDISABIO, NONE, 0);
+  _(KDENABIO, NONE, 0);
+  _(KDGETKEYCODE, WRITE, struct_kbkeycode_sz);
+  _(KDGETLED, WRITE, 1);
+  _(KDGETMODE, WRITE, sizeof(int));
+  _(KDGKBDIACR, WRITE, struct_kbdiacrs_sz);
+  _(KDGKBENT, WRITE, struct_kbentry_sz);
+  _(KDGKBLED, WRITE, sizeof(int));
+  _(KDGKBMETA, WRITE, sizeof(int));
+  _(KDGKBMODE, WRITE, sizeof(int));
+  _(KDGKBSENT, WRITE, struct_kbsentry_sz);
+  _(KDGKBTYPE, WRITE, 1);
+  _(KDMAPDISP, NONE, 0);
+  _(KDMKTONE, NONE, 0);
+  _(KDSETKEYCODE, READ, struct_kbkeycode_sz);
+  _(KDSETLED, NONE, 0);
+  _(KDSETMODE, NONE, 0);
+  _(KDSIGACCEPT, NONE, 0);
+  _(KDSKBDIACR, READ, struct_kbdiacrs_sz);
+  _(KDSKBENT, READ, struct_kbentry_sz);
+  _(KDSKBLED, NONE, 0);
+  _(KDSKBMETA, NONE, 0);
+  _(KDSKBMODE, NONE, 0);
+  _(KDSKBSENT, READ, struct_kbsentry_sz);
+  _(KDUNMAPDISP, NONE, 0);
+  _(KIOCSOUND, NONE, 0);
+  _(LPABORT, NONE, 0);
+  _(LPABORTOPEN, NONE, 0);
+  _(LPCAREFUL, NONE, 0);
+  _(LPCHAR, NONE, 0);
+  _(LPGETIRQ, WRITE, sizeof(int));
+  _(LPGETSTATUS, WRITE, sizeof(int));
+  _(LPRESET, NONE, 0);
+  _(LPSETIRQ, NONE, 0);
+  _(LPTIME, NONE, 0);
+  _(LPWAIT, NONE, 0);
+  _(MTIOCGETCONFIG, WRITE, struct_mtconfiginfo_sz);
+  _(MTIOCSETCONFIG, READ, struct_mtconfiginfo_sz);
+  _(PIO_CMAP, NONE, 0);
+  _(PIO_FONT, READ, 8192);
+  _(PIO_SCRNMAP, READ, e_tabsz);
+  _(PIO_UNIMAP, READ, struct_unimapdesc_sz);
+  _(PIO_UNIMAPCLR, READ, struct_unimapinit_sz);
+  _(PIO_UNISCRNMAP, READ, sizeof(short) * e_tabsz);
+  _(SCSI_IOCTL_PROBE_HOST, READ, sizeof(int));
+  _(SCSI_IOCTL_TAGGED_DISABLE, NONE, 0);
+  _(SCSI_IOCTL_TAGGED_ENABLE, NONE, 0);
+  _(SNDCTL_DSP_GETISPACE, WRITE, struct_audio_buf_info_sz);
+  _(SNDCTL_DSP_GETOSPACE, WRITE, struct_audio_buf_info_sz);
+  _(TIOCGSERIAL, WRITE, struct_serial_struct_sz);
+  _(TIOCSERGETMULTI, WRITE, struct_serial_multiport_struct_sz);
+  _(TIOCSERSETMULTI, READ, struct_serial_multiport_struct_sz);
+  _(TIOCSSERIAL, READ, struct_serial_struct_sz);
+
+  // The following ioctl requests are shared between AX25, IPX, netrom and
+  // mrouted.
+  // _(SIOCAIPXITFCRT, READ, sizeof(char));
+  // _(SIOCAX25GETUID, READ, struct_sockaddr_ax25_sz);
+  // _(SIOCNRGETPARMS, WRITE, struct_nr_parms_struct_sz);
+  // _(SIOCAIPXPRISLT, READ, sizeof(char));
+  // _(SIOCNRSETPARMS, READ, struct_nr_parms_struct_sz);
+  // _(SIOCAX25ADDUID, READ, struct_sockaddr_ax25_sz);
+  // _(SIOCNRDECOBS, NONE, 0);
+  // _(SIOCAX25DELUID, READ, struct_sockaddr_ax25_sz);
+  // _(SIOCIPXCFGDATA, WRITE, struct_ipx_config_data_sz);
+  // _(SIOCAX25NOUID, READ, sizeof(int));
+  // _(SIOCNRRTCTL, READ, sizeof(int));
+  // _(SIOCAX25DIGCTL, READ, sizeof(int));
+  // _(SIOCAX25GETPARMS, WRITE, struct_ax25_parms_struct_sz);
+  // _(SIOCAX25SETPARMS, READ, struct_ax25_parms_struct_sz);
+#endif
+#undef _
+}
+
+static bool ioctl_initialized = false;
+
+struct ioctl_desc_compare {
+  bool operator()(const ioctl_desc& left, const ioctl_desc& right) const {
+    return left.req < right.req;
+  }
+};
+
+static void ioctl_init() {
+  ioctl_table_fill();
+  InternalSort(&ioctl_table, ioctl_table_size, ioctl_desc_compare());
+
+  bool bad = false;
+  for (unsigned i = 0; i < ioctl_table_size - 1; ++i) {
+    if (ioctl_table[i].req >= ioctl_table[i + 1].req) {
+      Printf("Duplicate or unsorted ioctl request id %x >= %x (%s vs %s)\n",
+             ioctl_table[i].req, ioctl_table[i + 1].req, ioctl_table[i].name,
+             ioctl_table[i + 1].name);
+      bad = true;
+    }
+  }
+
+  if (bad) Die();
+
+  ioctl_initialized = true;
+}
+
+// Handle the most evil ioctls that encode argument value as part of request id.
+static unsigned ioctl_request_fixup(unsigned req) {
+#if SANITIZER_LINUX
+  // Strip size and event number.
+  const unsigned kEviocgbitMask =
+      (IOC_SIZEMASK << IOC_SIZESHIFT) | EVIOC_EV_MAX;
+  if ((req & ~kEviocgbitMask) == IOCTL_EVIOCGBIT)
+    return IOCTL_EVIOCGBIT;
+  // Strip absolute axis number.
+  if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCGABS)
+    return IOCTL_EVIOCGABS;
+  if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCSABS)
+    return IOCTL_EVIOCSABS;
+#endif
+  return req;
+}
+
+static const ioctl_desc *ioctl_table_lookup(unsigned req) {
+  int left = 0;
+  int right = ioctl_table_size;
+  while (left < right) {
+    int mid = (left + right) / 2;
+    if (ioctl_table[mid].req < req)
+      left = mid + 1;
+    else
+      right = mid;
+  }
+  if (left == right && ioctl_table[left].req == req)
+    return ioctl_table + left;
+  else
+    return nullptr;
+}
+
+static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
+  CHECK(desc);
+  desc->req = req;
+  desc->name = "<DECODED_IOCTL>";
+  desc->size = IOC_SIZE(req);
+  // Sanity check.
+  if (desc->size > 0xFFFF) return false;
+  unsigned dir = IOC_DIR(req);
+  switch (dir) {
+    case IOC_NONE:
+      desc->type = ioctl_desc::NONE;
+      break;
+    case IOC_READ | IOC_WRITE:
+      desc->type = ioctl_desc::READWRITE;
+      break;
+    case IOC_READ:
+      desc->type = ioctl_desc::WRITE;
+      break;
+    case IOC_WRITE:
+      desc->type = ioctl_desc::READ;
+      break;
+    default:
+      return false;
+  }
+  // Size can be 0 iff type is NONE.
+  if ((desc->type == IOC_NONE) != (desc->size == 0)) return false;
+  // Sanity check.
+  if (IOC_TYPE(req) == 0) return false;
+  return true;
+}
+
+static const ioctl_desc *ioctl_lookup(unsigned req) {
+  req = ioctl_request_fixup(req);
+  const ioctl_desc *desc = ioctl_table_lookup(req);
+  if (desc) return desc;
+
+  // Try stripping access size from the request id.
+  desc = ioctl_table_lookup(req & ~(IOC_SIZEMASK << IOC_SIZESHIFT));
+  // Sanity check: requests that encode access size are either read or write and
+  // have size of 0 in the table.
+  if (desc && desc->size == 0 &&
+      (desc->type == ioctl_desc::READWRITE || desc->type == ioctl_desc::WRITE ||
+       desc->type == ioctl_desc::READ))
+    return desc;
+  return nullptr;
+}
+
+static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
+                             unsigned request, void *arg) {
+  if (desc->type == ioctl_desc::READ || desc->type == ioctl_desc::READWRITE) {
+    unsigned size = desc->size ? desc->size : IOC_SIZE(request);
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, arg, size);
+  }
+  if (desc->type != ioctl_desc::CUSTOM)
+    return;
+  if (request == IOCTL_SIOCGIFCONF) {
+    struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+    COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
+  }
+}
+
+static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
+                              unsigned request, void *arg) {
+  if (desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READWRITE) {
+    // FIXME: add verbose output
+    unsigned size = desc->size ? desc->size : IOC_SIZE(request);
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg, size);
+  }
+  if (desc->type != ioctl_desc::CUSTOM)
+    return;
+  if (request == IOCTL_SIOCGIFCONF) {
+    struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+    COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
+  }
+}
diff --git a/lsan/include/sanitizer_common/sanitizer_common_syscalls.inc b/lsan/include/sanitizer_common/sanitizer_common_syscalls.inc
new file mode 100644 (file)
index 0000000..616b39c
--- /dev/null
@@ -0,0 +1,2852 @@
+//===-- sanitizer_common_syscalls.inc ---------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Common syscalls handlers for tools like AddressSanitizer,
+// ThreadSanitizer, MemorySanitizer, etc.
+//
+// This file should be included into the tool's interceptor file,
+// which has to define it's own macros:
+//   COMMON_SYSCALL_PRE_READ_RANGE
+//          Called in prehook for regions that will be read by the kernel and
+//          must be initialized.
+//   COMMON_SYSCALL_PRE_WRITE_RANGE
+//          Called in prehook for regions that will be written to by the kernel
+//          and must be addressable. The actual write range may be smaller than
+//          reported in the prehook. See POST_WRITE_RANGE.
+//   COMMON_SYSCALL_POST_READ_RANGE
+//          Called in posthook for regions that were read by the kernel. Does
+//          not make much sense.
+//   COMMON_SYSCALL_POST_WRITE_RANGE
+//          Called in posthook for regions that were written to by the kernel
+//          and are now initialized.
+//   COMMON_SYSCALL_ACQUIRE(addr)
+//          Acquire memory visibility from addr.
+//   COMMON_SYSCALL_RELEASE(addr)
+//          Release memory visibility to addr.
+//   COMMON_SYSCALL_FD_CLOSE(fd)
+//          Called before closing file descriptor fd.
+//   COMMON_SYSCALL_FD_ACQUIRE(fd)
+//          Acquire memory visibility from fd.
+//   COMMON_SYSCALL_FD_RELEASE(fd)
+//          Release memory visibility to fd.
+//   COMMON_SYSCALL_PRE_FORK()
+//          Called before fork syscall.
+//   COMMON_SYSCALL_POST_FORK(long res)
+//          Called after fork syscall.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include "sanitizer_libc.h"
+
+#define PRE_SYSCALL(name)                                                      \
+  SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name
+#define PRE_READ(p, s) COMMON_SYSCALL_PRE_READ_RANGE(p, s)
+#define PRE_WRITE(p, s) COMMON_SYSCALL_PRE_WRITE_RANGE(p, s)
+
+#define POST_SYSCALL(name)                                                     \
+  SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_post_impl_##name
+#define POST_READ(p, s) COMMON_SYSCALL_POST_READ_RANGE(p, s)
+#define POST_WRITE(p, s) COMMON_SYSCALL_POST_WRITE_RANGE(p, s)
+
+#ifndef COMMON_SYSCALL_ACQUIRE
+# define COMMON_SYSCALL_ACQUIRE(addr) ((void)(addr))
+#endif
+
+#ifndef COMMON_SYSCALL_RELEASE
+# define COMMON_SYSCALL_RELEASE(addr) ((void)(addr))
+#endif
+
+#ifndef COMMON_SYSCALL_FD_CLOSE
+# define COMMON_SYSCALL_FD_CLOSE(fd) ((void)(fd))
+#endif
+
+#ifndef COMMON_SYSCALL_FD_ACQUIRE
+# define COMMON_SYSCALL_FD_ACQUIRE(fd) ((void)(fd))
+#endif
+
+#ifndef COMMON_SYSCALL_FD_RELEASE
+# define COMMON_SYSCALL_FD_RELEASE(fd) ((void)(fd))
+#endif
+
+#ifndef COMMON_SYSCALL_PRE_FORK
+# define COMMON_SYSCALL_PRE_FORK() {}
+#endif
+
+#ifndef COMMON_SYSCALL_POST_FORK
+# define COMMON_SYSCALL_POST_FORK(res) {}
+#endif
+
+// FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such).
+
+extern "C" {
+struct sanitizer_kernel_iovec {
+  void *iov_base;
+  unsigned long iov_len;
+};
+
+struct sanitizer_kernel_msghdr {
+  void *msg_name;
+  int msg_namelen;
+  struct sanitizer_kernel_iovec *msg_iov;
+  unsigned long msg_iovlen;
+  void *msg_control;
+  unsigned long msg_controllen;
+  unsigned msg_flags;
+};
+
+struct sanitizer_kernel_mmsghdr {
+  struct sanitizer_kernel_msghdr msg_hdr;
+  unsigned msg_len;
+};
+
+struct sanitizer_kernel_timespec {
+  long tv_sec;
+  long tv_nsec;
+};
+
+struct sanitizer_kernel_timeval {
+  long tv_sec;
+  long tv_usec;
+};
+
+struct sanitizer_kernel_rusage {
+  struct sanitizer_kernel_timeval ru_timeval[2];
+  long ru_long[14];
+};
+
+struct sanitizer_kernel_sockaddr {
+  unsigned short sa_family;
+  char sa_data[14];
+};
+
+// Real sigset size is always passed as a syscall argument.
+// Declare it "void" to catch sizeof(kernel_sigset_t).
+typedef void kernel_sigset_t;
+
+static void kernel_write_iovec(const __sanitizer_iovec *iovec,
+                        SIZE_T iovlen, SIZE_T maxlen) {
+  for (SIZE_T i = 0; i < iovlen && maxlen; ++i) {
+    SSIZE_T sz = Min(iovec[i].iov_len, maxlen);
+    POST_WRITE(iovec[i].iov_base, sz);
+    maxlen -= sz;
+  }
+}
+
+// This functions uses POST_READ, because it needs to run after syscall to know
+// the real read range.
+static void kernel_read_iovec(const __sanitizer_iovec *iovec,
+                       SIZE_T iovlen, SIZE_T maxlen) {
+  POST_READ(iovec, sizeof(*iovec) * iovlen);
+  for (SIZE_T i = 0; i < iovlen && maxlen; ++i) {
+    SSIZE_T sz = Min(iovec[i].iov_len, maxlen);
+    POST_READ(iovec[i].iov_base, sz);
+    maxlen -= sz;
+  }
+}
+
+PRE_SYSCALL(recvmsg)(long sockfd, sanitizer_kernel_msghdr *msg, long flags) {
+  PRE_READ(msg, sizeof(*msg));
+}
+
+POST_SYSCALL(recvmsg)(long res, long sockfd, sanitizer_kernel_msghdr *msg,
+                      long flags) {
+  if (res >= 0) {
+    if (msg) {
+      for (unsigned long i = 0; i < msg->msg_iovlen; ++i) {
+        POST_WRITE(msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+      }
+      POST_WRITE(msg->msg_control, msg->msg_controllen);
+    }
+  }
+}
+
+PRE_SYSCALL(recvmmsg)(long fd, sanitizer_kernel_mmsghdr *msg, long vlen,
+                      long flags, void *timeout) {
+  PRE_READ(msg, vlen * sizeof(*msg));
+}
+
+POST_SYSCALL(recvmmsg)(long res, long fd, sanitizer_kernel_mmsghdr *msg,
+                       long vlen, long flags, void *timeout) {
+  if (res >= 0) {
+    if (msg) {
+      for (unsigned long i = 0; i < msg->msg_hdr.msg_iovlen; ++i) {
+        POST_WRITE(msg->msg_hdr.msg_iov[i].iov_base,
+                   msg->msg_hdr.msg_iov[i].iov_len);
+      }
+      POST_WRITE(msg->msg_hdr.msg_control, msg->msg_hdr.msg_controllen);
+      POST_WRITE(&msg->msg_len, sizeof(msg->msg_len));
+    }
+    if (timeout) POST_WRITE(timeout, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(read)(long fd, void *buf, uptr count) {
+  if (buf) {
+    PRE_WRITE(buf, count);
+  }
+}
+
+POST_SYSCALL(read)(long res, long fd, void *buf, uptr count) {
+  if (res > 0 && buf) {
+    POST_WRITE(buf, res);
+  }
+}
+
+PRE_SYSCALL(time)(void *tloc) {}
+
+POST_SYSCALL(time)(long res, void *tloc) {
+  if (res >= 0) {
+    if (tloc) POST_WRITE(tloc, sizeof(long));
+  }
+}
+
+PRE_SYSCALL(stime)(void *tptr) {}
+
+POST_SYSCALL(stime)(long res, void *tptr) {
+  if (res >= 0) {
+    if (tptr) POST_WRITE(tptr, sizeof(long));
+  }
+}
+
+PRE_SYSCALL(gettimeofday)(void *tv, void *tz) {}
+
+POST_SYSCALL(gettimeofday)(long res, void *tv, void *tz) {
+  if (res >= 0) {
+    if (tv) POST_WRITE(tv, timeval_sz);
+    if (tz) POST_WRITE(tz, struct_timezone_sz);
+  }
+}
+
+PRE_SYSCALL(settimeofday)(void *tv, void *tz) {}
+
+POST_SYSCALL(settimeofday)(long res, void *tv, void *tz) {
+  if (res >= 0) {
+    if (tv) POST_WRITE(tv, timeval_sz);
+    if (tz) POST_WRITE(tz, struct_timezone_sz);
+  }
+}
+
+#if !SANITIZER_ANDROID
+PRE_SYSCALL(adjtimex)(void *txc_p) {}
+
+POST_SYSCALL(adjtimex)(long res, void *txc_p) {
+  if (res >= 0) {
+    if (txc_p) POST_WRITE(txc_p, struct_timex_sz);
+  }
+}
+#endif
+
+PRE_SYSCALL(times)(void *tbuf) {}
+
+POST_SYSCALL(times)(long res, void *tbuf) {
+  if (res >= 0) {
+    if (tbuf) POST_WRITE(tbuf, struct_tms_sz);
+  }
+}
+
+PRE_SYSCALL(gettid)() {}
+
+POST_SYSCALL(gettid)(long res) {}
+
+PRE_SYSCALL(nanosleep)(void *rqtp, void *rmtp) {}
+
+POST_SYSCALL(nanosleep)(long res, void *rqtp, void *rmtp) {
+  if (res >= 0) {
+    if (rqtp) POST_WRITE(rqtp, struct_timespec_sz);
+    if (rmtp) POST_WRITE(rmtp, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(alarm)(long seconds) {}
+
+POST_SYSCALL(alarm)(long res, long seconds) {}
+
+PRE_SYSCALL(getpid)() {}
+
+POST_SYSCALL(getpid)(long res) {}
+
+PRE_SYSCALL(getppid)() {}
+
+POST_SYSCALL(getppid)(long res) {}
+
+PRE_SYSCALL(getuid)() {}
+
+POST_SYSCALL(getuid)(long res) {}
+
+PRE_SYSCALL(geteuid)() {}
+
+POST_SYSCALL(geteuid)(long res) {}
+
+PRE_SYSCALL(getgid)() {}
+
+POST_SYSCALL(getgid)(long res) {}
+
+PRE_SYSCALL(getegid)() {}
+
+POST_SYSCALL(getegid)(long res) {}
+
+PRE_SYSCALL(getresuid)(void *ruid, void *euid, void *suid) {}
+
+POST_SYSCALL(getresuid)(long res, void *ruid, void *euid, void *suid) {
+  if (res >= 0) {
+    if (ruid) POST_WRITE(ruid, sizeof(unsigned));
+    if (euid) POST_WRITE(euid, sizeof(unsigned));
+    if (suid) POST_WRITE(suid, sizeof(unsigned));
+  }
+}
+
+PRE_SYSCALL(getresgid)(void *rgid, void *egid, void *sgid) {}
+
+POST_SYSCALL(getresgid)(long res, void *rgid, void *egid, void *sgid) {
+  if (res >= 0) {
+    if (rgid) POST_WRITE(rgid, sizeof(unsigned));
+    if (egid) POST_WRITE(egid, sizeof(unsigned));
+    if (sgid) POST_WRITE(sgid, sizeof(unsigned));
+  }
+}
+
+PRE_SYSCALL(getpgid)(long pid) {}
+
+POST_SYSCALL(getpgid)(long res, long pid) {}
+
+PRE_SYSCALL(getpgrp)() {}
+
+POST_SYSCALL(getpgrp)(long res) {}
+
+PRE_SYSCALL(getsid)(long pid) {}
+
+POST_SYSCALL(getsid)(long res, long pid) {}
+
+PRE_SYSCALL(getgroups)(long gidsetsize, void *grouplist) {}
+
+POST_SYSCALL(getgroups)(long res, long gidsetsize,
+                        __sanitizer___kernel_gid_t *grouplist) {
+  if (res >= 0) {
+    if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist));
+  }
+}
+
+PRE_SYSCALL(setregid)(long rgid, long egid) {}
+
+POST_SYSCALL(setregid)(long res, long rgid, long egid) {}
+
+PRE_SYSCALL(setgid)(long gid) {}
+
+POST_SYSCALL(setgid)(long res, long gid) {}
+
+PRE_SYSCALL(setreuid)(long ruid, long euid) {}
+
+POST_SYSCALL(setreuid)(long res, long ruid, long euid) {}
+
+PRE_SYSCALL(setuid)(long uid) {}
+
+POST_SYSCALL(setuid)(long res, long uid) {}
+
+PRE_SYSCALL(setresuid)(long ruid, long euid, long suid) {}
+
+POST_SYSCALL(setresuid)(long res, long ruid, long euid, long suid) {}
+
+PRE_SYSCALL(setresgid)(long rgid, long egid, long sgid) {}
+
+POST_SYSCALL(setresgid)(long res, long rgid, long egid, long sgid) {}
+
+PRE_SYSCALL(setfsuid)(long uid) {}
+
+POST_SYSCALL(setfsuid)(long res, long uid) {}
+
+PRE_SYSCALL(setfsgid)(long gid) {}
+
+POST_SYSCALL(setfsgid)(long res, long gid) {}
+
+PRE_SYSCALL(setpgid)(long pid, long pgid) {}
+
+POST_SYSCALL(setpgid)(long res, long pid, long pgid) {}
+
+PRE_SYSCALL(setsid)() {}
+
+POST_SYSCALL(setsid)(long res) {}
+
+PRE_SYSCALL(setgroups)(long gidsetsize, __sanitizer___kernel_gid_t *grouplist) {
+  if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist));
+}
+
+POST_SYSCALL(setgroups)(long res, long gidsetsize,
+                        __sanitizer___kernel_gid_t *grouplist) {}
+
+PRE_SYSCALL(acct)(const void *name) {
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(acct)(long res, const void *name) {}
+
+PRE_SYSCALL(capget)(void *header, void *dataptr) {
+  if (header) PRE_READ(header, __user_cap_header_struct_sz);
+}
+
+POST_SYSCALL(capget)(long res, void *header, void *dataptr) {
+  if (res >= 0)
+    if (dataptr) POST_WRITE(dataptr, __user_cap_data_struct_sz);
+}
+
+PRE_SYSCALL(capset)(void *header, const void *data) {
+  if (header) PRE_READ(header, __user_cap_header_struct_sz);
+  if (data) PRE_READ(data, __user_cap_data_struct_sz);
+}
+
+POST_SYSCALL(capset)(long res, void *header, const void *data) {}
+
+PRE_SYSCALL(personality)(long personality) {}
+
+POST_SYSCALL(personality)(long res, long personality) {}
+
+PRE_SYSCALL(sigpending)(void *set) {}
+
+POST_SYSCALL(sigpending)(long res, void *set) {
+  if (res >= 0) {
+    if (set) POST_WRITE(set, old_sigset_t_sz);
+  }
+}
+
+PRE_SYSCALL(sigprocmask)(long how, void *set, void *oset) {}
+
+POST_SYSCALL(sigprocmask)(long res, long how, void *set, void *oset) {
+  if (res >= 0) {
+    if (set) POST_WRITE(set, old_sigset_t_sz);
+    if (oset) POST_WRITE(oset, old_sigset_t_sz);
+  }
+}
+
+PRE_SYSCALL(getitimer)(long which, void *value) {}
+
+POST_SYSCALL(getitimer)(long res, long which, void *value) {
+  if (res >= 0) {
+    if (value) POST_WRITE(value, struct_itimerval_sz);
+  }
+}
+
+PRE_SYSCALL(setitimer)(long which, void *value, void *ovalue) {}
+
+POST_SYSCALL(setitimer)(long res, long which, void *value, void *ovalue) {
+  if (res >= 0) {
+    if (value) POST_WRITE(value, struct_itimerval_sz);
+    if (ovalue) POST_WRITE(ovalue, struct_itimerval_sz);
+  }
+}
+
+PRE_SYSCALL(timer_create)(long which_clock, void *timer_event_spec,
+                          void *created_timer_id) {}
+
+POST_SYSCALL(timer_create)(long res, long which_clock, void *timer_event_spec,
+                           void *created_timer_id) {
+  if (res >= 0) {
+    if (timer_event_spec) POST_WRITE(timer_event_spec, struct_sigevent_sz);
+    if (created_timer_id) POST_WRITE(created_timer_id, sizeof(long));
+  }
+}
+
+PRE_SYSCALL(timer_gettime)(long timer_id, void *setting) {}
+
+POST_SYSCALL(timer_gettime)(long res, long timer_id, void *setting) {
+  if (res >= 0) {
+    if (setting) POST_WRITE(setting, struct_itimerspec_sz);
+  }
+}
+
+PRE_SYSCALL(timer_getoverrun)(long timer_id) {}
+
+POST_SYSCALL(timer_getoverrun)(long res, long timer_id) {}
+
+PRE_SYSCALL(timer_settime)(long timer_id, long flags, const void *new_setting,
+                           void *old_setting) {
+  if (new_setting) PRE_READ(new_setting, struct_itimerspec_sz);
+}
+
+POST_SYSCALL(timer_settime)(long res, long timer_id, long flags,
+                            const void *new_setting, void *old_setting) {
+  if (res >= 0) {
+    if (old_setting) POST_WRITE(old_setting, struct_itimerspec_sz);
+  }
+}
+
+PRE_SYSCALL(timer_delete)(long timer_id) {}
+
+POST_SYSCALL(timer_delete)(long res, long timer_id) {}
+
+PRE_SYSCALL(clock_settime)(long which_clock, const void *tp) {
+  if (tp) PRE_READ(tp, struct_timespec_sz);
+}
+
+POST_SYSCALL(clock_settime)(long res, long which_clock, const void *tp) {}
+
+PRE_SYSCALL(clock_gettime)(long which_clock, void *tp) {}
+
+POST_SYSCALL(clock_gettime)(long res, long which_clock, void *tp) {
+  if (res >= 0) {
+    if (tp) POST_WRITE(tp, struct_timespec_sz);
+  }
+}
+
+#if !SANITIZER_ANDROID
+PRE_SYSCALL(clock_adjtime)(long which_clock, void *tx) {}
+
+POST_SYSCALL(clock_adjtime)(long res, long which_clock, void *tx) {
+  if (res >= 0) {
+    if (tx) POST_WRITE(tx, struct_timex_sz);
+  }
+}
+#endif
+
+PRE_SYSCALL(clock_getres)(long which_clock, void *tp) {}
+
+POST_SYSCALL(clock_getres)(long res, long which_clock, void *tp) {
+  if (res >= 0) {
+    if (tp) POST_WRITE(tp, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(clock_nanosleep)(long which_clock, long flags, const void *rqtp,
+                             void *rmtp) {
+  if (rqtp) PRE_READ(rqtp, struct_timespec_sz);
+}
+
+POST_SYSCALL(clock_nanosleep)(long res, long which_clock, long flags,
+                              const void *rqtp, void *rmtp) {
+  if (res >= 0) {
+    if (rmtp) POST_WRITE(rmtp, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(nice)(long increment) {}
+
+POST_SYSCALL(nice)(long res, long increment) {}
+
+PRE_SYSCALL(sched_setscheduler)(long pid, long policy, void *param) {}
+
+POST_SYSCALL(sched_setscheduler)(long res, long pid, long policy, void *param) {
+  if (res >= 0) {
+    if (param) POST_WRITE(param, struct_sched_param_sz);
+  }
+}
+
+PRE_SYSCALL(sched_setparam)(long pid, void *param) {
+  if (param) PRE_READ(param, struct_sched_param_sz);
+}
+
+POST_SYSCALL(sched_setparam)(long res, long pid, void *param) {}
+
+PRE_SYSCALL(sched_getscheduler)(long pid) {}
+
+POST_SYSCALL(sched_getscheduler)(long res, long pid) {}
+
+PRE_SYSCALL(sched_getparam)(long pid, void *param) {}
+
+POST_SYSCALL(sched_getparam)(long res, long pid, void *param) {
+  if (res >= 0) {
+    if (param) POST_WRITE(param, struct_sched_param_sz);
+  }
+}
+
+PRE_SYSCALL(sched_setaffinity)(long pid, long len, void *user_mask_ptr) {
+  if (user_mask_ptr) PRE_READ(user_mask_ptr, len);
+}
+
+POST_SYSCALL(sched_setaffinity)(long res, long pid, long len,
+                                void *user_mask_ptr) {}
+
+PRE_SYSCALL(sched_getaffinity)(long pid, long len, void *user_mask_ptr) {}
+
+POST_SYSCALL(sched_getaffinity)(long res, long pid, long len,
+                                void *user_mask_ptr) {
+  if (res >= 0) {
+    if (user_mask_ptr) POST_WRITE(user_mask_ptr, len);
+  }
+}
+
+PRE_SYSCALL(sched_yield)() {}
+
+POST_SYSCALL(sched_yield)(long res) {}
+
+PRE_SYSCALL(sched_get_priority_max)(long policy) {}
+
+POST_SYSCALL(sched_get_priority_max)(long res, long policy) {}
+
+PRE_SYSCALL(sched_get_priority_min)(long policy) {}
+
+POST_SYSCALL(sched_get_priority_min)(long res, long policy) {}
+
+PRE_SYSCALL(sched_rr_get_interval)(long pid, void *interval) {}
+
+POST_SYSCALL(sched_rr_get_interval)(long res, long pid, void *interval) {
+  if (res >= 0) {
+    if (interval) POST_WRITE(interval, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(setpriority)(long which, long who, long niceval) {}
+
+POST_SYSCALL(setpriority)(long res, long which, long who, long niceval) {}
+
+PRE_SYSCALL(getpriority)(long which, long who) {}
+
+POST_SYSCALL(getpriority)(long res, long which, long who) {}
+
+PRE_SYSCALL(shutdown)(long arg0, long arg1) {}
+
+POST_SYSCALL(shutdown)(long res, long arg0, long arg1) {}
+
+PRE_SYSCALL(reboot)(long magic1, long magic2, long cmd, void *arg) {}
+
+POST_SYSCALL(reboot)(long res, long magic1, long magic2, long cmd, void *arg) {}
+
+PRE_SYSCALL(restart_syscall)() {}
+
+POST_SYSCALL(restart_syscall)(long res) {}
+
+PRE_SYSCALL(kexec_load)(long entry, long nr_segments, void *segments,
+                        long flags) {}
+
+POST_SYSCALL(kexec_load)(long res, long entry, long nr_segments, void *segments,
+                         long flags) {
+  if (res >= 0) {
+    if (segments) POST_WRITE(segments, struct_kexec_segment_sz);
+  }
+}
+
+PRE_SYSCALL(exit)(long error_code) {}
+
+POST_SYSCALL(exit)(long res, long error_code) {}
+
+PRE_SYSCALL(exit_group)(long error_code) {}
+
+POST_SYSCALL(exit_group)(long res, long error_code) {}
+
+PRE_SYSCALL(wait4)(long pid, void *stat_addr, long options, void *ru) {}
+
+POST_SYSCALL(wait4)(long res, long pid, void *stat_addr, long options,
+                    void *ru) {
+  if (res >= 0) {
+    if (stat_addr) POST_WRITE(stat_addr, sizeof(int));
+    if (ru) POST_WRITE(ru, struct_rusage_sz);
+  }
+}
+
+PRE_SYSCALL(waitid)(long which, long pid, void *infop, long options, void *ru) {
+}
+
+POST_SYSCALL(waitid)(long res, long which, long pid, void *infop, long options,
+                     void *ru) {
+  if (res >= 0) {
+    if (infop) POST_WRITE(infop, siginfo_t_sz);
+    if (ru) POST_WRITE(ru, struct_rusage_sz);
+  }
+}
+
+PRE_SYSCALL(waitpid)(long pid, void *stat_addr, long options) {}
+
+POST_SYSCALL(waitpid)(long res, long pid, void *stat_addr, long options) {
+  if (res >= 0) {
+    if (stat_addr) POST_WRITE(stat_addr, sizeof(int));
+  }
+}
+
+PRE_SYSCALL(set_tid_address)(void *tidptr) {}
+
+POST_SYSCALL(set_tid_address)(long res, void *tidptr) {
+  if (res >= 0) {
+    if (tidptr) POST_WRITE(tidptr, sizeof(int));
+  }
+}
+
+PRE_SYSCALL(init_module)(void *umod, long len, const void *uargs) {
+  if (uargs)
+    PRE_READ(uargs, __sanitizer::internal_strlen((const char *)uargs) + 1);
+}
+
+POST_SYSCALL(init_module)(long res, void *umod, long len, const void *uargs) {}
+
+PRE_SYSCALL(delete_module)(const void *name_user, long flags) {
+  if (name_user)
+    PRE_READ(name_user,
+             __sanitizer::internal_strlen((const char *)name_user) + 1);
+}
+
+POST_SYSCALL(delete_module)(long res, const void *name_user, long flags) {}
+
+PRE_SYSCALL(rt_sigprocmask)(long how, void *set, void *oset, long sigsetsize) {}
+
+POST_SYSCALL(rt_sigprocmask)(long res, long how, kernel_sigset_t *set,
+                             kernel_sigset_t *oset, long sigsetsize) {
+  if (res >= 0) {
+    if (set) POST_WRITE(set, sigsetsize);
+    if (oset) POST_WRITE(oset, sigsetsize);
+  }
+}
+
+PRE_SYSCALL(rt_sigpending)(void *set, long sigsetsize) {}
+
+POST_SYSCALL(rt_sigpending)(long res, kernel_sigset_t *set, long sigsetsize) {
+  if (res >= 0) {
+    if (set) POST_WRITE(set, sigsetsize);
+  }
+}
+
+PRE_SYSCALL(rt_sigtimedwait)(const kernel_sigset_t *uthese, void *uinfo,
+                             const void *uts, long sigsetsize) {
+  if (uthese) PRE_READ(uthese, sigsetsize);
+  if (uts) PRE_READ(uts, struct_timespec_sz);
+}
+
+POST_SYSCALL(rt_sigtimedwait)(long res, const void *uthese, void *uinfo,
+                              const void *uts, long sigsetsize) {
+  if (res >= 0) {
+    if (uinfo) POST_WRITE(uinfo, siginfo_t_sz);
+  }
+}
+
+PRE_SYSCALL(rt_tgsigqueueinfo)(long tgid, long pid, long sig, void *uinfo) {}
+
+POST_SYSCALL(rt_tgsigqueueinfo)(long res, long tgid, long pid, long sig,
+                                void *uinfo) {
+  if (res >= 0) {
+    if (uinfo) POST_WRITE(uinfo, siginfo_t_sz);
+  }
+}
+
+PRE_SYSCALL(kill)(long pid, long sig) {}
+
+POST_SYSCALL(kill)(long res, long pid, long sig) {}
+
+PRE_SYSCALL(tgkill)(long tgid, long pid, long sig) {}
+
+POST_SYSCALL(tgkill)(long res, long tgid, long pid, long sig) {}
+
+PRE_SYSCALL(tkill)(long pid, long sig) {}
+
+POST_SYSCALL(tkill)(long res, long pid, long sig) {}
+
+PRE_SYSCALL(rt_sigqueueinfo)(long pid, long sig, void *uinfo) {}
+
+POST_SYSCALL(rt_sigqueueinfo)(long res, long pid, long sig, void *uinfo) {
+  if (res >= 0) {
+    if (uinfo) POST_WRITE(uinfo, siginfo_t_sz);
+  }
+}
+
+PRE_SYSCALL(sgetmask)() {}
+
+POST_SYSCALL(sgetmask)(long res) {}
+
+PRE_SYSCALL(ssetmask)(long newmask) {}
+
+POST_SYSCALL(ssetmask)(long res, long newmask) {}
+
+PRE_SYSCALL(signal)(long sig, long handler) {}
+
+POST_SYSCALL(signal)(long res, long sig, long handler) {}
+
+PRE_SYSCALL(pause)() {}
+
+POST_SYSCALL(pause)(long res) {}
+
+PRE_SYSCALL(sync)() {}
+
+POST_SYSCALL(sync)(long res) {}
+
+PRE_SYSCALL(fsync)(long fd) {}
+
+POST_SYSCALL(fsync)(long res, long fd) {}
+
+PRE_SYSCALL(fdatasync)(long fd) {}
+
+POST_SYSCALL(fdatasync)(long res, long fd) {}
+
+PRE_SYSCALL(bdflush)(long func, long data) {}
+
+POST_SYSCALL(bdflush)(long res, long func, long data) {}
+
+PRE_SYSCALL(mount)(void *dev_name, void *dir_name, void *type, long flags,
+                   void *data) {}
+
+POST_SYSCALL(mount)(long res, void *dev_name, void *dir_name, void *type,
+                    long flags, void *data) {
+  if (res >= 0) {
+    if (dev_name)
+      POST_WRITE(dev_name,
+                 __sanitizer::internal_strlen((const char *)dev_name) + 1);
+    if (dir_name)
+      POST_WRITE(dir_name,
+                 __sanitizer::internal_strlen((const char *)dir_name) + 1);
+    if (type)
+      POST_WRITE(type, __sanitizer::internal_strlen((const char *)type) + 1);
+  }
+}
+
+PRE_SYSCALL(umount)(void *name, long flags) {}
+
+POST_SYSCALL(umount)(long res, void *name, long flags) {
+  if (res >= 0) {
+    if (name)
+      POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  }
+}
+
+PRE_SYSCALL(oldumount)(void *name) {}
+
+POST_SYSCALL(oldumount)(long res, void *name) {
+  if (res >= 0) {
+    if (name)
+      POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  }
+}
+
+PRE_SYSCALL(truncate)(const void *path, long length) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(truncate)(long res, const void *path, long length) {}
+
+PRE_SYSCALL(ftruncate)(long fd, long length) {}
+
+POST_SYSCALL(ftruncate)(long res, long fd, long length) {}
+
+PRE_SYSCALL(stat)(const void *filename, void *statbuf) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(stat)(long res, const void *filename, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz);
+  }
+}
+
+#if !SANITIZER_ANDROID
+PRE_SYSCALL(statfs)(const void *path, void *buf) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(statfs)(long res, const void *path, void *buf) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, struct_statfs_sz);
+  }
+}
+
+PRE_SYSCALL(statfs64)(const void *path, long sz, void *buf) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(statfs64)(long res, const void *path, long sz, void *buf) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, struct_statfs64_sz);
+  }
+}
+
+PRE_SYSCALL(fstatfs)(long fd, void *buf) {}
+
+POST_SYSCALL(fstatfs)(long res, long fd, void *buf) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, struct_statfs_sz);
+  }
+}
+
+PRE_SYSCALL(fstatfs64)(long fd, long sz, void *buf) {}
+
+POST_SYSCALL(fstatfs64)(long res, long fd, long sz, void *buf) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, struct_statfs64_sz);
+  }
+}
+#endif // !SANITIZER_ANDROID
+
+PRE_SYSCALL(lstat)(const void *filename, void *statbuf) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(lstat)(long res, const void *filename, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz);
+  }
+}
+
+PRE_SYSCALL(fstat)(long fd, void *statbuf) {}
+
+POST_SYSCALL(fstat)(long res, long fd, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz);
+  }
+}
+
+PRE_SYSCALL(newstat)(const void *filename, void *statbuf) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(newstat)(long res, const void *filename, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz);
+  }
+}
+
+PRE_SYSCALL(newlstat)(const void *filename, void *statbuf) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(newlstat)(long res, const void *filename, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz);
+  }
+}
+
+PRE_SYSCALL(newfstat)(long fd, void *statbuf) {}
+
+POST_SYSCALL(newfstat)(long res, long fd, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz);
+  }
+}
+
+#if !SANITIZER_ANDROID
+PRE_SYSCALL(ustat)(long dev, void *ubuf) {}
+
+POST_SYSCALL(ustat)(long res, long dev, void *ubuf) {
+  if (res >= 0) {
+    if (ubuf) POST_WRITE(ubuf, struct_ustat_sz);
+  }
+}
+#endif  // !SANITIZER_ANDROID
+
+PRE_SYSCALL(stat64)(const void *filename, void *statbuf) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(stat64)(long res, const void *filename, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz);
+  }
+}
+
+PRE_SYSCALL(fstat64)(long fd, void *statbuf) {}
+
+POST_SYSCALL(fstat64)(long res, long fd, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz);
+  }
+}
+
+PRE_SYSCALL(lstat64)(const void *filename, void *statbuf) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(lstat64)(long res, const void *filename, void *statbuf) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz);
+  }
+}
+
+PRE_SYSCALL(setxattr)(const void *path, const void *name, const void *value,
+                      long size, long flags) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  if (value) PRE_READ(value, size);
+}
+
+POST_SYSCALL(setxattr)(long res, const void *path, const void *name,
+                       const void *value, long size, long flags) {}
+
+PRE_SYSCALL(lsetxattr)(const void *path, const void *name, const void *value,
+                       long size, long flags) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  if (value) PRE_READ(value, size);
+}
+
+POST_SYSCALL(lsetxattr)(long res, const void *path, const void *name,
+                        const void *value, long size, long flags) {}
+
+PRE_SYSCALL(fsetxattr)(long fd, const void *name, const void *value, long size,
+                       long flags) {
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  if (value) PRE_READ(value, size);
+}
+
+POST_SYSCALL(fsetxattr)(long res, long fd, const void *name, const void *value,
+                        long size, long flags) {}
+
+PRE_SYSCALL(getxattr)(const void *path, const void *name, void *value,
+                      long size) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(getxattr)(long res, const void *path, const void *name,
+                       void *value, long size) {
+  if (size && res > 0) {
+    if (value) POST_WRITE(value, res);
+  }
+}
+
+PRE_SYSCALL(lgetxattr)(const void *path, const void *name, void *value,
+                       long size) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(lgetxattr)(long res, const void *path, const void *name,
+                        void *value, long size) {
+  if (size && res > 0) {
+    if (value) POST_WRITE(value, res);
+  }
+}
+
+PRE_SYSCALL(fgetxattr)(long fd, const void *name, void *value, long size) {
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(fgetxattr)(long res, long fd, const void *name, void *value,
+                        long size) {
+  if (size && res > 0) {
+    if (value) POST_WRITE(value, res);
+  }
+}
+
+PRE_SYSCALL(listxattr)(const void *path, void *list, long size) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(listxattr)(long res, const void *path, void *list, long size) {
+  if (size && res > 0) {
+    if (list) POST_WRITE(list, res);
+  }
+}
+
+PRE_SYSCALL(llistxattr)(const void *path, void *list, long size) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(llistxattr)(long res, const void *path, void *list, long size) {
+  if (size && res > 0) {
+    if (list) POST_WRITE(list, res);
+  }
+}
+
+PRE_SYSCALL(flistxattr)(long fd, void *list, long size) {}
+
+POST_SYSCALL(flistxattr)(long res, long fd, void *list, long size) {
+  if (size && res > 0) {
+    if (list) POST_WRITE(list, res);
+  }
+}
+
+PRE_SYSCALL(removexattr)(const void *path, const void *name) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(removexattr)(long res, const void *path, const void *name) {}
+
+PRE_SYSCALL(lremovexattr)(const void *path, const void *name) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(lremovexattr)(long res, const void *path, const void *name) {}
+
+PRE_SYSCALL(fremovexattr)(long fd, const void *name) {
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(fremovexattr)(long res, long fd, const void *name) {}
+
+PRE_SYSCALL(brk)(long brk) {}
+
+POST_SYSCALL(brk)(long res, long brk) {}
+
+PRE_SYSCALL(mprotect)(long start, long len, long prot) {}
+
+POST_SYSCALL(mprotect)(long res, long start, long len, long prot) {}
+
+PRE_SYSCALL(mremap)(long addr, long old_len, long new_len, long flags,
+                    long new_addr) {}
+
+POST_SYSCALL(mremap)(long res, long addr, long old_len, long new_len,
+                     long flags, long new_addr) {}
+
+PRE_SYSCALL(remap_file_pages)(long start, long size, long prot, long pgoff,
+                              long flags) {}
+
+POST_SYSCALL(remap_file_pages)(long res, long start, long size, long prot,
+                               long pgoff, long flags) {}
+
+PRE_SYSCALL(msync)(long start, long len, long flags) {}
+
+POST_SYSCALL(msync)(long res, long start, long len, long flags) {}
+
+PRE_SYSCALL(munmap)(long addr, long len) {}
+
+POST_SYSCALL(munmap)(long res, long addr, long len) {}
+
+PRE_SYSCALL(mlock)(long start, long len) {}
+
+POST_SYSCALL(mlock)(long res, long start, long len) {}
+
+PRE_SYSCALL(munlock)(long start, long len) {}
+
+POST_SYSCALL(munlock)(long res, long start, long len) {}
+
+PRE_SYSCALL(mlockall)(long flags) {}
+
+POST_SYSCALL(mlockall)(long res, long flags) {}
+
+PRE_SYSCALL(munlockall)() {}
+
+POST_SYSCALL(munlockall)(long res) {}
+
+PRE_SYSCALL(madvise)(long start, long len, long behavior) {}
+
+POST_SYSCALL(madvise)(long res, long start, long len, long behavior) {}
+
+PRE_SYSCALL(mincore)(long start, long len, void *vec) {}
+
+POST_SYSCALL(mincore)(long res, long start, long len, void *vec) {
+  if (res >= 0) {
+    if (vec) {
+      POST_WRITE(vec, (len + GetPageSizeCached() - 1) / GetPageSizeCached());
+    }
+  }
+}
+
+PRE_SYSCALL(pivot_root)(const void *new_root, const void *put_old) {
+  if (new_root)
+    PRE_READ(new_root,
+             __sanitizer::internal_strlen((const char *)new_root) + 1);
+  if (put_old)
+    PRE_READ(put_old, __sanitizer::internal_strlen((const char *)put_old) + 1);
+}
+
+POST_SYSCALL(pivot_root)(long res, const void *new_root, const void *put_old) {}
+
+PRE_SYSCALL(chroot)(const void *filename) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(chroot)(long res, const void *filename) {}
+
+PRE_SYSCALL(mknod)(const void *filename, long mode, long dev) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(mknod)(long res, const void *filename, long mode, long dev) {}
+
+PRE_SYSCALL(link)(const void *oldname, const void *newname) {
+  if (oldname)
+    PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1);
+  if (newname)
+    PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1);
+}
+
+POST_SYSCALL(link)(long res, const void *oldname, const void *newname) {}
+
+PRE_SYSCALL(symlink)(const void *old, const void *new_) {
+  if (old) PRE_READ(old, __sanitizer::internal_strlen((const char *)old) + 1);
+  if (new_)
+    PRE_READ(new_, __sanitizer::internal_strlen((const char *)new_) + 1);
+}
+
+POST_SYSCALL(symlink)(long res, const void *old, const void *new_) {}
+
+PRE_SYSCALL(unlink)(const void *pathname) {
+  if (pathname)
+    PRE_READ(pathname,
+             __sanitizer::internal_strlen((const char *)pathname) + 1);
+}
+
+POST_SYSCALL(unlink)(long res, const void *pathname) {}
+
+PRE_SYSCALL(rename)(const void *oldname, const void *newname) {
+  if (oldname)
+    PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1);
+  if (newname)
+    PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1);
+}
+
+POST_SYSCALL(rename)(long res, const void *oldname, const void *newname) {}
+
+PRE_SYSCALL(chmod)(const void *filename, long mode) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(chmod)(long res, const void *filename, long mode) {}
+
+PRE_SYSCALL(fchmod)(long fd, long mode) {}
+
+POST_SYSCALL(fchmod)(long res, long fd, long mode) {}
+
+PRE_SYSCALL(fcntl)(long fd, long cmd, long arg) {}
+
+POST_SYSCALL(fcntl)(long res, long fd, long cmd, long arg) {}
+
+PRE_SYSCALL(fcntl64)(long fd, long cmd, long arg) {}
+
+POST_SYSCALL(fcntl64)(long res, long fd, long cmd, long arg) {}
+
+PRE_SYSCALL(pipe)(void *fildes) {}
+
+POST_SYSCALL(pipe)(long res, void *fildes) {
+  if (res >= 0) {
+    if (fildes) POST_WRITE(fildes, sizeof(int));
+  }
+}
+
+PRE_SYSCALL(pipe2)(void *fildes, long flags) {}
+
+POST_SYSCALL(pipe2)(long res, void *fildes, long flags) {
+  if (res >= 0) {
+    if (fildes) POST_WRITE(fildes, sizeof(int));
+  }
+}
+
+PRE_SYSCALL(dup)(long fildes) {}
+
+POST_SYSCALL(dup)(long res, long fildes) {}
+
+PRE_SYSCALL(dup2)(long oldfd, long newfd) {}
+
+POST_SYSCALL(dup2)(long res, long oldfd, long newfd) {}
+
+PRE_SYSCALL(dup3)(long oldfd, long newfd, long flags) {}
+
+POST_SYSCALL(dup3)(long res, long oldfd, long newfd, long flags) {}
+
+PRE_SYSCALL(ioperm)(long from, long num, long on) {}
+
+POST_SYSCALL(ioperm)(long res, long from, long num, long on) {}
+
+PRE_SYSCALL(ioctl)(long fd, long cmd, long arg) {}
+
+POST_SYSCALL(ioctl)(long res, long fd, long cmd, long arg) {}
+
+PRE_SYSCALL(flock)(long fd, long cmd) {}
+
+POST_SYSCALL(flock)(long res, long fd, long cmd) {}
+
+PRE_SYSCALL(io_setup)(long nr_reqs, void **ctx) {
+  if (ctx) PRE_WRITE(ctx, sizeof(*ctx));
+}
+
+POST_SYSCALL(io_setup)(long res, long nr_reqs, void **ctx) {
+  if (res >= 0) {
+    if (ctx) POST_WRITE(ctx, sizeof(*ctx));
+    // (*ctx) is actually a pointer to a kernel mapped page, and there are
+    // people out there who are crazy enough to peek into that page's 32-byte
+    // header.
+    if (*ctx) POST_WRITE(*ctx, 32);
+  }
+}
+
+PRE_SYSCALL(io_destroy)(long ctx) {}
+
+POST_SYSCALL(io_destroy)(long res, long ctx) {}
+
+PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr,
+                          __sanitizer_io_event *ioevpp, void *timeout) {
+  if (timeout) PRE_READ(timeout, struct_timespec_sz);
+}
+
+POST_SYSCALL(io_getevents)(long res, long ctx_id, long min_nr, long nr,
+                           __sanitizer_io_event *ioevpp, void *timeout) {
+  if (res >= 0) {
+    if (ioevpp) POST_WRITE(ioevpp, res * sizeof(*ioevpp));
+    if (timeout) POST_WRITE(timeout, struct_timespec_sz);
+  }
+  for (long i = 0; i < res; i++) {
+    // We synchronize io_submit -> io_getevents/io_cancel using the
+    // user-provided data context. Data is not necessary a pointer, it can be
+    // an int, 0 or whatever; acquire/release will correctly handle this.
+    // This scheme can lead to false negatives, e.g. when all operations
+    // synchronize on 0. But there does not seem to be a better solution
+    // (except wrapping all operations in own context, which is unreliable).
+    // We can not reliably extract fildes in io_getevents.
+    COMMON_SYSCALL_ACQUIRE((void*)ioevpp[i].data);
+  }
+}
+
+PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) {
+  for (long i = 0; i < nr; ++i) {
+    uptr op = iocbpp[i]->aio_lio_opcode;
+    void *data = (void*)iocbpp[i]->aio_data;
+    void *buf = (void*)iocbpp[i]->aio_buf;
+    uptr len = (uptr)iocbpp[i]->aio_nbytes;
+    if (op == iocb_cmd_pwrite && buf && len) {
+      PRE_READ(buf, len);
+    } else if (op == iocb_cmd_pread && buf && len) {
+      POST_WRITE(buf, len);
+    } else if (op == iocb_cmd_pwritev) {
+      __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
+      for (uptr v = 0; v < len; v++)
+        PRE_READ(iovec[v].iov_base, iovec[v].iov_len);
+    } else if (op == iocb_cmd_preadv) {
+      __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
+      for (uptr v = 0; v < len; v++)
+        POST_WRITE(iovec[v].iov_base, iovec[v].iov_len);
+    }
+    // See comment in io_getevents.
+    COMMON_SYSCALL_RELEASE(data);
+  }
+}
+
+POST_SYSCALL(io_submit)(long res, long ctx_id, long nr,
+    __sanitizer_iocb **iocbpp) {}
+
+PRE_SYSCALL(io_cancel)(long ctx_id, __sanitizer_iocb *iocb,
+    __sanitizer_io_event *result) {
+}
+
+POST_SYSCALL(io_cancel)(long res, long ctx_id, __sanitizer_iocb *iocb,
+    __sanitizer_io_event *result) {
+  if (res == 0) {
+    if (result) {
+      // See comment in io_getevents.
+      COMMON_SYSCALL_ACQUIRE((void*)result->data);
+      POST_WRITE(result, sizeof(*result));
+    }
+    if (iocb)
+      POST_WRITE(iocb, sizeof(*iocb));
+  }
+}
+
+PRE_SYSCALL(sendfile)(long out_fd, long in_fd, void *offset, long count) {}
+
+POST_SYSCALL(sendfile)(long res, long out_fd, long in_fd,
+                       __sanitizer___kernel_off_t *offset, long count) {
+  if (res >= 0) {
+    if (offset) POST_WRITE(offset, sizeof(*offset));
+  }
+}
+
+PRE_SYSCALL(sendfile64)(long out_fd, long in_fd, void *offset, long count) {}
+
+POST_SYSCALL(sendfile64)(long res, long out_fd, long in_fd,
+                         __sanitizer___kernel_loff_t *offset, long count) {
+  if (res >= 0) {
+    if (offset) POST_WRITE(offset, sizeof(*offset));
+  }
+}
+
+PRE_SYSCALL(readlink)(const void *path, void *buf, long bufsiz) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(readlink)(long res, const void *path, void *buf, long bufsiz) {
+  if (res >= 0) {
+    if (buf)
+      POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1);
+  }
+}
+
+PRE_SYSCALL(creat)(const void *pathname, long mode) {
+  if (pathname)
+    PRE_READ(pathname,
+             __sanitizer::internal_strlen((const char *)pathname) + 1);
+}
+
+POST_SYSCALL(creat)(long res, const void *pathname, long mode) {}
+
+PRE_SYSCALL(open)(const void *filename, long flags, long mode) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(open)(long res, const void *filename, long flags, long mode) {}
+
+PRE_SYSCALL(close)(long fd) {
+  COMMON_SYSCALL_FD_CLOSE((int)fd);
+}
+
+POST_SYSCALL(close)(long res, long fd) {}
+
+PRE_SYSCALL(access)(const void *filename, long mode) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(access)(long res, const void *filename, long mode) {}
+
+PRE_SYSCALL(vhangup)() {}
+
+POST_SYSCALL(vhangup)(long res) {}
+
+PRE_SYSCALL(chown)(const void *filename, long user, long group) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(chown)(long res, const void *filename, long user, long group) {}
+
+PRE_SYSCALL(lchown)(const void *filename, long user, long group) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(lchown)(long res, const void *filename, long user, long group) {}
+
+PRE_SYSCALL(fchown)(long fd, long user, long group) {}
+
+POST_SYSCALL(fchown)(long res, long fd, long user, long group) {}
+
+PRE_SYSCALL(chown16)(const void *filename, long user, long group) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(chown16)(long res, const void *filename, long user, long group) {}
+
+PRE_SYSCALL(lchown16)(const void *filename, long user, long group) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(lchown16)(long res, const void *filename, long user, long group) {}
+
+PRE_SYSCALL(fchown16)(long fd, long user, long group) {}
+
+POST_SYSCALL(fchown16)(long res, long fd, long user, long group) {}
+
+PRE_SYSCALL(setregid16)(long rgid, long egid) {}
+
+POST_SYSCALL(setregid16)(long res, long rgid, long egid) {}
+
+PRE_SYSCALL(setgid16)(long gid) {}
+
+POST_SYSCALL(setgid16)(long res, long gid) {}
+
+PRE_SYSCALL(setreuid16)(long ruid, long euid) {}
+
+POST_SYSCALL(setreuid16)(long res, long ruid, long euid) {}
+
+PRE_SYSCALL(setuid16)(long uid) {}
+
+POST_SYSCALL(setuid16)(long res, long uid) {}
+
+PRE_SYSCALL(setresuid16)(long ruid, long euid, long suid) {}
+
+POST_SYSCALL(setresuid16)(long res, long ruid, long euid, long suid) {}
+
+PRE_SYSCALL(getresuid16)(void *ruid, void *euid, void *suid) {}
+
+POST_SYSCALL(getresuid16)(long res, __sanitizer___kernel_old_uid_t *ruid,
+                          __sanitizer___kernel_old_uid_t *euid,
+                          __sanitizer___kernel_old_uid_t *suid) {
+  if (res >= 0) {
+    if (ruid) POST_WRITE(ruid, sizeof(*ruid));
+    if (euid) POST_WRITE(euid, sizeof(*euid));
+    if (suid) POST_WRITE(suid, sizeof(*suid));
+  }
+}
+
+PRE_SYSCALL(setresgid16)(long rgid, long egid, long sgid) {}
+
+POST_SYSCALL(setresgid16)(long res, long rgid, long egid, long sgid) {}
+
+PRE_SYSCALL(getresgid16)(void *rgid, void *egid, void *sgid) {}
+
+POST_SYSCALL(getresgid16)(long res, __sanitizer___kernel_old_gid_t *rgid,
+                          __sanitizer___kernel_old_gid_t *egid,
+                          __sanitizer___kernel_old_gid_t *sgid) {
+  if (res >= 0) {
+    if (rgid) POST_WRITE(rgid, sizeof(*rgid));
+    if (egid) POST_WRITE(egid, sizeof(*egid));
+    if (sgid) POST_WRITE(sgid, sizeof(*sgid));
+  }
+}
+
+PRE_SYSCALL(setfsuid16)(long uid) {}
+
+POST_SYSCALL(setfsuid16)(long res, long uid) {}
+
+PRE_SYSCALL(setfsgid16)(long gid) {}
+
+POST_SYSCALL(setfsgid16)(long res, long gid) {}
+
+PRE_SYSCALL(getgroups16)(long gidsetsize,
+                         __sanitizer___kernel_old_gid_t *grouplist) {}
+
+POST_SYSCALL(getgroups16)(long res, long gidsetsize,
+                          __sanitizer___kernel_old_gid_t *grouplist) {
+  if (res >= 0) {
+    if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist));
+  }
+}
+
+PRE_SYSCALL(setgroups16)(long gidsetsize,
+                         __sanitizer___kernel_old_gid_t *grouplist) {
+  if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist));
+}
+
+POST_SYSCALL(setgroups16)(long res, long gidsetsize,
+                          __sanitizer___kernel_old_gid_t *grouplist) {}
+
+PRE_SYSCALL(getuid16)() {}
+
+POST_SYSCALL(getuid16)(long res) {}
+
+PRE_SYSCALL(geteuid16)() {}
+
+POST_SYSCALL(geteuid16)(long res) {}
+
+PRE_SYSCALL(getgid16)() {}
+
+POST_SYSCALL(getgid16)(long res) {}
+
+PRE_SYSCALL(getegid16)() {}
+
+POST_SYSCALL(getegid16)(long res) {}
+
+PRE_SYSCALL(utime)(void *filename, void *times) {}
+
+POST_SYSCALL(utime)(long res, void *filename, void *times) {
+  if (res >= 0) {
+    if (filename)
+      POST_WRITE(filename,
+                 __sanitizer::internal_strlen((const char *)filename) + 1);
+    if (times) POST_WRITE(times, struct_utimbuf_sz);
+  }
+}
+
+PRE_SYSCALL(utimes)(void *filename, void *utimes) {}
+
+POST_SYSCALL(utimes)(long res, void *filename, void *utimes) {
+  if (res >= 0) {
+    if (filename)
+      POST_WRITE(filename,
+                 __sanitizer::internal_strlen((const char *)filename) + 1);
+    if (utimes) POST_WRITE(utimes, timeval_sz);
+  }
+}
+
+PRE_SYSCALL(lseek)(long fd, long offset, long origin) {}
+
+POST_SYSCALL(lseek)(long res, long fd, long offset, long origin) {}
+
+PRE_SYSCALL(llseek)(long fd, long offset_high, long offset_low, void *result,
+                    long origin) {}
+
+POST_SYSCALL(llseek)(long res, long fd, long offset_high, long offset_low,
+                     void *result, long origin) {
+  if (res >= 0) {
+    if (result) POST_WRITE(result, sizeof(long long));
+  }
+}
+
+PRE_SYSCALL(readv)(long fd, const __sanitizer_iovec *vec, long vlen) {}
+
+POST_SYSCALL(readv)(long res, long fd, const __sanitizer_iovec *vec,
+                    long vlen) {
+  if (res >= 0) {
+    if (vec) kernel_write_iovec(vec, vlen, res);
+  }
+}
+
+PRE_SYSCALL(write)(long fd, const void *buf, long count) {
+  if (buf) PRE_READ(buf, count);
+}
+
+POST_SYSCALL(write)(long res, long fd, const void *buf, long count) {}
+
+PRE_SYSCALL(writev)(long fd, const __sanitizer_iovec *vec, long vlen) {}
+
+POST_SYSCALL(writev)(long res, long fd, const __sanitizer_iovec *vec,
+                     long vlen) {
+  if (res >= 0) {
+    if (vec) kernel_read_iovec(vec, vlen, res);
+  }
+}
+
+#ifdef _LP64
+PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos) {}
+
+POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, res);
+  }
+}
+
+PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos) {
+  if (buf) PRE_READ(buf, count);
+}
+
+POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count,
+                       long pos) {}
+#else
+PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos0, long pos1) {}
+
+POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos0,
+                      long pos1) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, res);
+  }
+}
+
+PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos0,
+                      long pos1) {
+  if (buf) PRE_READ(buf, count);
+}
+
+POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count,
+                       long pos0, long pos1) {}
+#endif
+
+PRE_SYSCALL(preadv)(long fd, const __sanitizer_iovec *vec, long vlen,
+                    long pos_l, long pos_h) {}
+
+POST_SYSCALL(preadv)(long res, long fd, const __sanitizer_iovec *vec, long vlen,
+                     long pos_l, long pos_h) {
+  if (res >= 0) {
+    if (vec) kernel_write_iovec(vec, vlen, res);
+  }
+}
+
+PRE_SYSCALL(pwritev)(long fd, const __sanitizer_iovec *vec, long vlen,
+                     long pos_l, long pos_h) {}
+
+POST_SYSCALL(pwritev)(long res, long fd, const __sanitizer_iovec *vec,
+                      long vlen, long pos_l, long pos_h) {
+  if (res >= 0) {
+    if (vec) kernel_read_iovec(vec, vlen, res);
+  }
+}
+
+PRE_SYSCALL(getcwd)(void *buf, long size) {}
+
+POST_SYSCALL(getcwd)(long res, void *buf, long size) {
+  if (res >= 0) {
+    if (buf)
+      POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1);
+  }
+}
+
+PRE_SYSCALL(mkdir)(const void *pathname, long mode) {
+  if (pathname)
+    PRE_READ(pathname,
+             __sanitizer::internal_strlen((const char *)pathname) + 1);
+}
+
+POST_SYSCALL(mkdir)(long res, const void *pathname, long mode) {}
+
+PRE_SYSCALL(chdir)(const void *filename) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(chdir)(long res, const void *filename) {}
+
+PRE_SYSCALL(fchdir)(long fd) {}
+
+POST_SYSCALL(fchdir)(long res, long fd) {}
+
+PRE_SYSCALL(rmdir)(const void *pathname) {
+  if (pathname)
+    PRE_READ(pathname,
+             __sanitizer::internal_strlen((const char *)pathname) + 1);
+}
+
+POST_SYSCALL(rmdir)(long res, const void *pathname) {}
+
+PRE_SYSCALL(lookup_dcookie)(u64 cookie64, void *buf, long len) {}
+
+POST_SYSCALL(lookup_dcookie)(long res, u64 cookie64, void *buf, long len) {
+  if (res >= 0) {
+    if (buf)
+      POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1);
+  }
+}
+
+PRE_SYSCALL(quotactl)(long cmd, const void *special, long id, void *addr) {
+  if (special)
+    PRE_READ(special, __sanitizer::internal_strlen((const char *)special) + 1);
+}
+
+POST_SYSCALL(quotactl)(long res, long cmd, const void *special, long id,
+                       void *addr) {}
+
+PRE_SYSCALL(getdents)(long fd, void *dirent, long count) {}
+
+POST_SYSCALL(getdents)(long res, long fd, void *dirent, long count) {
+  if (res >= 0) {
+    if (dirent) POST_WRITE(dirent, res);
+  }
+}
+
+PRE_SYSCALL(getdents64)(long fd, void *dirent, long count) {}
+
+POST_SYSCALL(getdents64)(long res, long fd, void *dirent, long count) {
+  if (res >= 0) {
+    if (dirent) POST_WRITE(dirent, res);
+  }
+}
+
+PRE_SYSCALL(setsockopt)(long fd, long level, long optname, void *optval,
+                        long optlen) {}
+
+POST_SYSCALL(setsockopt)(long res, long fd, long level, long optname,
+                         void *optval, long optlen) {
+  if (res >= 0) {
+    if (optval)
+      POST_WRITE(optval,
+                 __sanitizer::internal_strlen((const char *)optval) + 1);
+  }
+}
+
+PRE_SYSCALL(getsockopt)(long fd, long level, long optname, void *optval,
+                        void *optlen) {}
+
+POST_SYSCALL(getsockopt)(long res, long fd, long level, long optname,
+                         void *optval, void *optlen) {
+  if (res >= 0) {
+    if (optval)
+      POST_WRITE(optval,
+                 __sanitizer::internal_strlen((const char *)optval) + 1);
+    if (optlen) POST_WRITE(optlen, sizeof(int));
+  }
+}
+
+PRE_SYSCALL(bind)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {}
+
+POST_SYSCALL(bind)(long res, long arg0, sanitizer_kernel_sockaddr *arg1,
+                   long arg2) {
+  if (res >= 0) {
+    if (arg1) POST_WRITE(arg1, sizeof(*arg1));
+  }
+}
+
+PRE_SYSCALL(connect)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {}
+
+POST_SYSCALL(connect)(long res, long arg0, sanitizer_kernel_sockaddr *arg1,
+                      long arg2) {
+  if (res >= 0) {
+    if (arg1) POST_WRITE(arg1, sizeof(*arg1));
+  }
+}
+
+PRE_SYSCALL(accept)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2) {}
+
+POST_SYSCALL(accept)(long res, long arg0, sanitizer_kernel_sockaddr *arg1,
+                     void *arg2) {
+  if (res >= 0) {
+    if (arg1) POST_WRITE(arg1, sizeof(*arg1));
+    if (arg2) POST_WRITE(arg2, sizeof(unsigned));
+  }
+}
+
+PRE_SYSCALL(accept4)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2,
+                     long arg3) {}
+
+POST_SYSCALL(accept4)(long res, long arg0, sanitizer_kernel_sockaddr *arg1,
+                      void *arg2, long arg3) {
+  if (res >= 0) {
+    if (arg1) POST_WRITE(arg1, sizeof(*arg1));
+    if (arg2) POST_WRITE(arg2, sizeof(unsigned));
+  }
+}
+
+PRE_SYSCALL(getsockname)(long arg0, sanitizer_kernel_sockaddr *arg1,
+                         void *arg2) {}
+
+POST_SYSCALL(getsockname)(long res, long arg0, sanitizer_kernel_sockaddr *arg1,
+                          void *arg2) {
+  if (res >= 0) {
+    if (arg1) POST_WRITE(arg1, sizeof(*arg1));
+    if (arg2) POST_WRITE(arg2, sizeof(unsigned));
+  }
+}
+
+PRE_SYSCALL(getpeername)(long arg0, sanitizer_kernel_sockaddr *arg1,
+                         void *arg2) {}
+
+POST_SYSCALL(getpeername)(long res, long arg0, sanitizer_kernel_sockaddr *arg1,
+                          void *arg2) {
+  if (res >= 0) {
+    if (arg1) POST_WRITE(arg1, sizeof(*arg1));
+    if (arg2) POST_WRITE(arg2, sizeof(unsigned));
+  }
+}
+
+PRE_SYSCALL(send)(long arg0, void *arg1, long arg2, long arg3) {}
+
+POST_SYSCALL(send)(long res, long arg0, void *arg1, long arg2, long arg3) {
+  if (res) {
+    if (arg1) POST_READ(arg1, res);
+  }
+}
+
+PRE_SYSCALL(sendto)(long arg0, void *arg1, long arg2, long arg3,
+                    sanitizer_kernel_sockaddr *arg4, long arg5) {}
+
+POST_SYSCALL(sendto)(long res, long arg0, void *arg1, long arg2, long arg3,
+                     sanitizer_kernel_sockaddr *arg4, long arg5) {
+  if (res >= 0) {
+    if (arg1) POST_READ(arg1, res);
+    if (arg4) POST_WRITE(arg4, sizeof(*arg4));
+  }
+}
+
+PRE_SYSCALL(sendmsg)(long fd, void *msg, long flags) {}
+
+POST_SYSCALL(sendmsg)(long res, long fd, void *msg, long flags) {
+  // FIXME: POST_READ
+}
+
+PRE_SYSCALL(sendmmsg)(long fd, void *msg, long vlen, long flags) {}
+
+POST_SYSCALL(sendmmsg)(long res, long fd, void *msg, long vlen, long flags) {
+  // FIXME: POST_READ
+}
+
+PRE_SYSCALL(recv)(long arg0, void *buf, long len, long flags) {}
+
+POST_SYSCALL(recv)(long res, void *buf, long len, long flags) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, res);
+  }
+}
+
+PRE_SYSCALL(recvfrom)(long arg0, void *buf, long len, long flags,
+                      sanitizer_kernel_sockaddr *arg4, void *arg5) {}
+
+POST_SYSCALL(recvfrom)(long res, long arg0, void *buf, long len, long flags,
+                       sanitizer_kernel_sockaddr *arg4, void *arg5) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, res);
+    if (arg4) POST_WRITE(arg4, sizeof(*arg4));
+    if (arg5) POST_WRITE(arg5, sizeof(int));
+  }
+}
+
+PRE_SYSCALL(socket)(long arg0, long arg1, long arg2) {}
+
+POST_SYSCALL(socket)(long res, long arg0, long arg1, long arg2) {}
+
+PRE_SYSCALL(socketpair)(long arg0, long arg1, long arg2, void *arg3) {}
+
+POST_SYSCALL(socketpair)(long res, long arg0, long arg1, long arg2,
+                         void *arg3) {
+  if (res >= 0) {
+    if (arg3) POST_WRITE(arg3, sizeof(int));
+  }
+}
+
+PRE_SYSCALL(socketcall)(long call, void *args) {}
+
+POST_SYSCALL(socketcall)(long res, long call, void *args) {
+  if (res >= 0) {
+    if (args) POST_WRITE(args, sizeof(long));
+  }
+}
+
+PRE_SYSCALL(listen)(long arg0, long arg1) {}
+
+POST_SYSCALL(listen)(long res, long arg0, long arg1) {}
+
+PRE_SYSCALL(poll)(void *ufds, long nfds, long timeout) {}
+
+POST_SYSCALL(poll)(long res, __sanitizer_pollfd *ufds, long nfds,
+                   long timeout) {
+  if (res >= 0) {
+    if (ufds) POST_WRITE(ufds, nfds * sizeof(*ufds));
+  }
+}
+
+PRE_SYSCALL(select)(long n, __sanitizer___kernel_fd_set *inp,
+                    __sanitizer___kernel_fd_set *outp,
+                    __sanitizer___kernel_fd_set *exp, void *tvp) {}
+
+POST_SYSCALL(select)(long res, long n, __sanitizer___kernel_fd_set *inp,
+                     __sanitizer___kernel_fd_set *outp,
+                     __sanitizer___kernel_fd_set *exp, void *tvp) {
+  if (res >= 0) {
+    if (inp) POST_WRITE(inp, sizeof(*inp));
+    if (outp) POST_WRITE(outp, sizeof(*outp));
+    if (exp) POST_WRITE(exp, sizeof(*exp));
+    if (tvp) POST_WRITE(tvp, timeval_sz);
+  }
+}
+
+PRE_SYSCALL(old_select)(void *arg) {}
+
+POST_SYSCALL(old_select)(long res, void *arg) {}
+
+PRE_SYSCALL(epoll_create)(long size) {}
+
+POST_SYSCALL(epoll_create)(long res, long size) {}
+
+PRE_SYSCALL(epoll_create1)(long flags) {}
+
+POST_SYSCALL(epoll_create1)(long res, long flags) {}
+
+PRE_SYSCALL(epoll_ctl)(long epfd, long op, long fd, void *event) {}
+
+POST_SYSCALL(epoll_ctl)(long res, long epfd, long op, long fd, void *event) {
+  if (res >= 0) {
+    if (event) POST_WRITE(event, struct_epoll_event_sz);
+  }
+}
+
+PRE_SYSCALL(epoll_wait)(long epfd, void *events, long maxevents, long timeout) {
+}
+
+POST_SYSCALL(epoll_wait)(long res, long epfd, void *events, long maxevents,
+                         long timeout) {
+  if (res >= 0) {
+    if (events) POST_WRITE(events, struct_epoll_event_sz);
+  }
+}
+
+PRE_SYSCALL(epoll_pwait)(long epfd, void *events, long maxevents, long timeout,
+                         const kernel_sigset_t *sigmask, long sigsetsize) {
+  if (sigmask) PRE_READ(sigmask, sigsetsize);
+}
+
+POST_SYSCALL(epoll_pwait)(long res, long epfd, void *events, long maxevents,
+                          long timeout, const void *sigmask, long sigsetsize) {
+  if (res >= 0) {
+    if (events) POST_WRITE(events, struct_epoll_event_sz);
+  }
+}
+
+PRE_SYSCALL(gethostname)(void *name, long len) {}
+
+POST_SYSCALL(gethostname)(long res, void *name, long len) {
+  if (res >= 0) {
+    if (name)
+      POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  }
+}
+
+PRE_SYSCALL(sethostname)(void *name, long len) {}
+
+POST_SYSCALL(sethostname)(long res, void *name, long len) {
+  if (res >= 0) {
+    if (name)
+      POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  }
+}
+
+PRE_SYSCALL(setdomainname)(void *name, long len) {}
+
+POST_SYSCALL(setdomainname)(long res, void *name, long len) {
+  if (res >= 0) {
+    if (name)
+      POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1);
+  }
+}
+
+PRE_SYSCALL(newuname)(void *name) {}
+
+POST_SYSCALL(newuname)(long res, void *name) {
+  if (res >= 0) {
+    if (name) POST_WRITE(name, struct_new_utsname_sz);
+  }
+}
+
+PRE_SYSCALL(uname)(void *arg0) {}
+
+POST_SYSCALL(uname)(long res, void *arg0) {
+  if (res >= 0) {
+    if (arg0) POST_WRITE(arg0, struct_old_utsname_sz);
+  }
+}
+
+PRE_SYSCALL(olduname)(void *arg0) {}
+
+POST_SYSCALL(olduname)(long res, void *arg0) {
+  if (res >= 0) {
+    if (arg0) POST_WRITE(arg0, struct_oldold_utsname_sz);
+  }
+}
+
+PRE_SYSCALL(getrlimit)(long resource, void *rlim) {}
+
+POST_SYSCALL(getrlimit)(long res, long resource, void *rlim) {
+  if (res >= 0) {
+    if (rlim) POST_WRITE(rlim, struct_rlimit_sz);
+  }
+}
+
+PRE_SYSCALL(old_getrlimit)(long resource, void *rlim) {}
+
+POST_SYSCALL(old_getrlimit)(long res, long resource, void *rlim) {
+  if (res >= 0) {
+    if (rlim) POST_WRITE(rlim, struct_rlimit_sz);
+  }
+}
+
+PRE_SYSCALL(setrlimit)(long resource, void *rlim) {}
+
+POST_SYSCALL(setrlimit)(long res, long resource, void *rlim) {
+  if (res >= 0) {
+    if (rlim) POST_WRITE(rlim, struct_rlimit_sz);
+  }
+}
+
+#if !SANITIZER_ANDROID
+PRE_SYSCALL(prlimit64)(long pid, long resource, const void *new_rlim,
+                       void *old_rlim) {
+  if (new_rlim) PRE_READ(new_rlim, struct_rlimit64_sz);
+}
+
+POST_SYSCALL(prlimit64)(long res, long pid, long resource, const void *new_rlim,
+                        void *old_rlim) {
+  if (res >= 0) {
+    if (old_rlim) POST_WRITE(old_rlim, struct_rlimit64_sz);
+  }
+}
+#endif
+
+PRE_SYSCALL(getrusage)(long who, void *ru) {}
+
+POST_SYSCALL(getrusage)(long res, long who, void *ru) {
+  if (res >= 0) {
+    if (ru) POST_WRITE(ru, struct_rusage_sz);
+  }
+}
+
+PRE_SYSCALL(umask)(long mask) {}
+
+POST_SYSCALL(umask)(long res, long mask) {}
+
+PRE_SYSCALL(msgget)(long key, long msgflg) {}
+
+POST_SYSCALL(msgget)(long res, long key, long msgflg) {}
+
+PRE_SYSCALL(msgsnd)(long msqid, void *msgp, long msgsz, long msgflg) {
+  if (msgp) PRE_READ(msgp, msgsz);
+}
+
+POST_SYSCALL(msgsnd)(long res, long msqid, void *msgp, long msgsz,
+                     long msgflg) {}
+
+PRE_SYSCALL(msgrcv)(long msqid, void *msgp, long msgsz, long msgtyp,
+                    long msgflg) {}
+
+POST_SYSCALL(msgrcv)(long res, long msqid, void *msgp, long msgsz, long msgtyp,
+                     long msgflg) {
+  if (res >= 0) {
+    if (msgp) POST_WRITE(msgp, res);
+  }
+}
+
+#if !SANITIZER_ANDROID
+PRE_SYSCALL(msgctl)(long msqid, long cmd, void *buf) {}
+
+POST_SYSCALL(msgctl)(long res, long msqid, long cmd, void *buf) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, struct_msqid_ds_sz);
+  }
+}
+#endif
+
+PRE_SYSCALL(semget)(long key, long nsems, long semflg) {}
+
+POST_SYSCALL(semget)(long res, long key, long nsems, long semflg) {}
+
+PRE_SYSCALL(semop)(long semid, void *sops, long nsops) {}
+
+POST_SYSCALL(semop)(long res, long semid, void *sops, long nsops) {}
+
+PRE_SYSCALL(semctl)(long semid, long semnum, long cmd, void *arg) {}
+
+POST_SYSCALL(semctl)(long res, long semid, long semnum, long cmd, void *arg) {}
+
+PRE_SYSCALL(semtimedop)(long semid, void *sops, long nsops,
+                        const void *timeout) {
+  if (timeout) PRE_READ(timeout, struct_timespec_sz);
+}
+
+POST_SYSCALL(semtimedop)(long res, long semid, void *sops, long nsops,
+                         const void *timeout) {}
+
+PRE_SYSCALL(shmat)(long shmid, void *shmaddr, long shmflg) {}
+
+POST_SYSCALL(shmat)(long res, long shmid, void *shmaddr, long shmflg) {
+  if (res >= 0) {
+    if (shmaddr)
+      POST_WRITE(shmaddr,
+                 __sanitizer::internal_strlen((const char *)shmaddr) + 1);
+  }
+}
+
+PRE_SYSCALL(shmget)(long key, long size, long flag) {}
+
+POST_SYSCALL(shmget)(long res, long key, long size, long flag) {}
+
+PRE_SYSCALL(shmdt)(void *shmaddr) {}
+
+POST_SYSCALL(shmdt)(long res, void *shmaddr) {
+  if (res >= 0) {
+    if (shmaddr)
+      POST_WRITE(shmaddr,
+                 __sanitizer::internal_strlen((const char *)shmaddr) + 1);
+  }
+}
+
+PRE_SYSCALL(ipc)(long call, long first, long second, long third, void *ptr,
+                 long fifth) {}
+
+POST_SYSCALL(ipc)(long res, long call, long first, long second, long third,
+                  void *ptr, long fifth) {}
+
+#if !SANITIZER_ANDROID
+PRE_SYSCALL(shmctl)(long shmid, long cmd, void *buf) {}
+
+POST_SYSCALL(shmctl)(long res, long shmid, long cmd, void *buf) {
+  if (res >= 0) {
+    if (buf) POST_WRITE(buf, sizeof(__sanitizer_shmid_ds));
+  }
+}
+
+PRE_SYSCALL(mq_open)(const void *name, long oflag, long mode, void *attr) {
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(mq_open)(long res, const void *name, long oflag, long mode,
+                      void *attr) {
+  if (res >= 0) {
+    if (attr) POST_WRITE(attr, struct_mq_attr_sz);
+  }
+}
+
+PRE_SYSCALL(mq_unlink)(const void *name) {
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(mq_unlink)(long res, const void *name) {}
+
+PRE_SYSCALL(mq_timedsend)(long mqdes, const void *msg_ptr, long msg_len,
+                          long msg_prio, const void *abs_timeout) {
+  if (msg_ptr) PRE_READ(msg_ptr, msg_len);
+  if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz);
+}
+
+POST_SYSCALL(mq_timedsend)(long res, long mqdes, const void *msg_ptr,
+                           long msg_len, long msg_prio,
+                           const void *abs_timeout) {}
+
+PRE_SYSCALL(mq_timedreceive)(long mqdes, void *msg_ptr, long msg_len,
+                             void *msg_prio, const void *abs_timeout) {
+  if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz);
+}
+
+POST_SYSCALL(mq_timedreceive)(long res, long mqdes, void *msg_ptr, long msg_len,
+                              int *msg_prio, const void *abs_timeout) {
+  if (res >= 0) {
+    if (msg_ptr) POST_WRITE(msg_ptr, res);
+    if (msg_prio) POST_WRITE(msg_prio, sizeof(*msg_prio));
+  }
+}
+
+PRE_SYSCALL(mq_notify)(long mqdes, const void *notification) {
+  if (notification) PRE_READ(notification, struct_sigevent_sz);
+}
+
+POST_SYSCALL(mq_notify)(long res, long mqdes, const void *notification) {}
+
+PRE_SYSCALL(mq_getsetattr)(long mqdes, const void *mqstat, void *omqstat) {
+  if (mqstat) PRE_READ(mqstat, struct_mq_attr_sz);
+}
+
+POST_SYSCALL(mq_getsetattr)(long res, long mqdes, const void *mqstat,
+                            void *omqstat) {
+  if (res >= 0) {
+    if (omqstat) POST_WRITE(omqstat, struct_mq_attr_sz);
+  }
+}
+#endif  // SANITIZER_ANDROID
+
+PRE_SYSCALL(pciconfig_iobase)(long which, long bus, long devfn) {}
+
+POST_SYSCALL(pciconfig_iobase)(long res, long which, long bus, long devfn) {}
+
+PRE_SYSCALL(pciconfig_read)(long bus, long dfn, long off, long len, void *buf) {
+}
+
+POST_SYSCALL(pciconfig_read)(long res, long bus, long dfn, long off, long len,
+                             void *buf) {}
+
+PRE_SYSCALL(pciconfig_write)(long bus, long dfn, long off, long len,
+                             void *buf) {}
+
+POST_SYSCALL(pciconfig_write)(long res, long bus, long dfn, long off, long len,
+                              void *buf) {}
+
+PRE_SYSCALL(swapon)(const void *specialfile, long swap_flags) {
+  if (specialfile)
+    PRE_READ(specialfile,
+             __sanitizer::internal_strlen((const char *)specialfile) + 1);
+}
+
+POST_SYSCALL(swapon)(long res, const void *specialfile, long swap_flags) {}
+
+PRE_SYSCALL(swapoff)(const void *specialfile) {
+  if (specialfile)
+    PRE_READ(specialfile,
+             __sanitizer::internal_strlen((const char *)specialfile) + 1);
+}
+
+POST_SYSCALL(swapoff)(long res, const void *specialfile) {}
+
+PRE_SYSCALL(sysctl)(__sanitizer___sysctl_args *args) {
+  if (args) {
+    if (args->name) PRE_READ(args->name, args->nlen * sizeof(*args->name));
+    if (args->newval) PRE_READ(args->name, args->newlen);
+  }
+}
+
+POST_SYSCALL(sysctl)(long res, __sanitizer___sysctl_args *args) {
+  if (res >= 0) {
+    if (args && args->oldval && args->oldlenp) {
+      POST_WRITE(args->oldlenp, sizeof(*args->oldlenp));
+      POST_WRITE(args->oldval, *args->oldlenp);
+    }
+  }
+}
+
+PRE_SYSCALL(sysinfo)(void *info) {}
+
+POST_SYSCALL(sysinfo)(long res, void *info) {
+  if (res >= 0) {
+    if (info) POST_WRITE(info, struct_sysinfo_sz);
+  }
+}
+
+PRE_SYSCALL(sysfs)(long option, long arg1, long arg2) {}
+
+POST_SYSCALL(sysfs)(long res, long option, long arg1, long arg2) {}
+
+PRE_SYSCALL(syslog)(long type, void *buf, long len) {}
+
+POST_SYSCALL(syslog)(long res, long type, void *buf, long len) {
+  if (res >= 0) {
+    if (buf)
+      POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1);
+  }
+}
+
+PRE_SYSCALL(uselib)(const void *library) {
+  if (library)
+    PRE_READ(library, __sanitizer::internal_strlen((const char *)library) + 1);
+}
+
+POST_SYSCALL(uselib)(long res, const void *library) {}
+
+PRE_SYSCALL(ni_syscall)() {}
+
+POST_SYSCALL(ni_syscall)(long res) {}
+
+PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
+#if !SANITIZER_ANDROID && \
+    (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+     defined(__powerpc64__) || defined(__aarch64__))
+  if (data) {
+    if (request == ptrace_setregs) {
+      PRE_READ((void *)data, struct_user_regs_struct_sz);
+    } else if (request == ptrace_setfpregs) {
+      PRE_READ((void *)data, struct_user_fpregs_struct_sz);
+    } else if (request == ptrace_setfpxregs) {
+      PRE_READ((void *)data, struct_user_fpxregs_struct_sz);
+    } else if (request == ptrace_setsiginfo) {
+      PRE_READ((void *)data, siginfo_t_sz);
+    } else if (request == ptrace_setregset) {
+      __sanitizer_iovec *iov = (__sanitizer_iovec *)data;
+      PRE_READ(iov->iov_base, iov->iov_len);
+    }
+  }
+#endif
+}
+
+POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
+#if !SANITIZER_ANDROID && \
+    (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+     defined(__powerpc64__) || defined(__aarch64__))
+  if (res >= 0 && data) {
+    // Note that this is different from the interceptor in
+    // sanitizer_common_interceptors.inc.
+    // PEEK* requests return resulting values through data pointer.
+    if (request == ptrace_getregs) {
+      POST_WRITE((void *)data, struct_user_regs_struct_sz);
+    } else if (request == ptrace_getfpregs) {
+      POST_WRITE((void *)data, struct_user_fpregs_struct_sz);
+    } else if (request == ptrace_getfpxregs) {
+      POST_WRITE((void *)data, struct_user_fpxregs_struct_sz);
+    } else if (request == ptrace_getsiginfo) {
+      POST_WRITE((void *)data, siginfo_t_sz);
+    } else if (request == ptrace_getregset) {
+      __sanitizer_iovec *iov = (__sanitizer_iovec *)data;
+      POST_WRITE(iov->iov_base, iov->iov_len);
+    } else if (request == ptrace_peekdata || request == ptrace_peektext ||
+               request == ptrace_peekuser) {
+      POST_WRITE((void *)data, sizeof(void *));
+    }
+  }
+#endif
+}
+
+PRE_SYSCALL(add_key)(const void *_type, const void *_description,
+                     const void *_payload, long plen, long destringid) {
+  if (_type)
+    PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1);
+  if (_description)
+    PRE_READ(_description,
+             __sanitizer::internal_strlen((const char *)_description) + 1);
+}
+
+POST_SYSCALL(add_key)(long res, const void *_type, const void *_description,
+                      const void *_payload, long plen, long destringid) {}
+
+PRE_SYSCALL(request_key)(const void *_type, const void *_description,
+                         const void *_callout_info, long destringid) {
+  if (_type)
+    PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1);
+  if (_description)
+    PRE_READ(_description,
+             __sanitizer::internal_strlen((const char *)_description) + 1);
+  if (_callout_info)
+    PRE_READ(_callout_info,
+             __sanitizer::internal_strlen((const char *)_callout_info) + 1);
+}
+
+POST_SYSCALL(request_key)(long res, const void *_type, const void *_description,
+                          const void *_callout_info, long destringid) {}
+
+PRE_SYSCALL(keyctl)(long cmd, long arg2, long arg3, long arg4, long arg5) {}
+
+POST_SYSCALL(keyctl)(long res, long cmd, long arg2, long arg3, long arg4,
+                     long arg5) {}
+
+PRE_SYSCALL(ioprio_set)(long which, long who, long ioprio) {}
+
+POST_SYSCALL(ioprio_set)(long res, long which, long who, long ioprio) {}
+
+PRE_SYSCALL(ioprio_get)(long which, long who) {}
+
+POST_SYSCALL(ioprio_get)(long res, long which, long who) {}
+
+PRE_SYSCALL(set_mempolicy)(long mode, void *nmask, long maxnode) {}
+
+POST_SYSCALL(set_mempolicy)(long res, long mode, void *nmask, long maxnode) {
+  if (res >= 0) {
+    if (nmask) POST_WRITE(nmask, sizeof(long));
+  }
+}
+
+PRE_SYSCALL(migrate_pages)(long pid, long maxnode, const void *from,
+                           const void *to) {
+  if (from) PRE_READ(from, sizeof(long));
+  if (to) PRE_READ(to, sizeof(long));
+}
+
+POST_SYSCALL(migrate_pages)(long res, long pid, long maxnode, const void *from,
+                            const void *to) {}
+
+PRE_SYSCALL(move_pages)(long pid, long nr_pages, const void **pages,
+                        const int *nodes, int *status, long flags) {
+  if (pages) PRE_READ(pages, nr_pages * sizeof(*pages));
+  if (nodes) PRE_READ(nodes, nr_pages * sizeof(*nodes));
+}
+
+POST_SYSCALL(move_pages)(long res, long pid, long nr_pages, const void **pages,
+                         const int *nodes, int *status, long flags) {
+  if (res >= 0) {
+    if (status) POST_WRITE(status, nr_pages * sizeof(*status));
+  }
+}
+
+PRE_SYSCALL(mbind)(long start, long len, long mode, void *nmask, long maxnode,
+                   long flags) {}
+
+POST_SYSCALL(mbind)(long res, long start, long len, long mode, void *nmask,
+                    long maxnode, long flags) {
+  if (res >= 0) {
+    if (nmask) POST_WRITE(nmask, sizeof(long));
+  }
+}
+
+PRE_SYSCALL(get_mempolicy)(void *policy, void *nmask, long maxnode, long addr,
+                           long flags) {}
+
+POST_SYSCALL(get_mempolicy)(long res, void *policy, void *nmask, long maxnode,
+                            long addr, long flags) {
+  if (res >= 0) {
+    if (policy) POST_WRITE(policy, sizeof(int));
+    if (nmask) POST_WRITE(nmask, sizeof(long));
+  }
+}
+
+PRE_SYSCALL(inotify_init)() {}
+
+POST_SYSCALL(inotify_init)(long res) {}
+
+PRE_SYSCALL(inotify_init1)(long flags) {}
+
+POST_SYSCALL(inotify_init1)(long res, long flags) {}
+
+PRE_SYSCALL(inotify_add_watch)(long fd, const void *path, long mask) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(inotify_add_watch)(long res, long fd, const void *path,
+                                long mask) {}
+
+PRE_SYSCALL(inotify_rm_watch)(long fd, long wd) {}
+
+POST_SYSCALL(inotify_rm_watch)(long res, long fd, long wd) {}
+
+PRE_SYSCALL(spu_run)(long fd, void *unpc, void *ustatus) {}
+
+POST_SYSCALL(spu_run)(long res, long fd, unsigned *unpc, unsigned *ustatus) {
+  if (res >= 0) {
+    if (unpc) POST_WRITE(unpc, sizeof(*unpc));
+    if (ustatus) POST_WRITE(ustatus, sizeof(*ustatus));
+  }
+}
+
+PRE_SYSCALL(spu_create)(const void *name, long flags, long mode, long fd) {
+  if (name)
+    PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1);
+}
+
+POST_SYSCALL(spu_create)(long res, const void *name, long flags, long mode,
+                         long fd) {}
+
+PRE_SYSCALL(mknodat)(long dfd, const void *filename, long mode, long dev) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(mknodat)(long res, long dfd, const void *filename, long mode,
+                      long dev) {}
+
+PRE_SYSCALL(mkdirat)(long dfd, const void *pathname, long mode) {
+  if (pathname)
+    PRE_READ(pathname,
+             __sanitizer::internal_strlen((const char *)pathname) + 1);
+}
+
+POST_SYSCALL(mkdirat)(long res, long dfd, const void *pathname, long mode) {}
+
+PRE_SYSCALL(unlinkat)(long dfd, const void *pathname, long flag) {
+  if (pathname)
+    PRE_READ(pathname,
+             __sanitizer::internal_strlen((const char *)pathname) + 1);
+}
+
+POST_SYSCALL(unlinkat)(long res, long dfd, const void *pathname, long flag) {}
+
+PRE_SYSCALL(symlinkat)(const void *oldname, long newdfd, const void *newname) {
+  if (oldname)
+    PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1);
+  if (newname)
+    PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1);
+}
+
+POST_SYSCALL(symlinkat)(long res, const void *oldname, long newdfd,
+                        const void *newname) {}
+
+PRE_SYSCALL(linkat)(long olddfd, const void *oldname, long newdfd,
+                    const void *newname, long flags) {
+  if (oldname)
+    PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1);
+  if (newname)
+    PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1);
+}
+
+POST_SYSCALL(linkat)(long res, long olddfd, const void *oldname, long newdfd,
+                     const void *newname, long flags) {}
+
+PRE_SYSCALL(renameat)(long olddfd, const void *oldname, long newdfd,
+                      const void *newname) {
+  if (oldname)
+    PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1);
+  if (newname)
+    PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1);
+}
+
+POST_SYSCALL(renameat)(long res, long olddfd, const void *oldname, long newdfd,
+                       const void *newname) {}
+
+PRE_SYSCALL(futimesat)(long dfd, const void *filename, void *utimes) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(futimesat)(long res, long dfd, const void *filename,
+                        void *utimes) {
+  if (res >= 0) {
+    if (utimes) POST_WRITE(utimes, timeval_sz);
+  }
+}
+
+PRE_SYSCALL(faccessat)(long dfd, const void *filename, long mode) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(faccessat)(long res, long dfd, const void *filename, long mode) {}
+
+PRE_SYSCALL(fchmodat)(long dfd, const void *filename, long mode) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(fchmodat)(long res, long dfd, const void *filename, long mode) {}
+
+PRE_SYSCALL(fchownat)(long dfd, const void *filename, long user, long group,
+                      long flag) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(fchownat)(long res, long dfd, const void *filename, long user,
+                       long group, long flag) {}
+
+PRE_SYSCALL(openat)(long dfd, const void *filename, long flags, long mode) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(openat)(long res, long dfd, const void *filename, long flags,
+                     long mode) {}
+
+PRE_SYSCALL(newfstatat)(long dfd, const void *filename, void *statbuf,
+                        long flag) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(newfstatat)(long res, long dfd, const void *filename,
+                         void *statbuf, long flag) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz);
+  }
+}
+
+PRE_SYSCALL(fstatat64)(long dfd, const void *filename, void *statbuf,
+                       long flag) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(fstatat64)(long res, long dfd, const void *filename, void *statbuf,
+                        long flag) {
+  if (res >= 0) {
+    if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz);
+  }
+}
+
+PRE_SYSCALL(readlinkat)(long dfd, const void *path, void *buf, long bufsiz) {
+  if (path)
+    PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
+}
+
+POST_SYSCALL(readlinkat)(long res, long dfd, const void *path, void *buf,
+                         long bufsiz) {
+  if (res >= 0) {
+    if (buf)
+      POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1);
+  }
+}
+
+PRE_SYSCALL(utimensat)(long dfd, const void *filename, void *utimes,
+                       long flags) {
+  if (filename)
+    PRE_READ(filename,
+             __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(utimensat)(long res, long dfd, const void *filename, void *utimes,
+                        long flags) {
+  if (res >= 0) {
+    if (utimes) POST_WRITE(utimes, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(unshare)(long unshare_flags) {}
+
+POST_SYSCALL(unshare)(long res, long unshare_flags) {}
+
+PRE_SYSCALL(splice)(long fd_in, void *off_in, long fd_out, void *off_out,
+                    long len, long flags) {}
+
+POST_SYSCALL(splice)(long res, long fd_in, void *off_in, long fd_out,
+                     void *off_out, long len, long flags) {
+  if (res >= 0) {
+    if (off_in) POST_WRITE(off_in, sizeof(long long));
+    if (off_out) POST_WRITE(off_out, sizeof(long long));
+  }
+}
+
+PRE_SYSCALL(vmsplice)(long fd, const __sanitizer_iovec *iov, long nr_segs,
+                      long flags) {}
+
+POST_SYSCALL(vmsplice)(long res, long fd, const __sanitizer_iovec *iov,
+                       long nr_segs, long flags) {
+  if (res >= 0) {
+    if (iov) kernel_read_iovec(iov, nr_segs, res);
+  }
+}
+
+PRE_SYSCALL(tee)(long fdin, long fdout, long len, long flags) {}
+
+POST_SYSCALL(tee)(long res, long fdin, long fdout, long len, long flags) {}
+
+PRE_SYSCALL(get_robust_list)(long pid, void *head_ptr, void *len_ptr) {}
+
+POST_SYSCALL(get_robust_list)(long res, long pid, void *head_ptr,
+                              void *len_ptr) {}
+
+PRE_SYSCALL(set_robust_list)(void *head, long len) {}
+
+POST_SYSCALL(set_robust_list)(long res, void *head, long len) {}
+
+PRE_SYSCALL(getcpu)(void *cpu, void *node, void *cache) {}
+
+POST_SYSCALL(getcpu)(long res, void *cpu, void *node, void *cache) {
+  if (res >= 0) {
+    if (cpu) POST_WRITE(cpu, sizeof(unsigned));
+    if (node) POST_WRITE(node, sizeof(unsigned));
+    // The third argument to this system call is nowadays unused.
+  }
+}
+
+PRE_SYSCALL(signalfd)(long ufd, void *user_mask, long sizemask) {}
+
+POST_SYSCALL(signalfd)(long res, long ufd, kernel_sigset_t *user_mask,
+                       long sizemask) {
+  if (res >= 0) {
+    if (user_mask) POST_WRITE(user_mask, sizemask);
+  }
+}
+
+PRE_SYSCALL(signalfd4)(long ufd, void *user_mask, long sizemask, long flags) {}
+
+POST_SYSCALL(signalfd4)(long res, long ufd, kernel_sigset_t *user_mask,
+                        long sizemask, long flags) {
+  if (res >= 0) {
+    if (user_mask) POST_WRITE(user_mask, sizemask);
+  }
+}
+
+PRE_SYSCALL(timerfd_create)(long clockid, long flags) {}
+
+POST_SYSCALL(timerfd_create)(long res, long clockid, long flags) {}
+
+PRE_SYSCALL(timerfd_settime)(long ufd, long flags, const void *utmr,
+                             void *otmr) {
+  if (utmr) PRE_READ(utmr, struct_itimerspec_sz);
+}
+
+POST_SYSCALL(timerfd_settime)(long res, long ufd, long flags, const void *utmr,
+                              void *otmr) {
+  if (res >= 0) {
+    if (otmr) POST_WRITE(otmr, struct_itimerspec_sz);
+  }
+}
+
+PRE_SYSCALL(timerfd_gettime)(long ufd, void *otmr) {}
+
+POST_SYSCALL(timerfd_gettime)(long res, long ufd, void *otmr) {
+  if (res >= 0) {
+    if (otmr) POST_WRITE(otmr, struct_itimerspec_sz);
+  }
+}
+
+PRE_SYSCALL(eventfd)(long count) {}
+
+POST_SYSCALL(eventfd)(long res, long count) {}
+
+PRE_SYSCALL(eventfd2)(long count, long flags) {}
+
+POST_SYSCALL(eventfd2)(long res, long count, long flags) {}
+
+PRE_SYSCALL(old_readdir)(long arg0, void *arg1, long arg2) {}
+
+POST_SYSCALL(old_readdir)(long res, long arg0, void *arg1, long arg2) {
+  // Missing definition of 'struct old_linux_dirent'.
+}
+
+PRE_SYSCALL(pselect6)(long arg0, __sanitizer___kernel_fd_set *arg1,
+                      __sanitizer___kernel_fd_set *arg2,
+                      __sanitizer___kernel_fd_set *arg3, void *arg4,
+                      void *arg5) {}
+
+POST_SYSCALL(pselect6)(long res, long arg0, __sanitizer___kernel_fd_set *arg1,
+                       __sanitizer___kernel_fd_set *arg2,
+                       __sanitizer___kernel_fd_set *arg3, void *arg4,
+                       void *arg5) {
+  if (res >= 0) {
+    if (arg1) POST_WRITE(arg1, sizeof(*arg1));
+    if (arg2) POST_WRITE(arg2, sizeof(*arg2));
+    if (arg3) POST_WRITE(arg3, sizeof(*arg3));
+    if (arg4) POST_WRITE(arg4, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(ppoll)(__sanitizer_pollfd *arg0, long arg1, void *arg2,
+                   const kernel_sigset_t *arg3, long arg4) {
+  if (arg3) PRE_READ(arg3, arg4);
+}
+
+POST_SYSCALL(ppoll)(long res, __sanitizer_pollfd *arg0, long arg1, void *arg2,
+                    const void *arg3, long arg4) {
+  if (res >= 0) {
+    if (arg0) POST_WRITE(arg0, sizeof(*arg0));
+    if (arg2) POST_WRITE(arg2, struct_timespec_sz);
+  }
+}
+
+PRE_SYSCALL(syncfs)(long fd) {}
+
+POST_SYSCALL(syncfs)(long res, long fd) {}
+
+PRE_SYSCALL(perf_event_open)(__sanitizer_perf_event_attr *attr_uptr, long pid,
+                             long cpu, long group_fd, long flags) {
+  if (attr_uptr) PRE_READ(attr_uptr, attr_uptr->size);
+}
+
+POST_SYSCALL(perf_event_open)(long res, __sanitizer_perf_event_attr *attr_uptr,
+                              long pid, long cpu, long group_fd, long flags) {}
+
+PRE_SYSCALL(mmap_pgoff)(long addr, long len, long prot, long flags, long fd,
+                        long pgoff) {}
+
+POST_SYSCALL(mmap_pgoff)(long res, long addr, long len, long prot, long flags,
+                         long fd, long pgoff) {}
+
+PRE_SYSCALL(old_mmap)(void *arg) {}
+
+POST_SYSCALL(old_mmap)(long res, void *arg) {}
+
+PRE_SYSCALL(name_to_handle_at)(long dfd, const void *name, void *handle,
+                               void *mnt_id, long flag) {}
+
+POST_SYSCALL(name_to_handle_at)(long res, long dfd, const void *name,
+                                void *handle, void *mnt_id, long flag) {}
+
+PRE_SYSCALL(open_by_handle_at)(long mountdirfd, void *handle, long flags) {}
+
+POST_SYSCALL(open_by_handle_at)(long res, long mountdirfd, void *handle,
+                                long flags) {}
+
+PRE_SYSCALL(setns)(long fd, long nstype) {}
+
+POST_SYSCALL(setns)(long res, long fd, long nstype) {}
+
+PRE_SYSCALL(process_vm_readv)(long pid, const __sanitizer_iovec *lvec,
+                              long liovcnt, const void *rvec, long riovcnt,
+                              long flags) {}
+
+POST_SYSCALL(process_vm_readv)(long res, long pid,
+                               const __sanitizer_iovec *lvec, long liovcnt,
+                               const void *rvec, long riovcnt, long flags) {
+  if (res >= 0) {
+    if (lvec) kernel_write_iovec(lvec, liovcnt, res);
+  }
+}
+
+PRE_SYSCALL(process_vm_writev)(long pid, const __sanitizer_iovec *lvec,
+                               long liovcnt, const void *rvec, long riovcnt,
+                               long flags) {}
+
+POST_SYSCALL(process_vm_writev)(long res, long pid,
+                                const __sanitizer_iovec *lvec, long liovcnt,
+                                const void *rvec, long riovcnt, long flags) {
+  if (res >= 0) {
+    if (lvec) kernel_read_iovec(lvec, liovcnt, res);
+  }
+}
+
+PRE_SYSCALL(fork)() {
+  COMMON_SYSCALL_PRE_FORK();
+}
+
+POST_SYSCALL(fork)(long res) {
+  COMMON_SYSCALL_POST_FORK(res);
+}
+
+PRE_SYSCALL(vfork)() {
+  COMMON_SYSCALL_PRE_FORK();
+}
+
+POST_SYSCALL(vfork)(long res) {
+  COMMON_SYSCALL_POST_FORK(res);
+}
+}  // extern "C"
+
+#undef PRE_SYSCALL
+#undef PRE_READ
+#undef PRE_WRITE
+#undef POST_SYSCALL
+#undef POST_READ
+#undef POST_WRITE
+
+#endif  // SANITIZER_LINUX
diff --git a/lsan/include/sanitizer_common/sanitizer_deadlock_detector.h b/lsan/include/sanitizer_common/sanitizer_deadlock_detector.h
new file mode 100644 (file)
index 0000000..949c486
--- /dev/null
@@ -0,0 +1,410 @@
+//===-- sanitizer_deadlock_detector.h ---------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime.
+// The deadlock detector maintains a directed graph of lock acquisitions.
+// When a lock event happens, the detector checks if the locks already held by
+// the current thread are reachable from the newly acquired lock.
+//
+// The detector can handle only a fixed amount of simultaneously live locks
+// (a lock is alive if it has been locked at least once and has not been
+// destroyed). When the maximal number of locks is reached the entire graph
+// is flushed and the new lock epoch is started. The node ids from the old
+// epochs can not be used with any of the detector methods except for
+// nodeBelongsToCurrentEpoch().
+//
+// FIXME: this is work in progress, nothing really works yet.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_DEADLOCK_DETECTOR_H
+#define SANITIZER_DEADLOCK_DETECTOR_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_bvgraph.h"
+
+namespace __sanitizer {
+
+// Thread-local state for DeadlockDetector.
+// It contains the locks currently held by the owning thread.
+template <class BV>
+class DeadlockDetectorTLS {
+ public:
+  // No CTOR.
+  void clear() {
+    bv_.clear();
+    epoch_ = 0;
+    n_recursive_locks = 0;
+    n_all_locks_ = 0;
+  }
+
+  bool empty() const { return bv_.empty(); }
+
+  void ensureCurrentEpoch(uptr current_epoch) {
+    if (epoch_ == current_epoch) return;
+    bv_.clear();
+    epoch_ = current_epoch;
+  }
+
+  uptr getEpoch() const { return epoch_; }
+
+  // Returns true if this is the first (non-recursive) acquisition of this lock.
+  bool addLock(uptr lock_id, uptr current_epoch, u32 stk) {
+    // Printf("addLock: %zx %zx stk %u\n", lock_id, current_epoch, stk);
+    CHECK_EQ(epoch_, current_epoch);
+    if (!bv_.setBit(lock_id)) {
+      // The lock is already held by this thread, it must be recursive.
+      CHECK_LT(n_recursive_locks, ARRAY_SIZE(recursive_locks));
+      recursive_locks[n_recursive_locks++] = lock_id;
+      return false;
+    }
+    CHECK_LT(n_all_locks_, ARRAY_SIZE(all_locks_with_contexts_));
+    // lock_id < BV::kSize, can cast to a smaller int.
+    u32 lock_id_short = static_cast<u32>(lock_id);
+    LockWithContext l = {lock_id_short, stk};
+    all_locks_with_contexts_[n_all_locks_++] = l;
+    return true;
+  }
+
+  void removeLock(uptr lock_id) {
+    if (n_recursive_locks) {
+      for (sptr i = n_recursive_locks - 1; i >= 0; i--) {
+        if (recursive_locks[i] == lock_id) {
+          n_recursive_locks--;
+          Swap(recursive_locks[i], recursive_locks[n_recursive_locks]);
+          return;
+        }
+      }
+    }
+    // Printf("remLock: %zx %zx\n", lock_id, epoch_);
+    CHECK(bv_.clearBit(lock_id));
+    if (n_all_locks_) {
+      for (sptr i = n_all_locks_ - 1; i >= 0; i--) {
+        if (all_locks_with_contexts_[i].lock == static_cast<u32>(lock_id)) {
+          Swap(all_locks_with_contexts_[i],
+               all_locks_with_contexts_[n_all_locks_ - 1]);
+          n_all_locks_--;
+          break;
+        }
+      }
+    }
+  }
+
+  u32 findLockContext(uptr lock_id) {
+    for (uptr i = 0; i < n_all_locks_; i++)
+      if (all_locks_with_contexts_[i].lock == static_cast<u32>(lock_id))
+        return all_locks_with_contexts_[i].stk;
+    return 0;
+  }
+
+  const BV &getLocks(uptr current_epoch) const {
+    CHECK_EQ(epoch_, current_epoch);
+    return bv_;
+  }
+
+  uptr getNumLocks() const { return n_all_locks_; }
+  uptr getLock(uptr idx) const { return all_locks_with_contexts_[idx].lock; }
+
+ private:
+  BV bv_;
+  uptr epoch_;
+  uptr recursive_locks[64];
+  uptr n_recursive_locks;
+  struct LockWithContext {
+    u32 lock;
+    u32 stk;
+  };
+  LockWithContext all_locks_with_contexts_[64];
+  uptr n_all_locks_;
+};
+
+// DeadlockDetector.
+// For deadlock detection to work we need one global DeadlockDetector object
+// and one DeadlockDetectorTLS object per evey thread.
+// This class is not thread safe, all concurrent accesses should be guarded
+// by an external lock.
+// Most of the methods of this class are not thread-safe (i.e. should
+// be protected by an external lock) unless explicitly told otherwise.
+template <class BV>
+class DeadlockDetector {
+ public:
+  typedef BV BitVector;
+
+  uptr size() const { return g_.size(); }
+
+  // No CTOR.
+  void clear() {
+    current_epoch_ = 0;
+    available_nodes_.clear();
+    recycled_nodes_.clear();
+    g_.clear();
+    n_edges_ = 0;
+  }
+
+  // Allocate new deadlock detector node.
+  // If we are out of available nodes first try to recycle some.
+  // If there is nothing to recycle, flush the graph and increment the epoch.
+  // Associate 'data' (opaque user's object) with the new node.
+  uptr newNode(uptr data) {
+    if (!available_nodes_.empty())
+      return getAvailableNode(data);
+    if (!recycled_nodes_.empty()) {
+      // Printf("recycling: n_edges_ %zd\n", n_edges_);
+      for (sptr i = n_edges_ - 1; i >= 0; i--) {
+        if (recycled_nodes_.getBit(edges_[i].from) ||
+            recycled_nodes_.getBit(edges_[i].to)) {
+          Swap(edges_[i], edges_[n_edges_ - 1]);
+          n_edges_--;
+        }
+      }
+      CHECK(available_nodes_.empty());
+      // removeEdgesFrom was called in removeNode.
+      g_.removeEdgesTo(recycled_nodes_);
+      available_nodes_.setUnion(recycled_nodes_);
+      recycled_nodes_.clear();
+      return getAvailableNode(data);
+    }
+    // We are out of vacant nodes. Flush and increment the current_epoch_.
+    current_epoch_ += size();
+    recycled_nodes_.clear();
+    available_nodes_.setAll();
+    g_.clear();
+    return getAvailableNode(data);
+  }
+
+  // Get data associated with the node created by newNode().
+  uptr getData(uptr node) const { return data_[nodeToIndex(node)]; }
+
+  bool nodeBelongsToCurrentEpoch(uptr node) {
+    return node && (node / size() * size()) == current_epoch_;
+  }
+
+  void removeNode(uptr node) {
+    uptr idx = nodeToIndex(node);
+    CHECK(!available_nodes_.getBit(idx));
+    CHECK(recycled_nodes_.setBit(idx));
+    g_.removeEdgesFrom(idx);
+  }
+
+  void ensureCurrentEpoch(DeadlockDetectorTLS<BV> *dtls) {
+    dtls->ensureCurrentEpoch(current_epoch_);
+  }
+
+  // Returns true if there is a cycle in the graph after this lock event.
+  // Ideally should be called before the lock is acquired so that we can
+  // report a deadlock before a real deadlock happens.
+  bool onLockBefore(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
+    ensureCurrentEpoch(dtls);
+    uptr cur_idx = nodeToIndex(cur_node);
+    return g_.isReachable(cur_idx, dtls->getLocks(current_epoch_));
+  }
+
+  u32 findLockContext(DeadlockDetectorTLS<BV> *dtls, uptr node) {
+    return dtls->findLockContext(nodeToIndex(node));
+  }
+
+  // Add cur_node to the set of locks held currently by dtls.
+  void onLockAfter(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
+    ensureCurrentEpoch(dtls);
+    uptr cur_idx = nodeToIndex(cur_node);
+    dtls->addLock(cur_idx, current_epoch_, stk);
+  }
+
+  // Experimental *racy* fast path function.
+  // Returns true if all edges from the currently held locks to cur_node exist.
+  bool hasAllEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
+    uptr local_epoch = dtls->getEpoch();
+    // Read from current_epoch_ is racy.
+    if (cur_node && local_epoch == current_epoch_ &&
+        local_epoch == nodeToEpoch(cur_node)) {
+      uptr cur_idx = nodeToIndexUnchecked(cur_node);
+      for (uptr i = 0, n = dtls->getNumLocks(); i < n; i++) {
+        if (!g_.hasEdge(dtls->getLock(i), cur_idx))
+          return false;
+      }
+      return true;
+    }
+    return false;
+  }
+
+  // Adds edges from currently held locks to cur_node,
+  // returns the number of added edges, and puts the sources of added edges
+  // into added_edges[].
+  // Should be called before onLockAfter.
+  uptr addEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk,
+                int unique_tid) {
+    ensureCurrentEpoch(dtls);
+    uptr cur_idx = nodeToIndex(cur_node);
+    uptr added_edges[40];
+    uptr n_added_edges = g_.addEdges(dtls->getLocks(current_epoch_), cur_idx,
+                                     added_edges, ARRAY_SIZE(added_edges));
+    for (uptr i = 0; i < n_added_edges; i++) {
+      if (n_edges_ < ARRAY_SIZE(edges_)) {
+        Edge e = {(u16)added_edges[i], (u16)cur_idx,
+                  dtls->findLockContext(added_edges[i]), stk,
+                  unique_tid};
+        edges_[n_edges_++] = e;
+      }
+      // Printf("Edge%zd: %u %zd=>%zd in T%d\n",
+      //        n_edges_, stk, added_edges[i], cur_idx, unique_tid);
+    }
+    return n_added_edges;
+  }
+
+  bool findEdge(uptr from_node, uptr to_node, u32 *stk_from, u32 *stk_to,
+                int *unique_tid) {
+    uptr from_idx = nodeToIndex(from_node);
+    uptr to_idx = nodeToIndex(to_node);
+    for (uptr i = 0; i < n_edges_; i++) {
+      if (edges_[i].from == from_idx && edges_[i].to == to_idx) {
+        *stk_from = edges_[i].stk_from;
+        *stk_to = edges_[i].stk_to;
+        *unique_tid = edges_[i].unique_tid;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // Test-only function. Handles the before/after lock events,
+  // returns true if there is a cycle.
+  bool onLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
+    ensureCurrentEpoch(dtls);
+    bool is_reachable = !isHeld(dtls, cur_node) && onLockBefore(dtls, cur_node);
+    addEdges(dtls, cur_node, stk, 0);
+    onLockAfter(dtls, cur_node, stk);
+    return is_reachable;
+  }
+
+  // Handles the try_lock event, returns false.
+  // When a try_lock event happens (i.e. a try_lock call succeeds) we need
+  // to add this lock to the currently held locks, but we should not try to
+  // change the lock graph or to detect a cycle.  We may want to investigate
+  // whether a more aggressive strategy is possible for try_lock.
+  bool onTryLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
+    ensureCurrentEpoch(dtls);
+    uptr cur_idx = nodeToIndex(cur_node);
+    dtls->addLock(cur_idx, current_epoch_, stk);
+    return false;
+  }
+
+  // Returns true iff dtls is empty (no locks are currently held) and we can
+  // add the node to the currently held locks w/o chanding the global state.
+  // This operation is thread-safe as it only touches the dtls.
+  bool onFirstLock(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
+    if (!dtls->empty()) return false;
+    if (dtls->getEpoch() && dtls->getEpoch() == nodeToEpoch(node)) {
+      dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
+      return true;
+    }
+    return false;
+  }
+
+  // Finds a path between the lock 'cur_node' (currently not held in dtls)
+  // and some currently held lock, returns the length of the path
+  // or 0 on failure.
+  uptr findPathToLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, uptr *path,
+                      uptr path_size) {
+    tmp_bv_.copyFrom(dtls->getLocks(current_epoch_));
+    uptr idx = nodeToIndex(cur_node);
+    CHECK(!tmp_bv_.getBit(idx));
+    uptr res = g_.findShortestPath(idx, tmp_bv_, path, path_size);
+    for (uptr i = 0; i < res; i++)
+      path[i] = indexToNode(path[i]);
+    if (res)
+      CHECK_EQ(path[0], cur_node);
+    return res;
+  }
+
+  // Handle the unlock event.
+  // This operation is thread-safe as it only touches the dtls.
+  void onUnlock(DeadlockDetectorTLS<BV> *dtls, uptr node) {
+    if (dtls->getEpoch() == nodeToEpoch(node))
+      dtls->removeLock(nodeToIndexUnchecked(node));
+  }
+
+  // Tries to handle the lock event w/o writing to global state.
+  // Returns true on success.
+  // This operation is thread-safe as it only touches the dtls
+  // (modulo racy nature of hasAllEdges).
+  bool onLockFast(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
+    if (hasAllEdges(dtls, node)) {
+      dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
+      return true;
+    }
+    return false;
+  }
+
+  bool isHeld(DeadlockDetectorTLS<BV> *dtls, uptr node) const {
+    return dtls->getLocks(current_epoch_).getBit(nodeToIndex(node));
+  }
+
+  uptr testOnlyGetEpoch() const { return current_epoch_; }
+  bool testOnlyHasEdge(uptr l1, uptr l2) {
+    return g_.hasEdge(nodeToIndex(l1), nodeToIndex(l2));
+  }
+  // idx1 and idx2 are raw indices to g_, not lock IDs.
+  bool testOnlyHasEdgeRaw(uptr idx1, uptr idx2) {
+    return g_.hasEdge(idx1, idx2);
+  }
+
+  void Print() {
+    for (uptr from = 0; from < size(); from++)
+      for (uptr to = 0; to < size(); to++)
+        if (g_.hasEdge(from, to))
+          Printf("  %zx => %zx\n", from, to);
+  }
+
+ private:
+  void check_idx(uptr idx) const { CHECK_LT(idx, size()); }
+
+  void check_node(uptr node) const {
+    CHECK_GE(node, size());
+    CHECK_EQ(current_epoch_, nodeToEpoch(node));
+  }
+
+  uptr indexToNode(uptr idx) const {
+    check_idx(idx);
+    return idx + current_epoch_;
+  }
+
+  uptr nodeToIndexUnchecked(uptr node) const { return node % size(); }
+
+  uptr nodeToIndex(uptr node) const {
+    check_node(node);
+    return nodeToIndexUnchecked(node);
+  }
+
+  uptr nodeToEpoch(uptr node) const { return node / size() * size(); }
+
+  uptr getAvailableNode(uptr data) {
+    uptr idx = available_nodes_.getAndClearFirstOne();
+    data_[idx] = data;
+    return indexToNode(idx);
+  }
+
+  struct Edge {
+    u16 from;
+    u16 to;
+    u32 stk_from;
+    u32 stk_to;
+    int unique_tid;
+  };
+
+  uptr current_epoch_;
+  BV available_nodes_;
+  BV recycled_nodes_;
+  BV tmp_bv_;
+  BVGraph<BV> g_;
+  uptr data_[BV::kSize];
+  Edge edges_[BV::kSize * 32];
+  uptr n_edges_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_DEADLOCK_DETECTOR_H
diff --git a/lsan/include/sanitizer_common/sanitizer_deadlock_detector_interface.h b/lsan/include/sanitizer_common/sanitizer_deadlock_detector_interface.h
new file mode 100644 (file)
index 0000000..07c3755
--- /dev/null
@@ -0,0 +1,91 @@
+//===-- sanitizer_deadlock_detector_interface.h -----------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime.
+// Abstract deadlock detector interface.
+// FIXME: this is work in progress, nothing really works yet.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
+#define SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
+
+#ifndef SANITIZER_DEADLOCK_DETECTOR_VERSION
+# define SANITIZER_DEADLOCK_DETECTOR_VERSION 1
+#endif
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_atomic.h"
+
+namespace __sanitizer {
+
+// dd - deadlock detector.
+// lt - logical (user) thread.
+// pt - physical (OS) thread.
+
+struct DDPhysicalThread;
+struct DDLogicalThread;
+
+struct DDMutex {
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+  uptr id;
+  u32  stk;  // creation stack
+#elif SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
+  u32              id;
+  u32              recursion;
+  atomic_uintptr_t owner;
+#else
+# error "BAD SANITIZER_DEADLOCK_DETECTOR_VERSION"
+#endif
+  u64  ctx;
+};
+
+struct DDFlags {
+  bool second_deadlock_stack;
+};
+
+struct DDReport {
+  enum { kMaxLoopSize = 8 };
+  int n;  // number of entries in loop
+  struct {
+    u64 thr_ctx;   // user thread context
+    u64 mtx_ctx0;  // user mutex context, start of the edge
+    u64 mtx_ctx1;  // user mutex context, end of the edge
+    u32 stk[2];  // stack ids for the edge
+  } loop[kMaxLoopSize];
+};
+
+struct DDCallback {
+  DDPhysicalThread *pt;
+  DDLogicalThread  *lt;
+
+  virtual u32 Unwind() { return 0; }
+  virtual int UniqueTid() { return 0; }
+};
+
+struct DDetector {
+  static DDetector *Create(const DDFlags *flags);
+
+  virtual DDPhysicalThread* CreatePhysicalThread() { return nullptr; }
+  virtual void DestroyPhysicalThread(DDPhysicalThread *pt) {}
+
+  virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return nullptr; }
+  virtual void DestroyLogicalThread(DDLogicalThread *lt) {}
+
+  virtual void MutexInit(DDCallback *cb, DDMutex *m) {}
+  virtual void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {}
+  virtual void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+      bool trylock) {}
+  virtual void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {}
+  virtual void MutexDestroy(DDCallback *cb, DDMutex *m) {}
+
+  virtual DDReport *GetReport(DDCallback *cb) { return nullptr; }
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
diff --git a/lsan/include/sanitizer_common/sanitizer_flag_parser.h b/lsan/include/sanitizer_common/sanitizer_flag_parser.h
new file mode 100644 (file)
index 0000000..7827d73
--- /dev/null
@@ -0,0 +1,120 @@
+//===-- sanitizer_flag_parser.h ---------------------------------*- C++ -*-===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_FLAG_REGISTRY_H
+#define SANITIZER_FLAG_REGISTRY_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+class FlagHandlerBase {
+ public:
+  virtual bool Parse(const char *value) { return false; }
+};
+
+template <typename T>
+class FlagHandler : public FlagHandlerBase {
+  T *t_;
+
+ public:
+  explicit FlagHandler(T *t) : t_(t) {}
+  bool Parse(const char *value) final;
+};
+
+template <>
+inline bool FlagHandler<bool>::Parse(const char *value) {
+  if (internal_strcmp(value, "0") == 0 ||
+      internal_strcmp(value, "no") == 0 ||
+      internal_strcmp(value, "false") == 0) {
+    *t_ = false;
+    return true;
+  }
+  if (internal_strcmp(value, "1") == 0 ||
+      internal_strcmp(value, "yes") == 0 ||
+      internal_strcmp(value, "true") == 0) {
+    *t_ = true;
+    return true;
+  }
+  Printf("ERROR: Invalid value for bool option: '%s'\n", value);
+  return false;
+}
+
+template <>
+inline bool FlagHandler<const char *>::Parse(const char *value) {
+  *t_ = internal_strdup(value);
+  return true;
+}
+
+template <>
+inline bool FlagHandler<int>::Parse(const char *value) {
+  char *value_end;
+  *t_ = internal_simple_strtoll(value, &value_end, 10);
+  bool ok = *value_end == 0;
+  if (!ok) Printf("ERROR: Invalid value for int option: '%s'\n", value);
+  return ok;
+}
+
+template <>
+inline bool FlagHandler<uptr>::Parse(const char *value) {
+  char *value_end;
+  *t_ = internal_simple_strtoll(value, &value_end, 10);
+  bool ok = *value_end == 0;
+  if (!ok) Printf("ERROR: Invalid value for uptr option: '%s'\n", value);
+  return ok;
+}
+
+class FlagParser {
+  static const int kMaxFlags = 200;
+  struct Flag {
+    const char *name;
+    const char *desc;
+    FlagHandlerBase *handler;
+  } *flags_;
+  int n_flags_;
+
+  const char *buf_;
+  uptr pos_;
+
+ public:
+  FlagParser();
+  void RegisterHandler(const char *name, FlagHandlerBase *handler,
+                       const char *desc);
+  void ParseString(const char *s);
+  bool ParseFile(const char *path, bool ignore_missing);
+  void PrintFlagDescriptions();
+
+  static LowLevelAllocator Alloc;
+
+ private:
+  void fatal_error(const char *err);
+  bool is_space(char c);
+  void skip_whitespace();
+  void parse_flags();
+  void parse_flag();
+  bool run_handler(const char *name, const char *value);
+  char *ll_strndup(const char *s, uptr n);
+};
+
+template <typename T>
+static void RegisterFlag(FlagParser *parser, const char *name, const char *desc,
+                         T *var) {
+  FlagHandler<T> *fh = new (FlagParser::Alloc) FlagHandler<T>(var);  // NOLINT
+  parser->RegisterHandler(name, fh, desc);
+}
+
+void ReportUnrecognizedFlags();
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_FLAG_REGISTRY_H
diff --git a/lsan/include/sanitizer_common/sanitizer_flags.h b/lsan/include/sanitizer_common/sanitizer_flags.h
new file mode 100644 (file)
index 0000000..f1b872b
--- /dev/null
@@ -0,0 +1,53 @@
+//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_FLAGS_H
+#define SANITIZER_FLAGS_H
+
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+struct CommonFlags {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+
+  void SetDefaults();
+  void CopyFrom(const CommonFlags &other);
+};
+
+// Functions to get/set global CommonFlags shared by all sanitizer runtimes:
+extern CommonFlags common_flags_dont_use;
+inline const CommonFlags *common_flags() {
+  return &common_flags_dont_use;
+}
+
+inline void SetCommonFlagsDefaults() {
+  common_flags_dont_use.SetDefaults();
+}
+
+// This function can only be used to setup tool-specific overrides for
+// CommonFlags defaults. Generally, it should only be used right after
+// SetCommonFlagsDefaults(), but before ParseCommonFlagsFromString(), and
+// only during the flags initialization (i.e. before they are used for
+// the first time).
+inline void OverrideCommonFlags(const CommonFlags &cf) {
+  common_flags_dont_use.CopyFrom(cf);
+}
+
+class FlagParser;
+void RegisterCommonFlags(FlagParser *parser,
+                         CommonFlags *cf = &common_flags_dont_use);
+void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf);
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_FLAGS_H
diff --git a/lsan/include/sanitizer_common/sanitizer_flags.inc b/lsan/include/sanitizer_common/sanitizer_flags.inc
new file mode 100644 (file)
index 0000000..517bc37
--- /dev/null
@@ -0,0 +1,197 @@
+//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes common flags available in all sanitizers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef COMMON_FLAG
+#error "Define COMMON_FLAG prior to including this file!"
+#endif
+
+// COMMON_FLAG(Type, Name, DefaultValue, Description)
+// Supported types: bool, const char *, int, uptr.
+// Default value must be a compile-time constant.
+// Description must be a string literal.
+
+COMMON_FLAG(
+    bool, symbolize, true,
+    "If set, use the online symbolizer from common sanitizer runtime to turn "
+    "virtual addresses to file/line locations.")
+COMMON_FLAG(
+    const char *, external_symbolizer_path, nullptr,
+    "Path to external symbolizer. If empty, the tool will search $PATH for "
+    "the symbolizer.")
+COMMON_FLAG(
+    bool, allow_addr2line, false,
+    "If set, allows online symbolizer to run addr2line binary to symbolize "
+    "stack traces (addr2line will only be used if llvm-symbolizer binary is "
+    "unavailable.")
+COMMON_FLAG(const char *, strip_path_prefix, "",
+            "Strips this prefix from file paths in error reports.")
+COMMON_FLAG(bool, fast_unwind_on_check, false,
+            "If available, use the fast frame-pointer-based unwinder on "
+            "internal CHECK failures.")
+COMMON_FLAG(bool, fast_unwind_on_fatal, false,
+            "If available, use the fast frame-pointer-based unwinder on fatal "
+            "errors.")
+COMMON_FLAG(bool, fast_unwind_on_malloc, true,
+            "If available, use the fast frame-pointer-based unwinder on "
+            "malloc/free.")
+COMMON_FLAG(bool, handle_ioctl, false, "Intercept and handle ioctl requests.")
+COMMON_FLAG(int, malloc_context_size, 1,
+            "Max number of stack frames kept for each allocation/deallocation.")
+COMMON_FLAG(
+    const char *, log_path, "stderr",
+    "Write logs to \"log_path.pid\". The special values are \"stdout\" and "
+    "\"stderr\". The default is \"stderr\".")
+COMMON_FLAG(
+    bool, log_exe_name, false,
+    "Mention name of executable when reporting error and "
+    "append executable name to logs (as in \"log_path.exe_name.pid\").")
+COMMON_FLAG(
+    bool, log_to_syslog, SANITIZER_ANDROID,
+    "Write all sanitizer output to syslog in addition to other means of "
+    "logging.")
+COMMON_FLAG(
+    int, verbosity, 0,
+    "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).")
+COMMON_FLAG(bool, detect_leaks, false, "Enable memory leak detection.")
+COMMON_FLAG(
+    bool, leak_check_at_exit, true,
+    "Invoke leak checking in an atexit handler. Has no effect if "
+    "detect_leaks=false, or if __lsan_do_leak_check() is called before the "
+    "handler has a chance to run.")
+COMMON_FLAG(bool, allocator_may_return_null, false,
+            "If false, the allocator will crash instead of returning 0 on "
+            "out-of-memory.")
+COMMON_FLAG(bool, print_summary, true,
+            "If false, disable printing error summaries in addition to error "
+            "reports.")
+COMMON_FLAG(bool, check_printf, true, "Check printf arguments.")
+COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV,
+            "If set, registers the tool's custom SIGSEGV/SIGBUS handler.")
+COMMON_FLAG(bool, handle_abort, false,
+            "If set, registers the tool's custom SIGABRT handler.")
+COMMON_FLAG(bool, handle_sigfpe, true,
+            "If set, registers the tool's custom SIGFPE handler.")
+COMMON_FLAG(bool, allow_user_segv_handler, false,
+            "If set, allows user to register a SEGV handler even if the tool "
+            "registers one.")
+COMMON_FLAG(bool, use_sigaltstack, true,
+            "If set, uses alternate stack for signal handling.")
+COMMON_FLAG(bool, detect_deadlocks, false,
+            "If set, deadlock detection is enabled.")
+COMMON_FLAG(
+    uptr, clear_shadow_mmap_threshold, 64 * 1024,
+    "Large shadow regions are zero-filled using mmap(NORESERVE) instead of "
+    "memset(). This is the threshold size in bytes.")
+COMMON_FLAG(const char *, color, "auto",
+            "Colorize reports: (always|never|auto).")
+COMMON_FLAG(
+    bool, legacy_pthread_cond, false,
+    "Enables support for dynamic libraries linked with libpthread 2.2.5.")
+COMMON_FLAG(bool, intercept_tls_get_addr, false, "Intercept __tls_get_addr.")
+COMMON_FLAG(bool, help, false, "Print the flag descriptions.")
+COMMON_FLAG(uptr, mmap_limit_mb, 0,
+            "Limit the amount of mmap-ed memory (excluding shadow) in Mb; "
+            "not a user-facing flag, used mosly for testing the tools")
+COMMON_FLAG(uptr, hard_rss_limit_mb, 0,
+            "Hard RSS limit in Mb."
+            " If non-zero, a background thread is spawned at startup"
+            " which periodically reads RSS and aborts the process if the"
+            " limit is reached")
+COMMON_FLAG(uptr, soft_rss_limit_mb, 0,
+            "Soft RSS limit in Mb."
+            " If non-zero, a background thread is spawned at startup"
+            " which periodically reads RSS. If the limit is reached"
+            " all subsequent malloc/new calls will fail or return NULL"
+            " (depending on the value of allocator_may_return_null)"
+            " until the RSS goes below the soft limit."
+            " This limit does not affect memory allocations other than"
+            " malloc/new.")
+COMMON_FLAG(bool, can_use_proc_maps_statm, true,
+            "If false, do not attempt to read /proc/maps/statm."
+            " Mostly useful for testing sanitizers.")
+COMMON_FLAG(
+    bool, coverage, false,
+    "If set, coverage information will be dumped at program shutdown (if the "
+    "coverage instrumentation was enabled at compile time).")
+COMMON_FLAG(bool, coverage_pcs, true,
+            "If set (and if 'coverage' is set too), the coverage information "
+            "will be dumped as a set of PC offsets for every module.")
+COMMON_FLAG(bool, coverage_order_pcs, false,
+             "If true, the PCs will be dumped in the order they've"
+             " appeared during the execution.")
+COMMON_FLAG(bool, coverage_bitset, false,
+            "If set (and if 'coverage' is set too), the coverage information "
+            "will also be dumped as a bitset to a separate file.")
+COMMON_FLAG(bool, coverage_counters, false,
+            "If set (and if 'coverage' is set too), the bitmap that corresponds"
+            " to coverage counters will be dumped.")
+COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID,
+            "If set, coverage information will be dumped directly to a memory "
+            "mapped file. This way data is not lost even if the process is "
+            "suddenly killed.")
+COMMON_FLAG(const char *, coverage_dir, ".",
+            "Target directory for coverage dumps. Defaults to the current "
+            "directory.")
+COMMON_FLAG(bool, full_address_space, false,
+            "Sanitize complete address space; "
+            "by default kernel area on 32-bit platforms will not be sanitized")
+COMMON_FLAG(bool, print_suppressions, true,
+            "Print matched suppressions at exit.")
+COMMON_FLAG(
+    bool, disable_coredump, (SANITIZER_WORDSIZE == 64),
+    "Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
+    "dumping a 16T+ core file. Ignored on OSes that don't dump core by"
+    "default and for sanitizers that don't reserve lots of virtual memory.")
+COMMON_FLAG(bool, use_madv_dontdump, true,
+          "If set, instructs kernel to not store the (huge) shadow "
+          "in core file.")
+COMMON_FLAG(bool, symbolize_inline_frames, true,
+            "Print inlined frames in stacktraces. Defaults to true.")
+COMMON_FLAG(bool, symbolize_vs_style, false,
+            "Print file locations in Visual Studio style (e.g: "
+            " file(10,42): ...")
+COMMON_FLAG(const char *, stack_trace_format, "DEFAULT",
+            "Format string used to render stack frames. "
+            "See sanitizer_stacktrace_printer.h for the format description. "
+            "Use DEFAULT to get default format.")
+COMMON_FLAG(bool, no_huge_pages_for_shadow, true,
+            "If true, the shadow is not allowed to use huge pages. ")
+COMMON_FLAG(bool, strict_string_checks, false,
+            "If set check that string arguments are properly null-terminated")
+COMMON_FLAG(bool, intercept_strstr, true,
+            "If set, uses custom wrappers for strstr and strcasestr functions "
+            "to find more errors.")
+COMMON_FLAG(bool, intercept_strspn, true,
+            "If set, uses custom wrappers for strspn and strcspn function "
+            "to find more errors.")
+COMMON_FLAG(bool, intercept_strpbrk, true,
+            "If set, uses custom wrappers for strpbrk function "
+            "to find more errors.")
+COMMON_FLAG(bool, intercept_memcmp, true,
+            "If set, uses custom wrappers for memcmp function "
+            "to find more errors.")
+COMMON_FLAG(bool, strict_memcmp, true,
+          "If true, assume that memcmp(p1, p2, n) always reads n bytes before "
+          "comparing p1 and p2.")
+COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer "
+                                             "mappings in /proc/self/maps with "
+                                             "user-readable names")
+COMMON_FLAG(int, exitcode, 1, "Override the program exit status if the tool "
+                              "found an error")
+COMMON_FLAG(
+    bool, abort_on_error, SANITIZER_MAC,
+    "If set, the tool calls abort() instead of _exit() after printing the "
+    "error report.")
+COMMON_FLAG(bool, suppress_equal_pcs, true,
+            "Deduplicate multiple reports for single source location in "
+            "halt_on_error=false mode (asan only).")
+COMMON_FLAG(bool, print_cmdline, false, "Print command line on crash "
+            "(asan only).")
diff --git a/lsan/include/sanitizer_common/sanitizer_freebsd.h b/lsan/include/sanitizer_common/sanitizer_freebsd.h
new file mode 100644 (file)
index 0000000..47bb131
--- /dev/null
@@ -0,0 +1,135 @@
+//===-- sanitizer_freebsd.h -------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime. It contains FreeBSD-specific
+// definitions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_FREEBSD_H
+#define SANITIZER_FREEBSD_H
+
+#include "sanitizer_internal_defs.h"
+
+// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
+// 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+# include <osreldate.h>
+# if __FreeBSD_version <= 902001  // v9.2
+#  include <link.h>
+#  include <sys/param.h>
+#  include <ucontext.h>
+
+namespace __sanitizer {
+
+typedef unsigned long long __xuint64_t;
+
+typedef __int32_t __xregister_t;
+
+typedef struct __xmcontext {
+  __xregister_t mc_onstack;
+  __xregister_t mc_gs;
+  __xregister_t mc_fs;
+  __xregister_t mc_es;
+  __xregister_t mc_ds;
+  __xregister_t mc_edi;
+  __xregister_t mc_esi;
+  __xregister_t mc_ebp;
+  __xregister_t mc_isp;
+  __xregister_t mc_ebx;
+  __xregister_t mc_edx;
+  __xregister_t mc_ecx;
+  __xregister_t mc_eax;
+  __xregister_t mc_trapno;
+  __xregister_t mc_err;
+  __xregister_t mc_eip;
+  __xregister_t mc_cs;
+  __xregister_t mc_eflags;
+  __xregister_t mc_esp;
+  __xregister_t mc_ss;
+
+  int mc_len;
+  int mc_fpformat;
+  int mc_ownedfp;
+  __xregister_t mc_flags;
+
+  int mc_fpstate[128] __aligned(16);
+  __xregister_t mc_fsbase;
+  __xregister_t mc_gsbase;
+  __xregister_t mc_xfpustate;
+  __xregister_t mc_xfpustate_len;
+
+  int mc_spare2[4];
+} xmcontext_t;
+
+typedef struct __xucontext {
+  sigset_t  uc_sigmask;
+  xmcontext_t  uc_mcontext;
+
+  struct __ucontext *uc_link;
+  stack_t uc_stack;
+  int uc_flags;
+  int __spare__[4];
+} xucontext_t;
+
+struct xkinfo_vmentry {
+  int kve_structsize;
+  int kve_type;
+  __xuint64_t kve_start;
+  __xuint64_t kve_end;
+  __xuint64_t kve_offset;
+  __xuint64_t kve_vn_fileid;
+  __uint32_t kve_vn_fsid;
+  int kve_flags;
+  int kve_resident;
+  int kve_private_resident;
+  int kve_protection;
+  int kve_ref_count;
+  int kve_shadow_count;
+  int kve_vn_type;
+  __xuint64_t kve_vn_size;
+  __uint32_t kve_vn_rdev;
+  __uint16_t kve_vn_mode;
+  __uint16_t kve_status;
+  int _kve_ispare[12];
+  char kve_path[PATH_MAX];
+};
+
+typedef struct {
+  __uint32_t p_type;
+  __uint32_t p_offset;
+  __uint32_t p_vaddr;
+  __uint32_t p_paddr;
+  __uint32_t p_filesz;
+  __uint32_t p_memsz;
+  __uint32_t p_flags;
+  __uint32_t p_align;
+} XElf32_Phdr;
+
+struct xdl_phdr_info {
+  Elf_Addr dlpi_addr;
+  const char *dlpi_name;
+  const XElf32_Phdr *dlpi_phdr;
+  Elf_Half dlpi_phnum;
+  unsigned long long int dlpi_adds;
+  unsigned long long int dlpi_subs;
+  size_t dlpi_tls_modid;
+  void *dlpi_tls_data;
+};
+
+typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info*, size_t, void*);
+typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void*);
+
+#define xdl_iterate_phdr(callback, param) \
+  (((xdl_iterate_phdr_t*) dl_iterate_phdr)((callback), (param)))
+
+}  // namespace __sanitizer
+
+# endif  // __FreeBSD_version <= 902001
+#endif  // SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+
+#endif  // SANITIZER_FREEBSD_H
diff --git a/lsan/include/sanitizer_common/sanitizer_interface_internal.h b/lsan/include/sanitizer_common/sanitizer_interface_internal.h
new file mode 100644 (file)
index 0000000..0547f99
--- /dev/null
@@ -0,0 +1,59 @@
+//===-- sanitizer_interface_internal.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between run-time libraries of sanitizers.
+//
+// This header declares the sanitizer runtime interface functions.
+// The runtime library has to define these functions so the instrumented program
+// could call them.
+//
+// See also include/sanitizer/common_interface_defs.h
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_INTERFACE_INTERNAL_H
+#define SANITIZER_INTERFACE_INTERNAL_H
+
+#include "sanitizer_internal_defs.h"
+
+extern "C" {
+  // Tell the tools to write their reports to "path.<pid>" instead of stderr.
+  // The special values are "stdout" and "stderr".
+  SANITIZER_INTERFACE_ATTRIBUTE
+  void __sanitizer_set_report_path(const char *path);
+
+  typedef struct {
+      int coverage_sandboxed;
+      __sanitizer::sptr coverage_fd;
+      unsigned int coverage_max_block_size;
+  } __sanitizer_sandbox_arguments;
+
+  // Notify the tools that the sandbox is going to be turned on.
+  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
+      __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
+
+  // This function is called by the tool when it has just finished reporting
+  // an error. 'error_summary' is a one-line string that summarizes
+  // the error message. This function can be overridden by the client.
+  SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+  void __sanitizer_report_error_summary(const char *error_summary);
+
+  SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
+  SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init();
+  SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
+  SANITIZER_INTERFACE_ATTRIBUTE
+  void __sanitizer_annotate_contiguous_container(const void *beg,
+                                                 const void *end,
+                                                 const void *old_mid,
+                                                 const void *new_mid);
+  SANITIZER_INTERFACE_ATTRIBUTE
+  int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
+                                              const void *end);
+  SANITIZER_INTERFACE_ATTRIBUTE
+  const void *__sanitizer_contiguous_container_find_bad_address(
+      const void *beg, const void *mid, const void *end);
+  } // extern "C"
+
+#endif  // SANITIZER_INTERFACE_INTERNAL_H
diff --git a/lsan/include/sanitizer_common/sanitizer_internal_defs.h b/lsan/include/sanitizer_common/sanitizer_internal_defs.h
new file mode 100644 (file)
index 0000000..d76ed75
--- /dev/null
@@ -0,0 +1,311 @@
+//===-- sanitizer_internal_defs.h -------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer.
+// It contains macro used in run-time libraries code.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_DEFS_H
+#define SANITIZER_DEFS_H
+
+#include "sanitizer_platform.h"
+
+#ifndef SANITIZER_DEBUG
+# define SANITIZER_DEBUG 0
+#endif
+
+// Only use SANITIZER_*ATTRIBUTE* before the function return type!
+#if SANITIZER_WINDOWS
+# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
+// FIXME find out what we need on Windows, if anything.
+# define SANITIZER_WEAK_ATTRIBUTE
+#elif defined(SANITIZER_GO)
+# define SANITIZER_INTERFACE_ATTRIBUTE
+# define SANITIZER_WEAK_ATTRIBUTE
+#else
+# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
+# define SANITIZER_WEAK_ATTRIBUTE  __attribute__((weak))
+#endif
+
+#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && !defined(SANITIZER_GO)
+# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
+#else
+# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
+#endif
+
+// We can use .preinit_array section on Linux to call sanitizer initialization
+// functions very early in the process startup (unless PIC macro is defined).
+// FIXME: do we have anything like this on Mac?
+#if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC)
+# define SANITIZER_CAN_USE_PREINIT_ARRAY 1
+#else
+# define SANITIZER_CAN_USE_PREINIT_ARRAY 0
+#endif
+
+// GCC does not understand __has_feature
+#if !defined(__has_feature)
+# define __has_feature(x) 0
+#endif
+
+// For portability reasons we do not include stddef.h, stdint.h or any other
+// system header, but we do need some basic types that are not defined
+// in a portable way by the language itself.
+namespace __sanitizer {
+
+#if defined(_WIN64)
+// 64-bit Windows uses LLP64 data model.
+typedef unsigned long long uptr;  // NOLINT
+typedef signed   long long sptr;  // NOLINT
+#else
+typedef unsigned long uptr;  // NOLINT
+typedef signed   long sptr;  // NOLINT
+#endif  // defined(_WIN64)
+#if defined(__x86_64__)
+// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use
+// 64-bit pointer to unwind stack frame.
+typedef unsigned long long uhwptr;  // NOLINT
+#else
+typedef uptr uhwptr;   // NOLINT
+#endif
+typedef unsigned char u8;
+typedef unsigned short u16;  // NOLINT
+typedef unsigned int u32;
+typedef unsigned long long u64;  // NOLINT
+typedef signed   char s8;
+typedef signed   short s16;  // NOLINT
+typedef signed   int s32;
+typedef signed   long long s64;  // NOLINT
+#if SANITIZER_WINDOWS
+// On Windows, files are HANDLE, which is a synonim of void*.
+// Use void* to avoid including <windows.h> everywhere.
+typedef void* fd_t;
+typedef unsigned error_t;
+#else
+typedef int fd_t;
+typedef int error_t;
+#endif
+
+// WARNING: OFF_T may be different from OS type off_t, depending on the value of
+// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls
+// like pread and mmap, as opposed to pread64 and mmap64.
+// FreeBSD, Mac and Linux/x86-64 are special.
+#if SANITIZER_FREEBSD || SANITIZER_MAC || \
+  (SANITIZER_LINUX && defined(__x86_64__))
+typedef u64 OFF_T;
+#else
+typedef uptr OFF_T;
+#endif
+typedef u64  OFF64_T;
+
+#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC
+typedef uptr operator_new_size_type;
+#else
+typedef u32 operator_new_size_type;
+#endif
+}  // namespace __sanitizer
+
+
+using namespace __sanitizer;  // NOLINT
+// ----------- ATTENTION -------------
+// This header should NOT include any other headers to avoid portability issues.
+
+// Common defs.
+#define INLINE inline
+#define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+#define WEAK SANITIZER_WEAK_ATTRIBUTE
+
+// Platform-specific defs.
+#if defined(_MSC_VER)
+# define ALWAYS_INLINE __forceinline
+// FIXME(timurrrr): do we need this on Windows?
+# define ALIAS(x)
+# define ALIGNED(x) __declspec(align(x))
+# define FORMAT(f, a)
+# define NOINLINE __declspec(noinline)
+# define NORETURN __declspec(noreturn)
+# define THREADLOCAL   __declspec(thread)
+# define LIKELY(x) (x)
+# define UNLIKELY(x) (x)
+# define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */
+#else  // _MSC_VER
+# define ALWAYS_INLINE inline __attribute__((always_inline))
+# define ALIAS(x) __attribute__((alias(x)))
+// Please only use the ALIGNED macro before the type.
+// Using ALIGNED after the variable declaration is not portable!
+# define ALIGNED(x) __attribute__((aligned(x)))
+# define FORMAT(f, a)  __attribute__((format(printf, f, a)))
+# define NOINLINE __attribute__((noinline))
+# define NORETURN  __attribute__((noreturn))
+# define THREADLOCAL   __thread
+# define LIKELY(x)     __builtin_expect(!!(x), 1)
+# define UNLIKELY(x)   __builtin_expect(!!(x), 0)
+# if defined(__i386__) || defined(__x86_64__)
+// __builtin_prefetch(x) generates prefetchnt0 on x86
+#  define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x))
+# else
+#  define PREFETCH(x) __builtin_prefetch(x)
+# endif
+#endif  // _MSC_VER
+
+#if !defined(_MSC_VER) || defined(__clang__)
+# define UNUSED __attribute__((unused))
+# define USED __attribute__((used))
+#else
+# define UNUSED
+# define USED
+#endif
+
+#if !defined(_MSC_VER) || defined(__clang__) || MSC_PREREQ(1900)
+# define NOEXCEPT noexcept
+#else
+# define NOEXCEPT throw()
+#endif
+
+// Unaligned versions of basic types.
+typedef ALIGNED(1) u16 uu16;
+typedef ALIGNED(1) u32 uu32;
+typedef ALIGNED(1) u64 uu64;
+typedef ALIGNED(1) s16 us16;
+typedef ALIGNED(1) s32 us32;
+typedef ALIGNED(1) s64 us64;
+
+#if SANITIZER_WINDOWS
+typedef unsigned long DWORD;  // NOLINT
+typedef DWORD thread_return_t;
+# define THREAD_CALLING_CONV __stdcall
+#else  // _WIN32
+typedef void* thread_return_t;
+# define THREAD_CALLING_CONV
+#endif  // _WIN32
+typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg);
+
+// NOTE: Functions below must be defined in each run-time.
+namespace __sanitizer {
+void NORETURN Die();
+
+// FIXME: No, this shouldn't be in the sanitizer interface.
+SANITIZER_INTERFACE_ATTRIBUTE
+void NORETURN CheckFailed(const char *file, int line, const char *cond,
+                          u64 v1, u64 v2);
+}  // namespace __sanitizer
+
+// Check macro
+#define RAW_CHECK_MSG(expr, msg) do { \
+  if (UNLIKELY(!(expr))) { \
+    RawWrite(msg); \
+    Die(); \
+  } \
+} while (0)
+
+#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr)
+
+#define CHECK_IMPL(c1, op, c2) \
+  do { \
+    __sanitizer::u64 v1 = (u64)(c1); \
+    __sanitizer::u64 v2 = (u64)(c2); \
+    if (UNLIKELY(!(v1 op v2))) \
+      __sanitizer::CheckFailed(__FILE__, __LINE__, \
+        "(" #c1 ") " #op " (" #c2 ")", v1, v2); \
+  } while (false) \
+/**/
+
+#define CHECK(a)       CHECK_IMPL((a), !=, 0)
+#define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b))
+#define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b))
+#define CHECK_LT(a, b) CHECK_IMPL((a), <,  (b))
+#define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b))
+#define CHECK_GT(a, b) CHECK_IMPL((a), >,  (b))
+#define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b))
+
+#if SANITIZER_DEBUG
+#define DCHECK(a)       CHECK(a)
+#define DCHECK_EQ(a, b) CHECK_EQ(a, b)
+#define DCHECK_NE(a, b) CHECK_NE(a, b)
+#define DCHECK_LT(a, b) CHECK_LT(a, b)
+#define DCHECK_LE(a, b) CHECK_LE(a, b)
+#define DCHECK_GT(a, b) CHECK_GT(a, b)
+#define DCHECK_GE(a, b) CHECK_GE(a, b)
+#else
+#define DCHECK(a)
+#define DCHECK_EQ(a, b)
+#define DCHECK_NE(a, b)
+#define DCHECK_LT(a, b)
+#define DCHECK_LE(a, b)
+#define DCHECK_GT(a, b)
+#define DCHECK_GE(a, b)
+#endif
+
+#define UNREACHABLE(msg) do { \
+  CHECK(0 && msg); \
+  Die(); \
+} while (0)
+
+#define UNIMPLEMENTED() UNREACHABLE("unimplemented")
+
+#define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__)
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+#define IMPL_PASTE(a, b) a##b
+#define IMPL_COMPILER_ASSERT(pred, line) \
+    typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1]
+
+// Limits for integral types. We have to redefine it in case we don't
+// have stdint.h (like in Visual Studio 9).
+#undef __INT64_C
+#undef __UINT64_C
+#if SANITIZER_WORDSIZE == 64
+# define __INT64_C(c)  c ## L
+# define __UINT64_C(c) c ## UL
+#else
+# define __INT64_C(c)  c ## LL
+# define __UINT64_C(c) c ## ULL
+#endif  // SANITIZER_WORDSIZE == 64
+#undef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#undef INT32_MAX
+#define INT32_MAX              (2147483647)
+#undef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#undef INT64_MIN
+#define INT64_MIN              (-__INT64_C(9223372036854775807)-1)
+#undef INT64_MAX
+#define INT64_MAX              (__INT64_C(9223372036854775807))
+#undef UINT64_MAX
+#define UINT64_MAX             (__UINT64_C(18446744073709551615))
+
+enum LinkerInitialized { LINKER_INITIALIZED = 0 };
+
+#if !defined(_MSC_VER) || defined(__clang__)
+# define GET_CALLER_PC() (uptr)__builtin_return_address(0)
+# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0)
+#else
+extern "C" void* _ReturnAddress(void);
+# pragma intrinsic(_ReturnAddress)
+# define GET_CALLER_PC() (uptr)_ReturnAddress()
+// CaptureStackBackTrace doesn't need to know BP on Windows.
+// FIXME: This macro is still used when printing error reports though it's not
+// clear if the BP value is needed in the ASan reports on Windows.
+# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF
+#endif
+
+#define HANDLE_EINTR(res, f)                                       \
+  {                                                                \
+    int rverrno;                                                   \
+    do {                                                           \
+      res = (f);                                                   \
+    } while (internal_iserror(res, &rverrno) && rverrno == EINTR); \
+  }
+
+// Forces the compiler to generate a frame pointer in the function.
+#define ENABLE_FRAME_POINTER                                       \
+  do {                                                             \
+    volatile uptr enable_fp;                                       \
+    enable_fp = GET_CURRENT_FRAME();                               \
+    (void)enable_fp;                                               \
+  } while (0)
+
+#endif  // SANITIZER_DEFS_H
diff --git a/lsan/include/sanitizer_common/sanitizer_lfstack.h b/lsan/include/sanitizer_common/sanitizer_lfstack.h
new file mode 100644 (file)
index 0000000..8bd0e91
--- /dev/null
@@ -0,0 +1,71 @@
+//===-- sanitizer_lfstack.h -=-----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Lock-free stack.
+// Uses 32/17 bits as ABA-counter on 32/64-bit platforms.
+// The memory passed to Push() must not be ever munmap'ed.
+// The type T must contain T *next field.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_LFSTACK_H
+#define SANITIZER_LFSTACK_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_common.h"
+#include "sanitizer_atomic.h"
+
+namespace __sanitizer {
+
+template<typename T>
+struct LFStack {
+  void Clear() {
+    atomic_store(&head_, 0, memory_order_relaxed);
+  }
+
+  bool Empty() const {
+    return (atomic_load(&head_, memory_order_relaxed) & kPtrMask) == 0;
+  }
+
+  void Push(T *p) {
+    u64 cmp = atomic_load(&head_, memory_order_relaxed);
+    for (;;) {
+      u64 cnt = (cmp & kCounterMask) + kCounterInc;
+      u64 xch = (u64)(uptr)p | cnt;
+      p->next = (T*)(uptr)(cmp & kPtrMask);
+      if (atomic_compare_exchange_weak(&head_, &cmp, xch,
+                                       memory_order_release))
+        break;
+    }
+  }
+
+  T *Pop() {
+    u64 cmp = atomic_load(&head_, memory_order_acquire);
+    for (;;) {
+      T *cur = (T*)(uptr)(cmp & kPtrMask);
+      if (!cur)
+        return nullptr;
+      T *nxt = cur->next;
+      u64 cnt = (cmp & kCounterMask);
+      u64 xch = (u64)(uptr)nxt | cnt;
+      if (atomic_compare_exchange_weak(&head_, &cmp, xch,
+                                       memory_order_acquire))
+        return cur;
+    }
+  }
+
+  // private:
+  static const int kCounterBits = FIRST_32_SECOND_64(32, 17);
+  static const u64 kPtrMask = ((u64)-1) >> kCounterBits;
+  static const u64 kCounterMask = ~kPtrMask;
+  static const u64 kCounterInc = kPtrMask + 1;
+
+  atomic_uint64_t head_;
+};
+} // namespace __sanitizer
+
+#endif // SANITIZER_LFSTACK_H
diff --git a/lsan/include/sanitizer_common/sanitizer_libc.h b/lsan/include/sanitizer_common/sanitizer_libc.h
new file mode 100644 (file)
index 0000000..1b3f8ed
--- /dev/null
@@ -0,0 +1,81 @@
+//===-- sanitizer_libc.h ----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// These tools can not use some of the libc functions directly because those
+// functions are intercepted. Instead, we implement a tiny subset of libc here.
+// FIXME: Some of functions declared in this file are in fact POSIX, not libc.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_LIBC_H
+#define SANITIZER_LIBC_H
+
+// ----------- ATTENTION -------------
+// This header should NOT include any other headers from sanitizer runtime.
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+// internal_X() is a custom implementation of X() for use in RTL.
+
+// String functions
+s64 internal_atoll(const char *nptr);
+void *internal_memchr(const void *s, int c, uptr n);
+void *internal_memrchr(const void *s, int c, uptr n);
+int internal_memcmp(const void* s1, const void* s2, uptr n);
+void *internal_memcpy(void *dest, const void *src, uptr n);
+void *internal_memmove(void *dest, const void *src, uptr n);
+// Set [s, s + n) to 0. Both s and n should be 16-aligned.
+void internal_bzero_aligned16(void *s, uptr n);
+// Should not be used in performance-critical places.
+void *internal_memset(void *s, int c, uptr n);
+char* internal_strchr(const char *s, int c);
+char *internal_strchrnul(const char *s, int c);
+int internal_strcmp(const char *s1, const char *s2);
+uptr internal_strcspn(const char *s, const char *reject);
+char *internal_strdup(const char *s);
+char *internal_strndup(const char *s, uptr n);
+uptr internal_strlen(const char *s);
+char *internal_strncat(char *dst, const char *src, uptr n);
+int internal_strncmp(const char *s1, const char *s2, uptr n);
+char *internal_strncpy(char *dst, const char *src, uptr n);
+uptr internal_strnlen(const char *s, uptr maxlen);
+char *internal_strrchr(const char *s, int c);
+// This is O(N^2), but we are not using it in hot places.
+char *internal_strstr(const char *haystack, const char *needle);
+// Works only for base=10 and doesn't set errno.
+s64 internal_simple_strtoll(const char *nptr, char **endptr, int base);
+int internal_snprintf(char *buffer, uptr length, const char *format, ...);
+
+// Return true if all bytes in [mem, mem+size) are zero.
+// Optimized for the case when the result is true.
+bool mem_is_zero(const char *mem, uptr size);
+
+// I/O
+const fd_t kInvalidFd = (fd_t)-1;
+const fd_t kStdinFd = 0;
+const fd_t kStdoutFd = (fd_t)1;
+const fd_t kStderrFd = (fd_t)2;
+
+uptr internal_ftruncate(fd_t fd, uptr size);
+
+// OS
+void NORETURN internal__exit(int exitcode);
+
+uptr internal_getpid();
+uptr internal_getppid();
+
+// Threading
+uptr internal_sched_yield();
+
+// Error handling
+bool internal_iserror(uptr retval, int *rverrno = nullptr);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_LIBC_H
diff --git a/lsan/include/sanitizer_common/sanitizer_libignore.h b/lsan/include/sanitizer_common/sanitizer_libignore.h
new file mode 100644 (file)
index 0000000..84419d1
--- /dev/null
@@ -0,0 +1,81 @@
+//===-- sanitizer_libignore.h -----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// LibIgnore allows to ignore all interceptors called from a particular set
+// of dynamic libraries. LibIgnore can be initialized with several templates
+// of names of libraries to be ignored. It finds code ranges for the libraries;
+// and checks whether the provided PC value belongs to the code ranges.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_LIBIGNORE_H
+#define SANITIZER_LIBIGNORE_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_common.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_mutex.h"
+
+namespace __sanitizer {
+
+class LibIgnore {
+ public:
+  explicit LibIgnore(LinkerInitialized);
+
+  // Must be called during initialization.
+  void AddIgnoredLibrary(const char *name_templ);
+
+  // Must be called after a new dynamic library is loaded.
+  void OnLibraryLoaded(const char *name);
+
+  // Must be called after a dynamic library is unloaded.
+  void OnLibraryUnloaded();
+
+  // Checks whether the provided PC belongs to one of the ignored libraries.
+  bool IsIgnored(uptr pc) const;
+
+ private:
+  struct Lib {
+    char *templ;
+    char *name;
+    char *real_name;  // target of symlink
+    bool loaded;
+  };
+
+  struct LibCodeRange {
+    uptr begin;
+    uptr end;
+  };
+
+  static const uptr kMaxLibs = 128;
+
+  // Hot part:
+  atomic_uintptr_t loaded_count_;
+  LibCodeRange code_ranges_[kMaxLibs];
+
+  // Cold part:
+  BlockingMutex mutex_;
+  uptr count_;
+  Lib libs_[kMaxLibs];
+
+  // Disallow copying of LibIgnore objects.
+  LibIgnore(const LibIgnore&);  // not implemented
+  void operator = (const LibIgnore&);  // not implemented
+};
+
+inline bool LibIgnore::IsIgnored(uptr pc) const {
+  const uptr n = atomic_load(&loaded_count_, memory_order_acquire);
+  for (uptr i = 0; i < n; i++) {
+    if (pc >= code_ranges_[i].begin && pc < code_ranges_[i].end)
+      return true;
+  }
+  return false;
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_LIBIGNORE_H
diff --git a/lsan/include/sanitizer_common/sanitizer_linux.h b/lsan/include/sanitizer_common/sanitizer_linux.h
new file mode 100644 (file)
index 0000000..eed4f78
--- /dev/null
@@ -0,0 +1,87 @@
+//===-- sanitizer_linux.h ---------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Linux-specific syscall wrappers and classes.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_LINUX_H
+#define SANITIZER_LINUX_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_platform_limits_posix.h"
+
+struct link_map;  // Opaque type returned by dlopen().
+struct sigaltstack;
+
+namespace __sanitizer {
+// Dirent structure for getdents(). Note that this structure is different from
+// the one in <dirent.h>, which is used by readdir().
+struct linux_dirent;
+
+// Syscall wrappers.
+uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count);
+uptr internal_sigaltstack(const struct sigaltstack* ss,
+                          struct sigaltstack* oss);
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+    __sanitizer_sigset_t *oldset);
+void internal_sigfillset(__sanitizer_sigset_t *set);
+
+// Linux-only syscalls.
+#if SANITIZER_LINUX
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
+// Used only by sanitizer_stoptheworld. Signal handlers that are actually used
+// (like the process-wide error reporting SEGV handler) must use
+// internal_sigaction instead.
+int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
+#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) || defined(__arm__) || defined(__i386__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr);
+#endif
+#endif  // SANITIZER_LINUX
+
+// This class reads thread IDs from /proc/<pid>/task using only syscalls.
+class ThreadLister {
+ public:
+  explicit ThreadLister(int pid);
+  ~ThreadLister();
+  // GetNextTID returns -1 if the list of threads is exhausted, or if there has
+  // been an error.
+  int GetNextTID();
+  void Reset();
+  bool error();
+
+ private:
+  bool GetDirectoryEntries();
+
+  int pid_;
+  int descriptor_;
+  InternalScopedBuffer<char> buffer_;
+  bool error_;
+  struct linux_dirent* entry_;
+  int bytes_read_;
+};
+
+// Exposed for testing.
+uptr ThreadDescriptorSize();
+uptr ThreadSelf();
+uptr ThreadSelfOffset();
+
+// Matches a library's file name against a base name (stripping path and version
+// information).
+bool LibraryNameIs(const char *full_name, const char *base_name);
+
+// Call cb for each region mapped by map.
+void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
+#endif  // SANITIZER_LINUX_H
diff --git a/lsan/include/sanitizer_common/sanitizer_list.h b/lsan/include/sanitizer_common/sanitizer_list.h
new file mode 100644 (file)
index 0000000..9216ede
--- /dev/null
@@ -0,0 +1,144 @@
+//===-- sanitizer_list.h ----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains implementation of a list class to be used by
+// ThreadSanitizer, etc run-times.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_LIST_H
+#define SANITIZER_LIST_H
+
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+// Intrusive singly-linked list with size(), push_back(), push_front()
+// pop_front(), append_front() and append_back().
+// This class should be a POD (so that it can be put into TLS)
+// and an object with all zero fields should represent a valid empty list.
+// This class does not have a CTOR, so clear() should be called on all
+// non-zero-initialized objects before using.
+template<class Item>
+struct IntrusiveList {
+  friend class Iterator;
+
+  void clear() {
+    first_ = last_ = nullptr;
+    size_ = 0;
+  }
+
+  bool empty() const { return size_ == 0; }
+  uptr size() const { return size_; }
+
+  void push_back(Item *x) {
+    if (empty()) {
+      x->next = nullptr;
+      first_ = last_ = x;
+      size_ = 1;
+    } else {
+      x->next = nullptr;
+      last_->next = x;
+      last_ = x;
+      size_++;
+    }
+  }
+
+  void push_front(Item *x) {
+    if (empty()) {
+      x->next = nullptr;
+      first_ = last_ = x;
+      size_ = 1;
+    } else {
+      x->next = first_;
+      first_ = x;
+      size_++;
+    }
+  }
+
+  void pop_front() {
+    CHECK(!empty());
+    first_ = first_->next;
+    if (!first_)
+      last_ = nullptr;
+    size_--;
+  }
+
+  Item *front() { return first_; }
+  Item *back() { return last_; }
+
+  void append_front(IntrusiveList<Item> *l) {
+    CHECK_NE(this, l);
+    if (l->empty())
+      return;
+    if (empty()) {
+      *this = *l;
+    } else if (!l->empty()) {
+      l->last_->next = first_;
+      first_ = l->first_;
+      size_ += l->size();
+    }
+    l->clear();
+  }
+
+  void append_back(IntrusiveList<Item> *l) {
+    CHECK_NE(this, l);
+    if (l->empty())
+      return;
+    if (empty()) {
+      *this = *l;
+    } else {
+      last_->next = l->first_;
+      last_ = l->last_;
+      size_ += l->size();
+    }
+    l->clear();
+  }
+
+  void CheckConsistency() {
+    if (size_ == 0) {
+      CHECK_EQ(first_, 0);
+      CHECK_EQ(last_, 0);
+    } else {
+      uptr count = 0;
+      for (Item *i = first_; ; i = i->next) {
+        count++;
+        if (i == last_) break;
+      }
+      CHECK_EQ(size(), count);
+      CHECK_EQ(last_->next, 0);
+    }
+  }
+
+  template<class ListTy, class ItemTy>
+  class IteratorBase {
+   public:
+    explicit IteratorBase(ListTy *list)
+        : list_(list), current_(list->first_) { }
+    ItemTy *next() {
+      ItemTy *ret = current_;
+      if (current_) current_ = current_->next;
+      return ret;
+    }
+    bool hasNext() const { return current_ != nullptr; }
+   private:
+    ListTy *list_;
+    ItemTy *current_;
+  };
+
+  typedef IteratorBase<IntrusiveList<Item>, Item> Iterator;
+  typedef IteratorBase<const IntrusiveList<Item>, const Item> ConstIterator;
+
+// private, don't use directly.
+  uptr size_;
+  Item *first_;
+  Item *last_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_LIST_H
diff --git a/lsan/include/sanitizer_common/sanitizer_mac.h b/lsan/include/sanitizer_common/sanitizer_mac.h
new file mode 100644 (file)
index 0000000..2aac83e
--- /dev/null
@@ -0,0 +1,39 @@
+//===-- sanitizer_mac.h -----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// provides definitions for OSX-specific functions.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_MAC_H
+#define SANITIZER_MAC_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+#include "sanitizer_posix.h"
+
+namespace __sanitizer {
+
+enum MacosVersion {
+  MACOS_VERSION_UNINITIALIZED = 0,
+  MACOS_VERSION_UNKNOWN,
+  MACOS_VERSION_LEOPARD,
+  MACOS_VERSION_SNOW_LEOPARD,
+  MACOS_VERSION_LION,
+  MACOS_VERSION_MOUNTAIN_LION,
+  MACOS_VERSION_MAVERICKS,
+  MACOS_VERSION_YOSEMITE,
+  MACOS_VERSION_UNKNOWN_NEWER
+};
+
+MacosVersion GetMacosVersion();
+
+char **GetEnviron();
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_MAC
+#endif  // SANITIZER_MAC_H
diff --git a/lsan/include/sanitizer_common/sanitizer_malloc_mac.inc b/lsan/include/sanitizer_common/sanitizer_malloc_mac.inc
new file mode 100644 (file)
index 0000000..6ed0f69
--- /dev/null
@@ -0,0 +1,337 @@
+//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains Mac-specific malloc interceptors and a custom zone
+// implementation, which together replace the system allocator.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if !SANITIZER_MAC
+#error "This file should only be compiled on Darwin."
+#endif
+
+#include <AvailabilityMacros.h>
+#include <CoreFoundation/CFBase.h>
+#include <dlfcn.h>
+#include <malloc/malloc.h>
+#include <sys/mman.h>
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_mac.h"
+
+// Similar code is used in Google Perftools,
+// http://code.google.com/p/google-perftools.
+
+static malloc_zone_t sanitizer_zone;
+
+INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
+                             vm_size_t start_size, unsigned zone_flags) {
+  COMMON_MALLOC_ENTER();
+  uptr page_size = GetPageSizeCached();
+  uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
+  COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
+  malloc_zone_t *new_zone = (malloc_zone_t *)p;
+  internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
+  new_zone->zone_name = NULL;  // The name will be changed anyway.
+  if (GetMacosVersion() >= MACOS_VERSION_LION) {
+    // Prevent the client app from overwriting the zone contents.
+    // Library functions that need to modify the zone will set PROT_WRITE on it.
+    // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
+    mprotect(new_zone, allocated_size, PROT_READ);
+  }
+  return new_zone;
+}
+
+INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
+  COMMON_MALLOC_ENTER();
+  return &sanitizer_zone;
+}
+
+INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
+  // FIXME: ASan should support purgeable allocations.
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=139
+  COMMON_MALLOC_ENTER();
+  return &sanitizer_zone;
+}
+
+INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
+  // FIXME: ASan should support purgeable allocations. Ignoring them is fine
+  // for now.
+  COMMON_MALLOC_ENTER();
+}
+
+INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
+  // FIXME: ASan should support purgeable allocations. Ignoring them is fine
+  // for now.
+  COMMON_MALLOC_ENTER();
+  // Must return 0 if the contents were not purged since the last call to
+  // malloc_make_purgeable().
+  return 0;
+}
+
+INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
+  COMMON_MALLOC_ENTER();
+  // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
+  // bytes.
+  size_t buflen =
+      sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
+  InternalScopedString new_name(buflen);
+  if (name && zone->introspect == sanitizer_zone.introspect) {
+    new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
+    name = new_name.data();
+  }
+
+  // Call the system malloc's implementation for both external and our zones,
+  // since that appropriately changes VM region protections on the zone.
+  REAL(malloc_set_zone_name)(zone, name);
+}
+
+INTERCEPTOR(void *, malloc, size_t size) {
+  COMMON_MALLOC_ENTER();
+  COMMON_MALLOC_MALLOC(size);
+  return p;
+}
+
+INTERCEPTOR(void, free, void *ptr) {
+  COMMON_MALLOC_ENTER();
+  if (!ptr) return;
+  COMMON_MALLOC_FREE(ptr);
+}
+
+INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
+  COMMON_MALLOC_ENTER();
+  COMMON_MALLOC_REALLOC(ptr, size);
+  return p;
+}
+
+INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
+  COMMON_MALLOC_ENTER();
+  COMMON_MALLOC_CALLOC(nmemb, size);
+  return p;
+}
+
+INTERCEPTOR(void *, valloc, size_t size) {
+  COMMON_MALLOC_ENTER();
+  COMMON_MALLOC_VALLOC(size);
+  return p;
+}
+
+INTERCEPTOR(size_t, malloc_good_size, size_t size) {
+  COMMON_MALLOC_ENTER();
+  return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
+}
+
+INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
+  COMMON_MALLOC_ENTER();
+  CHECK(memptr);
+  COMMON_MALLOC_MEMALIGN(alignment, size);
+  if (p) {
+    *memptr = p;
+    return 0;
+  }
+  return -1;
+}
+
+namespace {
+
+// TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
+// wrappers, as they are basically copied from there.
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
+  COMMON_MALLOC_SIZE(ptr);
+  return size;
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
+  COMMON_MALLOC_ENTER();
+  COMMON_MALLOC_MALLOC(size);
+  return p;
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
+  if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
+    // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+    const size_t kCallocPoolSize = 1024;
+    static uptr calloc_memory_for_dlsym[kCallocPoolSize];
+    static size_t allocated;
+    size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
+    void *mem = (void*)&calloc_memory_for_dlsym[allocated];
+    allocated += size_in_words;
+    CHECK(allocated < kCallocPoolSize);
+    return mem;
+  }
+  COMMON_MALLOC_CALLOC(nmemb, size);
+  return p;
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
+  COMMON_MALLOC_ENTER();
+  COMMON_MALLOC_VALLOC(size);
+  return p;
+}
+
+#define GET_ZONE_FOR_PTR(ptr) \
+  malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
+  const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
+
+void ALWAYS_INLINE free_common(void *context, void *ptr) {
+  if (!ptr) return;
+  // FIXME: need to retire this flag.
+  if (!COMMON_MALLOC_IGNORE_INVALID_FREE) {
+    COMMON_MALLOC_FREE(ptr);
+  } else {
+    GET_ZONE_FOR_PTR(ptr);
+    COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name);
+  }
+}
+
+// TODO(glider): the allocation callbacks need to be refactored.
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
+  free_common(zone, ptr);
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
+  if (!ptr) {
+    COMMON_MALLOC_MALLOC(new_size);
+    return p;
+  } else {
+    COMMON_MALLOC_SIZE(ptr);
+    if (size) {
+      COMMON_MALLOC_REALLOC(ptr, new_size);
+      return p;
+    } else {
+      // We can't recover from reallocating an unknown address, because
+      // this would require reading at most |new_size| bytes from
+      // potentially unaccessible memory.
+      GET_ZONE_FOR_PTR(ptr);
+      COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
+      return nullptr;
+    }
+  }
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_mz_destroy(malloc_zone_t* zone) {
+  // A no-op -- we will not be destroyed!
+  Report("__sanitizer_mz_destroy() called -- ignoring\n");
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
+  COMMON_MALLOC_ENTER();
+  COMMON_MALLOC_MEMALIGN(align, size);
+  return p;
+}
+
+// This function is currently unused, and we build with -Werror.
+#if 0
+void __sanitizer_mz_free_definite_size(
+    malloc_zone_t* zone, void *ptr, size_t size) {
+  // TODO(glider): check that |size| is valid.
+  UNIMPLEMENTED();
+}
+#endif
+
+kern_return_t mi_enumerator(task_t task, void *,
+                            unsigned type_mask, vm_address_t zone_address,
+                            memory_reader_t reader,
+                            vm_range_recorder_t recorder) {
+  // Should enumerate all the pointers we have.  Seems like a lot of work.
+  return KERN_FAILURE;
+}
+
+size_t mi_good_size(malloc_zone_t *zone, size_t size) {
+  // I think it's always safe to return size, but we maybe could do better.
+  return size;
+}
+
+boolean_t mi_check(malloc_zone_t *zone) {
+  UNIMPLEMENTED();
+}
+
+void mi_print(malloc_zone_t *zone, boolean_t verbose) {
+  UNIMPLEMENTED();
+}
+
+void mi_log(malloc_zone_t *zone, void *address) {
+  // I don't think we support anything like this
+}
+
+void mi_force_lock(malloc_zone_t *zone) {
+  COMMON_MALLOC_FORCE_LOCK();
+}
+
+void mi_force_unlock(malloc_zone_t *zone) {
+  COMMON_MALLOC_FORCE_UNLOCK();
+}
+
+void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
+  COMMON_MALLOC_FILL_STATS(zone, stats);
+}
+
+boolean_t mi_zone_locked(malloc_zone_t *zone) {
+  // UNIMPLEMENTED();
+  return false;
+}
+
+}  // unnamed namespace
+
+namespace COMMON_MALLOC_NAMESPACE {
+
+void ReplaceSystemMalloc() {
+  static malloc_introspection_t sanitizer_zone_introspection;
+  // Ok to use internal_memset, these places are not performance-critical.
+  internal_memset(&sanitizer_zone_introspection, 0,
+                  sizeof(sanitizer_zone_introspection));
+
+  sanitizer_zone_introspection.enumerator = &mi_enumerator;
+  sanitizer_zone_introspection.good_size = &mi_good_size;
+  sanitizer_zone_introspection.check = &mi_check;
+  sanitizer_zone_introspection.print = &mi_print;
+  sanitizer_zone_introspection.log = &mi_log;
+  sanitizer_zone_introspection.force_lock = &mi_force_lock;
+  sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
+  sanitizer_zone_introspection.statistics = &mi_statistics;
+  sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
+
+  internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
+
+  // Use version 6 for OSX >= 10.6.
+  sanitizer_zone.version = 6;
+  sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
+  sanitizer_zone.size = &__sanitizer_mz_size;
+  sanitizer_zone.malloc = &__sanitizer_mz_malloc;
+  sanitizer_zone.calloc = &__sanitizer_mz_calloc;
+  sanitizer_zone.valloc = &__sanitizer_mz_valloc;
+  sanitizer_zone.free = &__sanitizer_mz_free;
+  sanitizer_zone.realloc = &__sanitizer_mz_realloc;
+  sanitizer_zone.destroy = &__sanitizer_mz_destroy;
+  sanitizer_zone.batch_malloc = 0;
+  sanitizer_zone.batch_free = 0;
+  sanitizer_zone.free_definite_size = 0;
+  sanitizer_zone.memalign = &__sanitizer_mz_memalign;
+  sanitizer_zone.introspect = &sanitizer_zone_introspection;
+
+  // Register the zone.
+  malloc_zone_register(&sanitizer_zone);
+}
+
+}  // namespace COMMON_MALLOC_NAMESPACE
diff --git a/lsan/include/sanitizer_common/sanitizer_mutex.h b/lsan/include/sanitizer_common/sanitizer_mutex.h
new file mode 100644 (file)
index 0000000..75f495a
--- /dev/null
@@ -0,0 +1,217 @@
+//===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_MUTEX_H
+#define SANITIZER_MUTEX_H
+
+#include "sanitizer_atomic.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+
+namespace __sanitizer {
+
+class StaticSpinMutex {
+ public:
+  void Init() {
+    atomic_store(&state_, 0, memory_order_relaxed);
+  }
+
+  void Lock() {
+    if (TryLock())
+      return;
+    LockSlow();
+  }
+
+  bool TryLock() {
+    return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
+  }
+
+  void Unlock() {
+    atomic_store(&state_, 0, memory_order_release);
+  }
+
+  void CheckLocked() {
+    CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
+  }
+
+ private:
+  atomic_uint8_t state_;
+
+  void NOINLINE LockSlow() {
+    for (int i = 0;; i++) {
+      if (i < 10)
+        proc_yield(10);
+      else
+        internal_sched_yield();
+      if (atomic_load(&state_, memory_order_relaxed) == 0
+          && atomic_exchange(&state_, 1, memory_order_acquire) == 0)
+        return;
+    }
+  }
+};
+
+class SpinMutex : public StaticSpinMutex {
+ public:
+  SpinMutex() {
+    Init();
+  }
+
+ private:
+  SpinMutex(const SpinMutex&);
+  void operator=(const SpinMutex&);
+};
+
+class BlockingMutex {
+ public:
+#if SANITIZER_WINDOWS
+  // Windows does not currently support LinkerInitialized
+  explicit BlockingMutex(LinkerInitialized);
+#else
+  explicit constexpr BlockingMutex(LinkerInitialized)
+      : opaque_storage_ {0, }, owner_(0) {}
+#endif
+  BlockingMutex();
+  void Lock();
+  void Unlock();
+  void CheckLocked();
+ private:
+  uptr opaque_storage_[10];
+  uptr owner_;  // for debugging
+};
+
+// Reader-writer spin mutex.
+class RWMutex {
+ public:
+  RWMutex() {
+    atomic_store(&state_, kUnlocked, memory_order_relaxed);
+  }
+
+  ~RWMutex() {
+    CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
+  }
+
+  void Lock() {
+    u32 cmp = kUnlocked;
+    if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
+                                       memory_order_acquire))
+      return;
+    LockSlow();
+  }
+
+  void Unlock() {
+    u32 prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
+    DCHECK_NE(prev & kWriteLock, 0);
+    (void)prev;
+  }
+
+  void ReadLock() {
+    u32 prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
+    if ((prev & kWriteLock) == 0)
+      return;
+    ReadLockSlow();
+  }
+
+  void ReadUnlock() {
+    u32 prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
+    DCHECK_EQ(prev & kWriteLock, 0);
+    DCHECK_GT(prev & ~kWriteLock, 0);
+    (void)prev;
+  }
+
+  void CheckLocked() {
+    CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked);
+  }
+
+ private:
+  atomic_uint32_t state_;
+
+  enum {
+    kUnlocked = 0,
+    kWriteLock = 1,
+    kReadLock = 2
+  };
+
+  void NOINLINE LockSlow() {
+    for (int i = 0;; i++) {
+      if (i < 10)
+        proc_yield(10);
+      else
+        internal_sched_yield();
+      u32 cmp = atomic_load(&state_, memory_order_relaxed);
+      if (cmp == kUnlocked &&
+          atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
+                                       memory_order_acquire))
+          return;
+    }
+  }
+
+  void NOINLINE ReadLockSlow() {
+    for (int i = 0;; i++) {
+      if (i < 10)
+        proc_yield(10);
+      else
+        internal_sched_yield();
+      u32 prev = atomic_load(&state_, memory_order_acquire);
+      if ((prev & kWriteLock) == 0)
+        return;
+    }
+  }
+
+  RWMutex(const RWMutex&);
+  void operator = (const RWMutex&);
+};
+
+template<typename MutexType>
+class GenericScopedLock {
+ public:
+  explicit GenericScopedLock(MutexType *mu)
+      : mu_(mu) {
+    mu_->Lock();
+  }
+
+  ~GenericScopedLock() {
+    mu_->Unlock();
+  }
+
+ private:
+  MutexType *mu_;
+
+  GenericScopedLock(const GenericScopedLock&);
+  void operator=(const GenericScopedLock&);
+};
+
+template<typename MutexType>
+class GenericScopedReadLock {
+ public:
+  explicit GenericScopedReadLock(MutexType *mu)
+      : mu_(mu) {
+    mu_->ReadLock();
+  }
+
+  ~GenericScopedReadLock() {
+    mu_->ReadUnlock();
+  }
+
+ private:
+  MutexType *mu_;
+
+  GenericScopedReadLock(const GenericScopedReadLock&);
+  void operator=(const GenericScopedReadLock&);
+};
+
+typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
+typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
+typedef GenericScopedLock<RWMutex> RWMutexLock;
+typedef GenericScopedReadLock<RWMutex> RWMutexReadLock;
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_MUTEX_H
diff --git a/lsan/include/sanitizer_common/sanitizer_persistent_allocator.h b/lsan/include/sanitizer_common/sanitizer_persistent_allocator.h
new file mode 100644 (file)
index 0000000..7118503
--- /dev/null
@@ -0,0 +1,70 @@
+//===-- sanitizer_persistent_allocator.h ------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A fast memory allocator that does not support free() nor realloc().
+// All allocations are forever.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PERSISTENT_ALLOCATOR_H
+#define SANITIZER_PERSISTENT_ALLOCATOR_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+class PersistentAllocator {
+ public:
+  void *alloc(uptr size);
+
+ private:
+  void *tryAlloc(uptr size);
+  StaticSpinMutex mtx;  // Protects alloc of new blocks for region allocator.
+  atomic_uintptr_t region_pos;  // Region allocator for Node's.
+  atomic_uintptr_t region_end;
+};
+
+inline void *PersistentAllocator::tryAlloc(uptr size) {
+  // Optimisic lock-free allocation, essentially try to bump the region ptr.
+  for (;;) {
+    uptr cmp = atomic_load(&region_pos, memory_order_acquire);
+    uptr end = atomic_load(&region_end, memory_order_acquire);
+    if (cmp == 0 || cmp + size > end) return nullptr;
+    if (atomic_compare_exchange_weak(&region_pos, &cmp, cmp + size,
+                                     memory_order_acquire))
+      return (void *)cmp;
+  }
+}
+
+inline void *PersistentAllocator::alloc(uptr size) {
+  // First, try to allocate optimisitically.
+  void *s = tryAlloc(size);
+  if (s) return s;
+  // If failed, lock, retry and alloc new superblock.
+  SpinMutexLock l(&mtx);
+  for (;;) {
+    s = tryAlloc(size);
+    if (s) return s;
+    atomic_store(&region_pos, 0, memory_order_relaxed);
+    uptr allocsz = 64 * 1024;
+    if (allocsz < size) allocsz = size;
+    uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
+    atomic_store(&region_end, mem + allocsz, memory_order_release);
+    atomic_store(&region_pos, mem, memory_order_release);
+  }
+}
+
+extern PersistentAllocator thePersistentAllocator;
+inline void *PersistentAlloc(uptr sz) {
+  return thePersistentAllocator.alloc(sz);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_PERSISTENT_ALLOCATOR_H
diff --git a/lsan/include/sanitizer_common/sanitizer_placement_new.h b/lsan/include/sanitizer_common/sanitizer_placement_new.h
new file mode 100644 (file)
index 0000000..7231e96
--- /dev/null
@@ -0,0 +1,23 @@
+//===-- sanitizer_placement_new.h -------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//
+// The file provides 'placement new'.
+// Do not include it into header files, only into source files.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_PLACEMENT_NEW_H
+#define SANITIZER_PLACEMENT_NEW_H
+
+#include "sanitizer_internal_defs.h"
+
+inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) {
+  return p;
+}
+
+#endif  // SANITIZER_PLACEMENT_NEW_H
diff --git a/lsan/include/sanitizer_common/sanitizer_platform.h b/lsan/include/sanitizer_common/sanitizer_platform.h
new file mode 100644 (file)
index 0000000..7d0ff28
--- /dev/null
@@ -0,0 +1,163 @@
+//===-- sanitizer_platform.h ------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Common platform macros.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PLATFORM_H
+#define SANITIZER_PLATFORM_H
+
+#if !defined(__linux__) && !defined(__FreeBSD__) && \
+  !defined(__APPLE__) && !defined(_WIN32)
+# error "This operating system is not supported"
+#endif
+
+#if defined(__linux__)
+# define SANITIZER_LINUX   1
+#else
+# define SANITIZER_LINUX   0
+#endif
+
+#if defined(__FreeBSD__)
+# define SANITIZER_FREEBSD 1
+#else
+# define SANITIZER_FREEBSD 0
+#endif
+
+#if defined(__APPLE__)
+# define SANITIZER_MAC     1
+# include <TargetConditionals.h>
+# if TARGET_OS_IPHONE
+#  define SANITIZER_IOS    1
+# else
+#  define SANITIZER_IOS    0
+# endif
+# if TARGET_IPHONE_SIMULATOR
+#  define SANITIZER_IOSSIM 1
+# else
+#  define SANITIZER_IOSSIM 0
+# endif
+#else
+# define SANITIZER_MAC     0
+# define SANITIZER_IOS     0
+# define SANITIZER_IOSSIM  0
+#endif
+
+#if defined(_WIN32)
+# define SANITIZER_WINDOWS 1
+#else
+# define SANITIZER_WINDOWS 0
+#endif
+
+#if defined(__ANDROID__)
+# define SANITIZER_ANDROID 1
+#else
+# define SANITIZER_ANDROID 0
+#endif
+
+#define SANITIZER_POSIX (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC)
+
+#if __LP64__ || defined(_WIN64)
+#  define SANITIZER_WORDSIZE 64
+#else
+#  define SANITIZER_WORDSIZE 32
+#endif
+
+#if SANITIZER_WORDSIZE == 64
+# define FIRST_32_SECOND_64(a, b) (b)
+#else
+# define FIRST_32_SECOND_64(a, b) (a)
+#endif
+
+#if defined(__x86_64__) && !defined(_LP64)
+# define SANITIZER_X32 1
+#else
+# define SANITIZER_X32 0
+#endif
+
+// VMA size definition for architecture that support multiple sizes.
+// AArch64 has 3 VMA sizes: 39, 42 and 48.
+#if !defined(SANITIZER_AARCH64_VMA)
+# define SANITIZER_AARCH64_VMA 39
+#else
+# if SANITIZER_AARCH64_VMA != 39 && SANITIZER_AARCH64_VMA != 42
+#  error "invalid SANITIZER_AARCH64_VMA size"
+# endif
+#endif
+
+// By default we allow to use SizeClassAllocator64 on 64-bit platform.
+// But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64
+// does not work well and we need to fallback to SizeClassAllocator32.
+// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
+// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
+#ifndef SANITIZER_CAN_USE_ALLOCATOR64
+# if defined(__mips64) || defined(__aarch64__)
+#  define SANITIZER_CAN_USE_ALLOCATOR64 0
+# else
+#  define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
+# endif
+#endif
+
+// The range of addresses which can be returned my mmap.
+// FIXME: this value should be different on different platforms.  Larger values
+// will still work but will consume more memory for TwoLevelByteMap.
+#if defined(__mips__)
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
+#else
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
+#endif
+
+// The AArch64 linux port uses the canonical syscall set as mandated by
+// the upstream linux community for all new ports. Other ports may still
+// use legacy syscalls.
+#ifndef SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+# if defined(__aarch64__) && SANITIZER_LINUX
+# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 1
+# else
+# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 0
+# endif
+#endif
+
+// udi16 syscalls can only be used when the following conditions are
+// met:
+// * target is one of arm32, x86-32, sparc32, sh or m68k
+// * libc version is libc5, glibc-2.0, glibc-2.1 or glibc-2.2 to 2.15
+//   built against > linux-2.2 kernel headers
+// Since we don't want to include libc headers here, we check the
+// target only.
+#if defined(__arm__) || SANITIZER_X32 || defined(__sparc__)
+#define SANITIZER_USES_UID16_SYSCALLS 1
+#else
+#define SANITIZER_USES_UID16_SYSCALLS 0
+#endif
+
+#if defined(__mips__) || (defined(__aarch64__) && SANITIZER_AARCH64_VMA == 39)
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
+#elif defined(__aarch64__) && SANITIZER_AARCH64_VMA == 42
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 11)
+#else
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
+#endif
+
+// Assume obsolete RPC headers are available by default
+#if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H)
+# define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID)
+# define HAVE_TIRPC_RPC_XDR_H 0
+#endif
+
+/// \macro MSC_PREREQ
+/// \brief Is the compiler MSVC of at least the specified version?
+/// The common \param version values to check for are:
+///  * 1800: Microsoft Visual Studio 2013 / 12.0
+///  * 1900: Microsoft Visual Studio 2015 / 14.0
+#ifdef _MSC_VER
+# define MSC_PREREQ(version) (_MSC_VER >= (version))
+#else
+# define MSC_PREREQ(version) 0
+#endif
+
+#endif // SANITIZER_PLATFORM_H
diff --git a/lsan/include/sanitizer_common/sanitizer_platform_interceptors.h b/lsan/include/sanitizer_common/sanitizer_platform_interceptors.h
new file mode 100644 (file)
index 0000000..040e030
--- /dev/null
@@ -0,0 +1,264 @@
+//===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines macro telling whether sanitizer tools can/should intercept
+// given library functions on a given platform.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
+#define SANITIZER_PLATFORM_INTERCEPTORS_H
+
+#include "sanitizer_internal_defs.h"
+
+#if !SANITIZER_WINDOWS
+# define SI_NOT_WINDOWS 1
+# include "sanitizer_platform_limits_posix.h"
+#else
+# define SI_NOT_WINDOWS 0
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+# define SI_LINUX_NOT_ANDROID 1
+#else
+# define SI_LINUX_NOT_ANDROID 0
+#endif
+
+#if SANITIZER_FREEBSD
+# define SI_FREEBSD 1
+#else
+# define SI_FREEBSD 0
+#endif
+
+#if SANITIZER_LINUX
+# define SI_LINUX 1
+#else
+# define SI_LINUX 0
+#endif
+
+#if SANITIZER_MAC
+# define SI_MAC 1
+#else
+# define SI_MAC 0
+#endif
+
+#if SANITIZER_IOS
+# define SI_IOS 1
+#else
+# define SI_IOS 0
+#endif
+
+#define SANITIZER_INTERCEPT_STRCMP 1
+#define SANITIZER_INTERCEPT_STRSTR 1
+#define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRSPN 1
+#define SANITIZER_INTERCEPT_STRPBRK 1
+#define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MEMCMP 1
+#define SANITIZER_INTERCEPT_MEMCHR 1
+#define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX
+
+#define SANITIZER_INTERCEPT_READ   SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PREAD  SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_WRITE  SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS
+
+#define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS
+
+#define SANITIZER_INTERCEPT_PREADV SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_PRCTL   SI_LINUX
+
+#define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS
+
+#define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID
+
+#ifndef SANITIZER_INTERCEPT_PRINTF
+# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_PRINTF_L SI_FREEBSD
+# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID
+#endif
+
+#define SANITIZER_INTERCEPT_FREXP 1
+#define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS
+
+#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETPWENT \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETPWENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_FREEBSD || SI_LINUX
+#define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WAIT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_INET SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETADDRINFO SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_FREEBSD || SI_LINUX
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_IOCTL SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_INET_ATON SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SYSINFO SI_LINUX
+#define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \
+  (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+    defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
+#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_WCSNRTOMBS \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WCRTOMB \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_CONFSTR \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCANDIR \
+  SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WORDEXP \
+  SI_FREEBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGSETOPS \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_BACKTRACE SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
+#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATFS64 \
+  (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATVFS SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ETHER_NTOA_ATON SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ETHER_HOST \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_ETHER_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SHMCTL \
+  ((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64)
+#define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL \
+  SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING \
+  SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SINCOS SI_LINUX
+#define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX
+#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_RAND_R \
+  SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_ICONV SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS
+
+// FIXME: getline seems to be available on OSX 10.7
+#define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD || SI_MAC
+
+#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
+  SI_FREEBSD || SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_TLS_GET_ADDR \
+  SI_FREEBSD || SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX
+#define SANITIZER_INTERCEPT_GETXATTR SI_LINUX
+#define SANITIZER_INTERCEPT_GETRESID SI_LINUX
+#define SANITIZER_INTERCEPT_GETIFADDRS \
+  SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_IF_INDEXTONAME \
+  SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_AEABI_MEM SI_LINUX && defined(__arm__)
+#define SANITIZER_INTERCEPT___BZERO SI_MAC
+#define SANITIZER_INTERCEPT_FTIME !SI_FREEBSD && SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_FOPEN SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FOPEN64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_OPEN_MEMSTREAM SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE \
+    SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SEM SI_LINUX || SI_FREEBSD
+#define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MINCORE SI_LINUX
+#define SANITIZER_INTERCEPT_PROCESS_VM_READV SI_LINUX
+
+#define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX
+
+#endif  // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
diff --git a/lsan/include/sanitizer_common/sanitizer_platform_limits_posix.h b/lsan/include/sanitizer_common/sanitizer_platform_limits_posix.h
new file mode 100644 (file)
index 0000000..fc1fb80
--- /dev/null
@@ -0,0 +1,1430 @@
+//===-- sanitizer_platform_limits_posix.h ---------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific POSIX data structures.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H
+#define SANITIZER_PLATFORM_LIMITS_POSIX_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD
+// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that
+// incroporates the map structure.
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
+    ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 544)))
+#else
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) ((link_map*)(handle))
+#endif  // !SANITIZER_FREEBSD
+
+namespace __sanitizer {
+  extern unsigned struct_utsname_sz;
+  extern unsigned struct_stat_sz;
+#if !SANITIZER_FREEBSD && !SANITIZER_IOS
+  extern unsigned struct_stat64_sz;
+#endif
+  extern unsigned struct_rusage_sz;
+  extern unsigned siginfo_t_sz;
+  extern unsigned struct_itimerval_sz;
+  extern unsigned pthread_t_sz;
+  extern unsigned pthread_cond_t_sz;
+  extern unsigned pid_t_sz;
+  extern unsigned timeval_sz;
+  extern unsigned uid_t_sz;
+  extern unsigned gid_t_sz;
+  extern unsigned mbstate_t_sz;
+  extern unsigned struct_timezone_sz;
+  extern unsigned struct_tms_sz;
+  extern unsigned struct_itimerspec_sz;
+  extern unsigned struct_sigevent_sz;
+  extern unsigned struct_sched_param_sz;
+  extern unsigned struct_statfs64_sz;
+
+#if !SANITIZER_ANDROID
+  extern unsigned struct_statfs_sz;
+  extern unsigned struct_sockaddr_sz;
+  extern unsigned ucontext_t_sz;
+#endif // !SANITIZER_ANDROID
+
+#if SANITIZER_LINUX
+
+#if defined(__x86_64__)
+  const unsigned struct_kernel_stat_sz = 144;
+  const unsigned struct_kernel_stat64_sz = 0;
+#elif defined(__i386__)
+  const unsigned struct_kernel_stat_sz = 64;
+  const unsigned struct_kernel_stat64_sz = 96;
+#elif defined(__arm__)
+  const unsigned struct_kernel_stat_sz = 64;
+  const unsigned struct_kernel_stat64_sz = 104;
+#elif defined(__aarch64__)
+  const unsigned struct_kernel_stat_sz = 128;
+  const unsigned struct_kernel_stat64_sz = 104;
+#elif defined(__powerpc__) && !defined(__powerpc64__)
+  const unsigned struct_kernel_stat_sz = 72;
+  const unsigned struct_kernel_stat64_sz = 104;
+#elif defined(__powerpc64__)
+  const unsigned struct_kernel_stat_sz = 144;
+  const unsigned struct_kernel_stat64_sz = 104;
+#elif defined(__sparc__) && defined(__arch64__)
+  const unsigned struct___old_kernel_stat_sz = 0;
+  const unsigned struct_kernel_stat_sz = 104;
+  const unsigned struct_kernel_stat64_sz = 144;
+#elif defined(__sparc__) && !defined(__arch64__)
+  const unsigned struct___old_kernel_stat_sz = 0;
+  const unsigned struct_kernel_stat_sz = 64;
+#elif defined(__mips__)
+  #if SANITIZER_WORDSIZE == 64
+  const unsigned struct_kernel_stat_sz = 216;
+  #else
+  const unsigned struct_kernel_stat_sz = 144;
+  #endif
+  const unsigned struct_kernel_stat64_sz = 104;
+#elif defined(__sparc__) && defined(__arch64__)
+  const unsigned struct___old_kernel_stat_sz = 0;
+  const unsigned struct_kernel_stat_sz = 104;
+  const unsigned struct_kernel_stat64_sz = 144;
+#elif defined(__sparc__) && !defined(__arch64__)
+  const unsigned struct___old_kernel_stat_sz = 0;
+  const unsigned struct_kernel_stat_sz = 64;
+  const unsigned struct_kernel_stat64_sz = 104;
+#endif
+  struct __sanitizer_perf_event_attr {
+    unsigned type;
+    unsigned size;
+    // More fields that vary with the kernel version.
+  };
+
+  extern unsigned struct_epoll_event_sz;
+  extern unsigned struct_sysinfo_sz;
+  extern unsigned __user_cap_header_struct_sz;
+  extern unsigned __user_cap_data_struct_sz;
+  extern unsigned struct_new_utsname_sz;
+  extern unsigned struct_old_utsname_sz;
+  extern unsigned struct_oldold_utsname_sz;
+
+  const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long);
+#endif  // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if defined(__powerpc64__)
+  const unsigned struct___old_kernel_stat_sz = 0;
+#elif !defined(__sparc__)
+  const unsigned struct___old_kernel_stat_sz = 32;
+#endif
+
+  extern unsigned struct_rlimit_sz;
+  extern unsigned struct_utimbuf_sz;
+  extern unsigned struct_timespec_sz;
+
+  struct __sanitizer_iocb {
+    u64   aio_data;
+    u32   aio_key_or_aio_reserved1; // Simply crazy.
+    u32   aio_reserved1_or_aio_key; // Luckily, we don't need these.
+    u16   aio_lio_opcode;
+    s16   aio_reqprio;
+    u32   aio_fildes;
+    u64   aio_buf;
+    u64   aio_nbytes;
+    s64   aio_offset;
+    u64   aio_reserved2;
+    u64   aio_reserved3;
+  };
+
+  struct __sanitizer_io_event {
+    u64 data;
+    u64 obj;
+    u64 res;
+    u64 res2;
+  };
+
+  const unsigned iocb_cmd_pread = 0;
+  const unsigned iocb_cmd_pwrite = 1;
+  const unsigned iocb_cmd_preadv = 7;
+  const unsigned iocb_cmd_pwritev = 8;
+
+  struct __sanitizer___sysctl_args {
+    int *name;
+    int nlen;
+    void *oldval;
+    uptr *oldlenp;
+    void *newval;
+    uptr newlen;
+    unsigned long ___unused[4];
+  };
+
+  const unsigned old_sigset_t_sz = sizeof(unsigned long);
+
+  struct __sanitizer_sem_t {
+#if SANITIZER_ANDROID && defined(_LP64)
+    int data[4];
+#elif SANITIZER_ANDROID && !defined(_LP64)
+    int data;
+#elif SANITIZER_LINUX
+    uptr data[4];
+#elif SANITIZER_FREEBSD
+    u32 data[4];
+#endif
+  };
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if SANITIZER_ANDROID
+  struct __sanitizer_mallinfo {
+    uptr v[10];
+  };
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  struct __sanitizer_mallinfo {
+    int v[10];
+  };
+
+  extern unsigned struct_ustat_sz;
+  extern unsigned struct_rlimit64_sz;
+  extern unsigned struct_statvfs64_sz;
+
+  struct __sanitizer_ipc_perm {
+    int __key;
+    int uid;
+    int gid;
+    int cuid;
+    int cgid;
+#ifdef __powerpc__
+    unsigned mode;
+    unsigned __seq;
+    u64 __unused1;
+    u64 __unused2;
+#elif defined(__mips__) || defined(__aarch64__)
+    unsigned int mode;
+    unsigned short __seq;
+    unsigned short __pad1;
+    unsigned long __unused1;
+    unsigned long __unused2;
+#elif defined(__sparc__)
+# if defined(__arch64__)
+    unsigned mode;
+    unsigned short __pad1;
+# else
+    unsigned short __pad1;
+    unsigned short mode;
+    unsigned short __pad2;
+# endif
+    unsigned short __seq;
+    unsigned long long __unused1;
+    unsigned long long __unused2;
+#else
+    unsigned short mode;
+    unsigned short __pad1;
+    unsigned short __seq;
+    unsigned short __pad2;
+#if defined(__x86_64__) && !defined(_LP64)
+    u64 __unused1;
+    u64 __unused2;
+#else
+    unsigned long __unused1;
+    unsigned long __unused2;
+#endif
+#endif
+  };
+
+  struct __sanitizer_shmid_ds {
+    __sanitizer_ipc_perm shm_perm;
+  #if defined(__sparc__)
+  # if !defined(__arch64__)
+    u32 __pad1;
+  # endif
+    long shm_atime;
+  # if !defined(__arch64__)
+    u32 __pad2;
+  # endif
+    long shm_dtime;
+  # if !defined(__arch64__)
+    u32 __pad3;
+  # endif
+    long shm_ctime;
+    uptr shm_segsz;
+    int shm_cpid;
+    int shm_lpid;
+    unsigned long shm_nattch;
+    unsigned long __glibc_reserved1;
+    unsigned long __glibc_reserved2;
+  #else    
+  #ifndef __powerpc__
+    uptr shm_segsz;
+  #elif !defined(__powerpc64__)
+    uptr __unused0;
+  #endif
+  #if defined(__x86_64__) && !defined(_LP64)
+    u64 shm_atime;
+    u64 shm_dtime;
+    u64 shm_ctime;
+  #else
+    uptr shm_atime;
+  #if !defined(_LP64) && !defined(__mips__)
+    uptr __unused1;
+  #endif
+    uptr shm_dtime;
+  #if !defined(_LP64) && !defined(__mips__)
+    uptr __unused2;
+  #endif
+    uptr shm_ctime;
+  #if !defined(_LP64) && !defined(__mips__)
+    uptr __unused3;
+  #endif
+  #endif
+  #ifdef __powerpc__
+    uptr shm_segsz;
+  #endif
+    int shm_cpid;
+    int shm_lpid;
+  #if defined(__x86_64__) && !defined(_LP64)
+    u64 shm_nattch;
+    u64 __unused4;
+    u64 __unused5;
+  #else
+    uptr shm_nattch;
+    uptr __unused4;
+    uptr __unused5;
+  #endif
+#endif
+  };
+#elif SANITIZER_FREEBSD
+  struct __sanitizer_ipc_perm {
+    unsigned int cuid;
+    unsigned int cgid;
+    unsigned int uid;
+    unsigned int gid;
+    unsigned short mode;
+    unsigned short seq;
+    long key;
+  };
+
+  struct __sanitizer_shmid_ds {
+    __sanitizer_ipc_perm shm_perm;
+    unsigned long shm_segsz;
+    unsigned int shm_lpid;
+    unsigned int shm_cpid;
+    int shm_nattch;
+    unsigned long shm_atime;
+    unsigned long shm_dtime;
+    unsigned long shm_ctime;
+  };
+#endif
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  extern unsigned struct_msqid_ds_sz;
+  extern unsigned struct_mq_attr_sz;
+  extern unsigned struct_timex_sz;
+  extern unsigned struct_statvfs_sz;
+#endif  // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+  struct __sanitizer_iovec {
+    void *iov_base;
+    uptr iov_len;
+  };
+
+#if !SANITIZER_ANDROID
+  struct __sanitizer_ifaddrs {
+    struct __sanitizer_ifaddrs *ifa_next;
+    char *ifa_name;
+    unsigned int ifa_flags;
+    void *ifa_addr;    // (struct sockaddr *)
+    void *ifa_netmask; // (struct sockaddr *)
+    // This is a union on Linux.
+# ifdef ifa_dstaddr
+# undef ifa_dstaddr
+# endif
+    void *ifa_dstaddr; // (struct sockaddr *)
+    void *ifa_data;
+  };
+#endif  // !SANITIZER_ANDROID
+
+#if SANITIZER_MAC
+  typedef unsigned long __sanitizer_pthread_key_t;
+#else
+  typedef unsigned __sanitizer_pthread_key_t;
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+
+  struct __sanitizer_XDR {
+    int x_op;
+    void *x_ops;
+    uptr x_public;
+    uptr x_private;
+    uptr x_base;
+    unsigned x_handy;
+  };
+
+  const int __sanitizer_XDR_ENCODE = 0;
+  const int __sanitizer_XDR_DECODE = 1;
+  const int __sanitizer_XDR_FREE = 2;
+#endif
+
+  struct __sanitizer_passwd {
+    char *pw_name;
+    char *pw_passwd;
+    int pw_uid;
+    int pw_gid;
+#if SANITIZER_MAC || SANITIZER_FREEBSD
+    long pw_change;
+    char *pw_class;
+#endif
+#if !(SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32))
+    char *pw_gecos;
+#endif
+    char *pw_dir;
+    char *pw_shell;
+#if SANITIZER_MAC || SANITIZER_FREEBSD
+    long pw_expire;
+#endif
+#if SANITIZER_FREEBSD
+    int pw_fields;
+#endif
+  };
+
+  struct __sanitizer_group {
+    char *gr_name;
+    char *gr_passwd;
+    int gr_gid;
+    char **gr_mem;
+  };
+
+#if defined(__x86_64__) && !defined(_LP64)
+  typedef long long __sanitizer_time_t;
+#else
+  typedef long __sanitizer_time_t;
+#endif
+
+  struct __sanitizer_timeb {
+    __sanitizer_time_t time;
+    unsigned short millitm;
+    short timezone;
+    short dstflag;
+  };
+
+  struct __sanitizer_ether_addr {
+    u8 octet[6];
+  };
+
+  struct __sanitizer_tm {
+    int tm_sec;
+    int tm_min;
+    int tm_hour;
+    int tm_mday;
+    int tm_mon;
+    int tm_year;
+    int tm_wday;
+    int tm_yday;
+    int tm_isdst;
+    long int tm_gmtoff;
+    const char *tm_zone;
+  };
+
+#if SANITIZER_LINUX
+  struct __sanitizer_mntent {
+    char *mnt_fsname;
+    char *mnt_dir;
+    char *mnt_type;
+    char *mnt_opts;
+    int mnt_freq;
+    int mnt_passno;
+  };
+#endif
+
+#if SANITIZER_MAC || SANITIZER_FREEBSD
+  struct __sanitizer_msghdr {
+    void *msg_name;
+    unsigned msg_namelen;
+    struct __sanitizer_iovec *msg_iov;
+    unsigned msg_iovlen;
+    void *msg_control;
+    unsigned msg_controllen;
+    int msg_flags;
+  };
+  struct __sanitizer_cmsghdr {
+    unsigned cmsg_len;
+    int cmsg_level;
+    int cmsg_type;
+  };
+#else
+  struct __sanitizer_msghdr {
+    void *msg_name;
+    unsigned msg_namelen;
+    struct __sanitizer_iovec *msg_iov;
+    uptr msg_iovlen;
+    void *msg_control;
+    uptr msg_controllen;
+    int msg_flags;
+  };
+  struct __sanitizer_cmsghdr {
+    uptr cmsg_len;
+    int cmsg_level;
+    int cmsg_type;
+  };
+#endif
+
+#if SANITIZER_MAC
+  struct __sanitizer_dirent {
+    unsigned long long d_ino;
+    unsigned long long d_seekoff;
+    unsigned short d_reclen;
+    // more fields that we don't care about
+  };
+#elif SANITIZER_FREEBSD
+  struct __sanitizer_dirent {
+    unsigned int d_fileno;
+    unsigned short d_reclen;
+    // more fields that we don't care about
+  };
+#elif SANITIZER_ANDROID || defined(__x86_64__)
+  struct __sanitizer_dirent {
+    unsigned long long d_ino;
+    unsigned long long d_off;
+    unsigned short d_reclen;
+    // more fields that we don't care about
+  };
+#else
+  struct __sanitizer_dirent {
+    uptr d_ino;
+    uptr d_off;
+    unsigned short d_reclen;
+    // more fields that we don't care about
+  };
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  struct __sanitizer_dirent64 {
+    unsigned long long d_ino;
+    unsigned long long d_off;
+    unsigned short d_reclen;
+    // more fields that we don't care about
+  };
+#endif
+
+// 'clock_t' is 32 bits wide on x64 FreeBSD
+#if SANITIZER_FREEBSD
+  typedef int __sanitizer_clock_t;
+#elif defined(__x86_64__) && !defined(_LP64)
+  typedef long long __sanitizer_clock_t;
+#else
+  typedef long __sanitizer_clock_t;
+#endif
+
+#if SANITIZER_LINUX
+  typedef int __sanitizer_clockid_t;
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
+                   || defined(__mips__)
+  typedef unsigned __sanitizer___kernel_uid_t;
+  typedef unsigned __sanitizer___kernel_gid_t;
+#else
+  typedef unsigned short __sanitizer___kernel_uid_t;
+  typedef unsigned short __sanitizer___kernel_gid_t;
+#endif
+#if defined(__x86_64__) && !defined(_LP64)
+  typedef long long __sanitizer___kernel_off_t;
+#else
+  typedef long __sanitizer___kernel_off_t;
+#endif
+
+#if defined(__powerpc__) || defined(__mips__)
+  typedef unsigned int __sanitizer___kernel_old_uid_t;
+  typedef unsigned int __sanitizer___kernel_old_gid_t;
+#else
+  typedef unsigned short __sanitizer___kernel_old_uid_t;
+  typedef unsigned short __sanitizer___kernel_old_gid_t;
+#endif
+
+  typedef long long __sanitizer___kernel_loff_t;
+  typedef struct {
+    unsigned long fds_bits[1024 / (8 * sizeof(long))];
+  } __sanitizer___kernel_fd_set;
+#endif
+
+  // This thing depends on the platform. We are only interested in the upper
+  // limit. Verified with a compiler assert in .cc.
+  const int pthread_attr_t_max_sz = 128;
+  union __sanitizer_pthread_attr_t {
+    char size[pthread_attr_t_max_sz]; // NOLINT
+    void *align;
+  };
+
+#if SANITIZER_ANDROID
+  typedef unsigned long __sanitizer_sigset_t;
+#elif SANITIZER_MAC
+  typedef unsigned __sanitizer_sigset_t;
+#elif SANITIZER_LINUX
+  struct __sanitizer_sigset_t {
+    // The size is determined by looking at sizeof of real sigset_t on linux.
+    uptr val[128 / sizeof(uptr)];
+  };
+#elif SANITIZER_FREEBSD
+  struct __sanitizer_sigset_t {
+     // uint32_t * 4
+     unsigned int __bits[4];
+  };
+#endif
+
+  // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
+#if SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 64)
+  struct __sanitizer_sigaction {
+    unsigned sa_flags;
+    union {
+      void (*sigaction)(int sig, void *siginfo, void *uctx);
+      void (*handler)(int sig);
+    };
+    __sanitizer_sigset_t sa_mask;
+    void (*sa_restorer)();
+  };
+#elif SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32)
+  struct __sanitizer_sigaction {
+    union {
+      void (*sigaction)(int sig, void *siginfo, void *uctx);
+      void (*handler)(int sig);
+    };
+    __sanitizer_sigset_t sa_mask;
+    uptr sa_flags;
+    void (*sa_restorer)();
+  };
+#else // !SANITIZER_ANDROID
+  struct __sanitizer_sigaction {
+#if defined(__mips__) && !SANITIZER_FREEBSD
+    unsigned int sa_flags;
+#endif
+    union {
+      void (*sigaction)(int sig, void *siginfo, void *uctx);
+      void (*handler)(int sig);
+    };
+#if SANITIZER_FREEBSD
+    int sa_flags;
+    __sanitizer_sigset_t sa_mask;
+#else
+    __sanitizer_sigset_t sa_mask;
+#ifndef __mips__
+#if defined(__sparc__)
+    unsigned long sa_flags;
+#else
+    int sa_flags;
+#endif
+#endif
+#endif
+#if SANITIZER_LINUX
+    void (*sa_restorer)();
+#endif
+#if defined(__mips__) && (SANITIZER_WORDSIZE == 32)
+    int sa_resv[1];
+#endif
+  };
+#endif // !SANITIZER_ANDROID
+
+#if SANITIZER_FREEBSD
+  typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
+#elif defined(__mips__)
+  struct __sanitizer_kernel_sigset_t {
+    u8 sig[16];
+  };
+#else
+  struct __sanitizer_kernel_sigset_t {
+    u8 sig[8];
+  };
+#endif
+
+  // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
+  struct __sanitizer_kernel_sigaction_t {
+    union {
+      void (*handler)(int signo);
+      void (*sigaction)(int signo, void *info, void *ctx);
+    };
+    unsigned long sa_flags;
+    void (*sa_restorer)(void);
+    __sanitizer_kernel_sigset_t sa_mask;
+  };
+
+  extern uptr sig_ign;
+  extern uptr sig_dfl;
+  extern uptr sa_siginfo;
+
+#if SANITIZER_LINUX
+  extern int e_tabsz;
+#endif
+
+  extern int af_inet;
+  extern int af_inet6;
+  uptr __sanitizer_in_addr_sz(int af);
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+  struct __sanitizer_dl_phdr_info {
+    uptr dlpi_addr;
+    const char *dlpi_name;
+    const void *dlpi_phdr;
+    short dlpi_phnum;
+  };
+
+  extern unsigned struct_ElfW_Phdr_sz;
+#endif
+
+  struct __sanitizer_addrinfo {
+    int ai_flags;
+    int ai_family;
+    int ai_socktype;
+    int ai_protocol;
+#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+    unsigned ai_addrlen;
+    char *ai_canonname;
+    void *ai_addr;
+#else // LINUX
+    unsigned ai_addrlen;
+    void *ai_addr;
+    char *ai_canonname;
+#endif
+    struct __sanitizer_addrinfo *ai_next;
+  };
+
+  struct __sanitizer_hostent {
+    char *h_name;
+    char **h_aliases;
+    int h_addrtype;
+    int h_length;
+    char **h_addr_list;
+  };
+
+  struct __sanitizer_pollfd {
+    int fd;
+    short events;
+    short revents;
+  };
+
+#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+  typedef unsigned __sanitizer_nfds_t;
+#else
+  typedef unsigned long __sanitizer_nfds_t;
+#endif
+
+#if !SANITIZER_ANDROID
+# if SANITIZER_LINUX
+  struct __sanitizer_glob_t {
+    uptr gl_pathc;
+    char **gl_pathv;
+    uptr gl_offs;
+    int gl_flags;
+
+    void (*gl_closedir)(void *dirp);
+    void *(*gl_readdir)(void *dirp);
+    void *(*gl_opendir)(const char *);
+    int (*gl_lstat)(const char *, void *);
+    int (*gl_stat)(const char *, void *);
+  };
+# elif SANITIZER_FREEBSD
+  struct __sanitizer_glob_t {
+    uptr gl_pathc;
+    uptr gl_matchc;
+    uptr gl_offs;
+    int gl_flags;
+    char **gl_pathv;
+    int (*gl_errfunc)(const char*, int);
+    void (*gl_closedir)(void *dirp);
+    struct dirent *(*gl_readdir)(void *dirp);
+    void *(*gl_opendir)(const char*);
+    int (*gl_lstat)(const char*, void* /* struct stat* */);
+    int (*gl_stat)(const char*, void* /* struct stat* */);
+  };
+# endif  // SANITIZER_FREEBSD
+
+# if SANITIZER_LINUX || SANITIZER_FREEBSD
+  extern int glob_nomatch;
+  extern int glob_altdirfunc;
+# endif
+#endif  // !SANITIZER_ANDROID
+
+  extern unsigned path_max;
+
+  struct __sanitizer_wordexp_t {
+    uptr we_wordc;
+    char **we_wordv;
+    uptr we_offs;
+#if SANITIZER_FREEBSD
+    char *we_strings;
+    uptr we_nbytes;
+#endif
+  };
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  struct __sanitizer_FILE {
+    int _flags;
+    char *_IO_read_ptr;
+    char *_IO_read_end;
+    char *_IO_read_base;
+    char *_IO_write_base;
+    char *_IO_write_ptr;
+    char *_IO_write_end;
+    char *_IO_buf_base;
+    char *_IO_buf_end;
+    char *_IO_save_base;
+    char *_IO_backup_base;
+    char *_IO_save_end;
+    void *_markers;
+    __sanitizer_FILE *_chain;
+    int _fileno;
+  };
+# define SANITIZER_HAS_STRUCT_FILE 1
+#else
+  typedef void __sanitizer_FILE;
+# define SANITIZER_HAS_STRUCT_FILE 0
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
+  (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+    defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
+  extern unsigned struct_user_regs_struct_sz;
+  extern unsigned struct_user_fpregs_struct_sz;
+  extern unsigned struct_user_fpxregs_struct_sz;
+  extern unsigned struct_user_vfpregs_struct_sz;
+
+  extern int ptrace_peektext;
+  extern int ptrace_peekdata;
+  extern int ptrace_peekuser;
+  extern int ptrace_getregs;
+  extern int ptrace_setregs;
+  extern int ptrace_getfpregs;
+  extern int ptrace_setfpregs;
+  extern int ptrace_getfpxregs;
+  extern int ptrace_setfpxregs;
+  extern int ptrace_getvfpregs;
+  extern int ptrace_setvfpregs;
+  extern int ptrace_getsiginfo;
+  extern int ptrace_setsiginfo;
+  extern int ptrace_getregset;
+  extern int ptrace_setregset;
+  extern int ptrace_geteventmsg;
+#endif
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  extern unsigned struct_shminfo_sz;
+  extern unsigned struct_shm_info_sz;
+  extern int shmctl_ipc_stat;
+  extern int shmctl_ipc_info;
+  extern int shmctl_shm_info;
+  extern int shmctl_shm_stat;
+#endif
+
+  extern int map_fixed;
+
+  // ioctl arguments
+  struct __sanitizer_ifconf {
+    int ifc_len;
+    union {
+      void *ifcu_req;
+    } ifc_ifcu;
+#if SANITIZER_MAC
+  } __attribute__((packed));
+#else
+  };
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+struct __sanitizer__obstack_chunk {
+  char *limit;
+  struct __sanitizer__obstack_chunk *prev;
+};
+
+struct __sanitizer_obstack {
+  long chunk_size;
+  struct __sanitizer__obstack_chunk *chunk;
+  char *object_base;
+  char *next_free;
+  uptr more_fields[7];
+};
+
+typedef uptr (*__sanitizer_cookie_io_read)(void *cookie, char *buf, uptr size);
+typedef uptr (*__sanitizer_cookie_io_write)(void *cookie, const char *buf,
+                                            uptr size);
+typedef int (*__sanitizer_cookie_io_seek)(void *cookie, u64 *offset,
+                                          int whence);
+typedef int (*__sanitizer_cookie_io_close)(void *cookie);
+
+struct __sanitizer_cookie_io_functions_t {
+  __sanitizer_cookie_io_read read;
+  __sanitizer_cookie_io_write write;
+  __sanitizer_cookie_io_seek seek;
+  __sanitizer_cookie_io_close close;
+};
+#endif
+
+#define IOC_NRBITS 8
+#define IOC_TYPEBITS 8
+#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__) || defined(__sparc__)
+#define IOC_SIZEBITS 13
+#define IOC_DIRBITS 3
+#define IOC_NONE 1U
+#define IOC_WRITE 4U
+#define IOC_READ 2U
+#else
+#define IOC_SIZEBITS 14
+#define IOC_DIRBITS 2
+#define IOC_NONE 0U
+#define IOC_WRITE 1U
+#define IOC_READ 2U
+#endif
+#define IOC_NRMASK ((1 << IOC_NRBITS) - 1)
+#define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1)
+#define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1)
+#if defined(IOC_DIRMASK)
+#undef IOC_DIRMASK
+#endif
+#define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1)
+#define IOC_NRSHIFT 0
+#define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS)
+#define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS)
+#define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS)
+#define EVIOC_EV_MAX 0x1f
+#define EVIOC_ABS_MAX 0x3f
+
+#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
+#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
+#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
+
+#if defined(__sparc__)
+// In sparc the 14 bits SIZE field overlaps with the
+// least significant bit of DIR, so either IOC_READ or
+// IOC_WRITE shall be 1 in order to get a non-zero SIZE.
+# define IOC_SIZE(nr)                       \
+  ((((((nr) >> 29) & 0x7) & (4U|2U)) == 0)? \
+   0 : (((nr) >> 16) & 0x3fff))
+#else
+#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
+#endif
+
+  extern unsigned struct_ifreq_sz;
+  extern unsigned struct_termios_sz;
+  extern unsigned struct_winsize_sz;
+
+#if SANITIZER_LINUX
+  extern unsigned struct_arpreq_sz;
+  extern unsigned struct_cdrom_msf_sz;
+  extern unsigned struct_cdrom_multisession_sz;
+  extern unsigned struct_cdrom_read_audio_sz;
+  extern unsigned struct_cdrom_subchnl_sz;
+  extern unsigned struct_cdrom_ti_sz;
+  extern unsigned struct_cdrom_tocentry_sz;
+  extern unsigned struct_cdrom_tochdr_sz;
+  extern unsigned struct_cdrom_volctrl_sz;
+  extern unsigned struct_ff_effect_sz;
+  extern unsigned struct_floppy_drive_params_sz;
+  extern unsigned struct_floppy_drive_struct_sz;
+  extern unsigned struct_floppy_fdc_state_sz;
+  extern unsigned struct_floppy_max_errors_sz;
+  extern unsigned struct_floppy_raw_cmd_sz;
+  extern unsigned struct_floppy_struct_sz;
+  extern unsigned struct_floppy_write_errors_sz;
+  extern unsigned struct_format_descr_sz;
+  extern unsigned struct_hd_driveid_sz;
+  extern unsigned struct_hd_geometry_sz;
+  extern unsigned struct_input_absinfo_sz;
+  extern unsigned struct_input_id_sz;
+  extern unsigned struct_mtpos_sz;
+  extern unsigned struct_termio_sz;
+  extern unsigned struct_vt_consize_sz;
+  extern unsigned struct_vt_sizes_sz;
+  extern unsigned struct_vt_stat_sz;
+#endif  // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+  extern unsigned struct_copr_buffer_sz;
+  extern unsigned struct_copr_debug_buf_sz;
+  extern unsigned struct_copr_msg_sz;
+  extern unsigned struct_midi_info_sz;
+  extern unsigned struct_mtget_sz;
+  extern unsigned struct_mtop_sz;
+  extern unsigned struct_rtentry_sz;
+  extern unsigned struct_sbi_instrument_sz;
+  extern unsigned struct_seq_event_rec_sz;
+  extern unsigned struct_synth_info_sz;
+  extern unsigned struct_vt_mode_sz;
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  extern unsigned struct_ax25_parms_struct_sz;
+  extern unsigned struct_cyclades_monitor_sz;
+  extern unsigned struct_input_keymap_entry_sz;
+  extern unsigned struct_ipx_config_data_sz;
+  extern unsigned struct_kbdiacrs_sz;
+  extern unsigned struct_kbentry_sz;
+  extern unsigned struct_kbkeycode_sz;
+  extern unsigned struct_kbsentry_sz;
+  extern unsigned struct_mtconfiginfo_sz;
+  extern unsigned struct_nr_parms_struct_sz;
+  extern unsigned struct_scc_modem_sz;
+  extern unsigned struct_scc_stat_sz;
+  extern unsigned struct_serial_multiport_struct_sz;
+  extern unsigned struct_serial_struct_sz;
+  extern unsigned struct_sockaddr_ax25_sz;
+  extern unsigned struct_unimapdesc_sz;
+  extern unsigned struct_unimapinit_sz;
+#endif  // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  extern unsigned struct_audio_buf_info_sz;
+  extern unsigned struct_ppp_stats_sz;
+#endif  // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+#if !SANITIZER_ANDROID && !SANITIZER_MAC
+  extern unsigned struct_sioc_sg_req_sz;
+  extern unsigned struct_sioc_vif_req_sz;
+#endif
+
+  // ioctl request identifiers
+
+  // A special value to mark ioctls that are not present on the target platform,
+  // when it can not be determined without including any system headers.
+  extern const unsigned IOCTL_NOT_PRESENT;
+
+  extern unsigned IOCTL_FIOASYNC;
+  extern unsigned IOCTL_FIOCLEX;
+  extern unsigned IOCTL_FIOGETOWN;
+  extern unsigned IOCTL_FIONBIO;
+  extern unsigned IOCTL_FIONCLEX;
+  extern unsigned IOCTL_FIOSETOWN;
+  extern unsigned IOCTL_SIOCADDMULTI;
+  extern unsigned IOCTL_SIOCATMARK;
+  extern unsigned IOCTL_SIOCDELMULTI;
+  extern unsigned IOCTL_SIOCGIFADDR;
+  extern unsigned IOCTL_SIOCGIFBRDADDR;
+  extern unsigned IOCTL_SIOCGIFCONF;
+  extern unsigned IOCTL_SIOCGIFDSTADDR;
+  extern unsigned IOCTL_SIOCGIFFLAGS;
+  extern unsigned IOCTL_SIOCGIFMETRIC;
+  extern unsigned IOCTL_SIOCGIFMTU;
+  extern unsigned IOCTL_SIOCGIFNETMASK;
+  extern unsigned IOCTL_SIOCGPGRP;
+  extern unsigned IOCTL_SIOCSIFADDR;
+  extern unsigned IOCTL_SIOCSIFBRDADDR;
+  extern unsigned IOCTL_SIOCSIFDSTADDR;
+  extern unsigned IOCTL_SIOCSIFFLAGS;
+  extern unsigned IOCTL_SIOCSIFMETRIC;
+  extern unsigned IOCTL_SIOCSIFMTU;
+  extern unsigned IOCTL_SIOCSIFNETMASK;
+  extern unsigned IOCTL_SIOCSPGRP;
+  extern unsigned IOCTL_TIOCCONS;
+  extern unsigned IOCTL_TIOCEXCL;
+  extern unsigned IOCTL_TIOCGETD;
+  extern unsigned IOCTL_TIOCGPGRP;
+  extern unsigned IOCTL_TIOCGWINSZ;
+  extern unsigned IOCTL_TIOCMBIC;
+  extern unsigned IOCTL_TIOCMBIS;
+  extern unsigned IOCTL_TIOCMGET;
+  extern unsigned IOCTL_TIOCMSET;
+  extern unsigned IOCTL_TIOCNOTTY;
+  extern unsigned IOCTL_TIOCNXCL;
+  extern unsigned IOCTL_TIOCOUTQ;
+  extern unsigned IOCTL_TIOCPKT;
+  extern unsigned IOCTL_TIOCSCTTY;
+  extern unsigned IOCTL_TIOCSETD;
+  extern unsigned IOCTL_TIOCSPGRP;
+  extern unsigned IOCTL_TIOCSTI;
+  extern unsigned IOCTL_TIOCSWINSZ;
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  extern unsigned IOCTL_SIOCGETSGCNT;
+  extern unsigned IOCTL_SIOCGETVIFCNT;
+#endif
+#if SANITIZER_LINUX
+  extern unsigned IOCTL_EVIOCGABS;
+  extern unsigned IOCTL_EVIOCGBIT;
+  extern unsigned IOCTL_EVIOCGEFFECTS;
+  extern unsigned IOCTL_EVIOCGID;
+  extern unsigned IOCTL_EVIOCGKEY;
+  extern unsigned IOCTL_EVIOCGKEYCODE;
+  extern unsigned IOCTL_EVIOCGLED;
+  extern unsigned IOCTL_EVIOCGNAME;
+  extern unsigned IOCTL_EVIOCGPHYS;
+  extern unsigned IOCTL_EVIOCGRAB;
+  extern unsigned IOCTL_EVIOCGREP;
+  extern unsigned IOCTL_EVIOCGSND;
+  extern unsigned IOCTL_EVIOCGSW;
+  extern unsigned IOCTL_EVIOCGUNIQ;
+  extern unsigned IOCTL_EVIOCGVERSION;
+  extern unsigned IOCTL_EVIOCRMFF;
+  extern unsigned IOCTL_EVIOCSABS;
+  extern unsigned IOCTL_EVIOCSFF;
+  extern unsigned IOCTL_EVIOCSKEYCODE;
+  extern unsigned IOCTL_EVIOCSREP;
+  extern unsigned IOCTL_BLKFLSBUF;
+  extern unsigned IOCTL_BLKGETSIZE;
+  extern unsigned IOCTL_BLKRAGET;
+  extern unsigned IOCTL_BLKRASET;
+  extern unsigned IOCTL_BLKROGET;
+  extern unsigned IOCTL_BLKROSET;
+  extern unsigned IOCTL_BLKRRPART;
+  extern unsigned IOCTL_CDROMAUDIOBUFSIZ;
+  extern unsigned IOCTL_CDROMEJECT;
+  extern unsigned IOCTL_CDROMEJECT_SW;
+  extern unsigned IOCTL_CDROMMULTISESSION;
+  extern unsigned IOCTL_CDROMPAUSE;
+  extern unsigned IOCTL_CDROMPLAYMSF;
+  extern unsigned IOCTL_CDROMPLAYTRKIND;
+  extern unsigned IOCTL_CDROMREADAUDIO;
+  extern unsigned IOCTL_CDROMREADCOOKED;
+  extern unsigned IOCTL_CDROMREADMODE1;
+  extern unsigned IOCTL_CDROMREADMODE2;
+  extern unsigned IOCTL_CDROMREADRAW;
+  extern unsigned IOCTL_CDROMREADTOCENTRY;
+  extern unsigned IOCTL_CDROMREADTOCHDR;
+  extern unsigned IOCTL_CDROMRESET;
+  extern unsigned IOCTL_CDROMRESUME;
+  extern unsigned IOCTL_CDROMSEEK;
+  extern unsigned IOCTL_CDROMSTART;
+  extern unsigned IOCTL_CDROMSTOP;
+  extern unsigned IOCTL_CDROMSUBCHNL;
+  extern unsigned IOCTL_CDROMVOLCTRL;
+  extern unsigned IOCTL_CDROMVOLREAD;
+  extern unsigned IOCTL_CDROM_GET_UPC;
+  extern unsigned IOCTL_FDCLRPRM;
+  extern unsigned IOCTL_FDDEFPRM;
+  extern unsigned IOCTL_FDFLUSH;
+  extern unsigned IOCTL_FDFMTBEG;
+  extern unsigned IOCTL_FDFMTEND;
+  extern unsigned IOCTL_FDFMTTRK;
+  extern unsigned IOCTL_FDGETDRVPRM;
+  extern unsigned IOCTL_FDGETDRVSTAT;
+  extern unsigned IOCTL_FDGETDRVTYP;
+  extern unsigned IOCTL_FDGETFDCSTAT;
+  extern unsigned IOCTL_FDGETMAXERRS;
+  extern unsigned IOCTL_FDGETPRM;
+  extern unsigned IOCTL_FDMSGOFF;
+  extern unsigned IOCTL_FDMSGON;
+  extern unsigned IOCTL_FDPOLLDRVSTAT;
+  extern unsigned IOCTL_FDRAWCMD;
+  extern unsigned IOCTL_FDRESET;
+  extern unsigned IOCTL_FDSETDRVPRM;
+  extern unsigned IOCTL_FDSETEMSGTRESH;
+  extern unsigned IOCTL_FDSETMAXERRS;
+  extern unsigned IOCTL_FDSETPRM;
+  extern unsigned IOCTL_FDTWADDLE;
+  extern unsigned IOCTL_FDWERRORCLR;
+  extern unsigned IOCTL_FDWERRORGET;
+  extern unsigned IOCTL_HDIO_DRIVE_CMD;
+  extern unsigned IOCTL_HDIO_GETGEO;
+  extern unsigned IOCTL_HDIO_GET_32BIT;
+  extern unsigned IOCTL_HDIO_GET_DMA;
+  extern unsigned IOCTL_HDIO_GET_IDENTITY;
+  extern unsigned IOCTL_HDIO_GET_KEEPSETTINGS;
+  extern unsigned IOCTL_HDIO_GET_MULTCOUNT;
+  extern unsigned IOCTL_HDIO_GET_NOWERR;
+  extern unsigned IOCTL_HDIO_GET_UNMASKINTR;
+  extern unsigned IOCTL_HDIO_SET_32BIT;
+  extern unsigned IOCTL_HDIO_SET_DMA;
+  extern unsigned IOCTL_HDIO_SET_KEEPSETTINGS;
+  extern unsigned IOCTL_HDIO_SET_MULTCOUNT;
+  extern unsigned IOCTL_HDIO_SET_NOWERR;
+  extern unsigned IOCTL_HDIO_SET_UNMASKINTR;
+  extern unsigned IOCTL_MTIOCPOS;
+  extern unsigned IOCTL_PPPIOCGASYNCMAP;
+  extern unsigned IOCTL_PPPIOCGDEBUG;
+  extern unsigned IOCTL_PPPIOCGFLAGS;
+  extern unsigned IOCTL_PPPIOCGUNIT;
+  extern unsigned IOCTL_PPPIOCGXASYNCMAP;
+  extern unsigned IOCTL_PPPIOCSASYNCMAP;
+  extern unsigned IOCTL_PPPIOCSDEBUG;
+  extern unsigned IOCTL_PPPIOCSFLAGS;
+  extern unsigned IOCTL_PPPIOCSMAXCID;
+  extern unsigned IOCTL_PPPIOCSMRU;
+  extern unsigned IOCTL_PPPIOCSXASYNCMAP;
+  extern unsigned IOCTL_SIOCDARP;
+  extern unsigned IOCTL_SIOCDRARP;
+  extern unsigned IOCTL_SIOCGARP;
+  extern unsigned IOCTL_SIOCGIFENCAP;
+  extern unsigned IOCTL_SIOCGIFHWADDR;
+  extern unsigned IOCTL_SIOCGIFMAP;
+  extern unsigned IOCTL_SIOCGIFMEM;
+  extern unsigned IOCTL_SIOCGIFNAME;
+  extern unsigned IOCTL_SIOCGIFSLAVE;
+  extern unsigned IOCTL_SIOCGRARP;
+  extern unsigned IOCTL_SIOCGSTAMP;
+  extern unsigned IOCTL_SIOCSARP;
+  extern unsigned IOCTL_SIOCSIFENCAP;
+  extern unsigned IOCTL_SIOCSIFHWADDR;
+  extern unsigned IOCTL_SIOCSIFLINK;
+  extern unsigned IOCTL_SIOCSIFMAP;
+  extern unsigned IOCTL_SIOCSIFMEM;
+  extern unsigned IOCTL_SIOCSIFSLAVE;
+  extern unsigned IOCTL_SIOCSRARP;
+  extern unsigned IOCTL_SNDCTL_COPR_HALT;
+  extern unsigned IOCTL_SNDCTL_COPR_LOAD;
+  extern unsigned IOCTL_SNDCTL_COPR_RCODE;
+  extern unsigned IOCTL_SNDCTL_COPR_RCVMSG;
+  extern unsigned IOCTL_SNDCTL_COPR_RDATA;
+  extern unsigned IOCTL_SNDCTL_COPR_RESET;
+  extern unsigned IOCTL_SNDCTL_COPR_RUN;
+  extern unsigned IOCTL_SNDCTL_COPR_SENDMSG;
+  extern unsigned IOCTL_SNDCTL_COPR_WCODE;
+  extern unsigned IOCTL_SNDCTL_COPR_WDATA;
+  extern unsigned IOCTL_TCFLSH;
+  extern unsigned IOCTL_TCGETA;
+  extern unsigned IOCTL_TCGETS;
+  extern unsigned IOCTL_TCSBRK;
+  extern unsigned IOCTL_TCSBRKP;
+  extern unsigned IOCTL_TCSETA;
+  extern unsigned IOCTL_TCSETAF;
+  extern unsigned IOCTL_TCSETAW;
+  extern unsigned IOCTL_TCSETS;
+  extern unsigned IOCTL_TCSETSF;
+  extern unsigned IOCTL_TCSETSW;
+  extern unsigned IOCTL_TCXONC;
+  extern unsigned IOCTL_TIOCGLCKTRMIOS;
+  extern unsigned IOCTL_TIOCGSOFTCAR;
+  extern unsigned IOCTL_TIOCINQ;
+  extern unsigned IOCTL_TIOCLINUX;
+  extern unsigned IOCTL_TIOCSERCONFIG;
+  extern unsigned IOCTL_TIOCSERGETLSR;
+  extern unsigned IOCTL_TIOCSERGWILD;
+  extern unsigned IOCTL_TIOCSERSWILD;
+  extern unsigned IOCTL_TIOCSLCKTRMIOS;
+  extern unsigned IOCTL_TIOCSSOFTCAR;
+  extern unsigned IOCTL_VT_DISALLOCATE;
+  extern unsigned IOCTL_VT_GETSTATE;
+  extern unsigned IOCTL_VT_RESIZE;
+  extern unsigned IOCTL_VT_RESIZEX;
+  extern unsigned IOCTL_VT_SENDSIG;
+#endif  // SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+  extern unsigned IOCTL_MTIOCGET;
+  extern unsigned IOCTL_MTIOCTOP;
+  extern unsigned IOCTL_SIOCADDRT;
+  extern unsigned IOCTL_SIOCDELRT;
+  extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE;
+  extern unsigned IOCTL_SNDCTL_DSP_GETFMTS;
+  extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK;
+  extern unsigned IOCTL_SNDCTL_DSP_POST;
+  extern unsigned IOCTL_SNDCTL_DSP_RESET;
+  extern unsigned IOCTL_SNDCTL_DSP_SETFMT;
+  extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT;
+  extern unsigned IOCTL_SNDCTL_DSP_SPEED;
+  extern unsigned IOCTL_SNDCTL_DSP_STEREO;
+  extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE;
+  extern unsigned IOCTL_SNDCTL_DSP_SYNC;
+  extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE;
+  extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR;
+  extern unsigned IOCTL_SNDCTL_MIDI_INFO;
+  extern unsigned IOCTL_SNDCTL_MIDI_PRETIME;
+  extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE;
+  extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT;
+  extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT;
+  extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS;
+  extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS;
+  extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND;
+  extern unsigned IOCTL_SNDCTL_SEQ_PANIC;
+  extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE;
+  extern unsigned IOCTL_SNDCTL_SEQ_RESET;
+  extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES;
+  extern unsigned IOCTL_SNDCTL_SEQ_SYNC;
+  extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI;
+  extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD;
+  extern unsigned IOCTL_SNDCTL_SYNTH_INFO;
+  extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL;
+  extern unsigned IOCTL_SNDCTL_TMR_CONTINUE;
+  extern unsigned IOCTL_SNDCTL_TMR_METRONOME;
+  extern unsigned IOCTL_SNDCTL_TMR_SELECT;
+  extern unsigned IOCTL_SNDCTL_TMR_SOURCE;
+  extern unsigned IOCTL_SNDCTL_TMR_START;
+  extern unsigned IOCTL_SNDCTL_TMR_STOP;
+  extern unsigned IOCTL_SNDCTL_TMR_TEMPO;
+  extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE;
+  extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM;
+  extern unsigned IOCTL_SOUND_MIXER_READ_BASS;
+  extern unsigned IOCTL_SOUND_MIXER_READ_CAPS;
+  extern unsigned IOCTL_SOUND_MIXER_READ_CD;
+  extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK;
+  extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE;
+  extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN;
+  extern unsigned IOCTL_SOUND_MIXER_READ_IMIX;
+  extern unsigned IOCTL_SOUND_MIXER_READ_LINE1;
+  extern unsigned IOCTL_SOUND_MIXER_READ_LINE2;
+  extern unsigned IOCTL_SOUND_MIXER_READ_LINE3;
+  extern unsigned IOCTL_SOUND_MIXER_READ_LINE;
+  extern unsigned IOCTL_SOUND_MIXER_READ_LOUD;
+  extern unsigned IOCTL_SOUND_MIXER_READ_MIC;
+  extern unsigned IOCTL_SOUND_MIXER_READ_MUTE;
+  extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN;
+  extern unsigned IOCTL_SOUND_MIXER_READ_PCM;
+  extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV;
+  extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK;
+  extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC;
+  extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER;
+  extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS;
+  extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH;
+  extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE;
+  extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_CD;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE;
+  extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME;
+  extern unsigned IOCTL_SOUND_PCM_READ_BITS;
+  extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS;
+  extern unsigned IOCTL_SOUND_PCM_READ_FILTER;
+  extern unsigned IOCTL_SOUND_PCM_READ_RATE;
+  extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS;
+  extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER;
+  extern unsigned IOCTL_VT_ACTIVATE;
+  extern unsigned IOCTL_VT_GETMODE;
+  extern unsigned IOCTL_VT_OPENQRY;
+  extern unsigned IOCTL_VT_RELDISP;
+  extern unsigned IOCTL_VT_SETMODE;
+  extern unsigned IOCTL_VT_WAITACTIVE;
+#endif  // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  extern unsigned IOCTL_CYGETDEFTHRESH;
+  extern unsigned IOCTL_CYGETDEFTIMEOUT;
+  extern unsigned IOCTL_CYGETMON;
+  extern unsigned IOCTL_CYGETTHRESH;
+  extern unsigned IOCTL_CYGETTIMEOUT;
+  extern unsigned IOCTL_CYSETDEFTHRESH;
+  extern unsigned IOCTL_CYSETDEFTIMEOUT;
+  extern unsigned IOCTL_CYSETTHRESH;
+  extern unsigned IOCTL_CYSETTIMEOUT;
+  extern unsigned IOCTL_EQL_EMANCIPATE;
+  extern unsigned IOCTL_EQL_ENSLAVE;
+  extern unsigned IOCTL_EQL_GETMASTRCFG;
+  extern unsigned IOCTL_EQL_GETSLAVECFG;
+  extern unsigned IOCTL_EQL_SETMASTRCFG;
+  extern unsigned IOCTL_EQL_SETSLAVECFG;
+  extern unsigned IOCTL_EVIOCGKEYCODE_V2;
+  extern unsigned IOCTL_EVIOCGPROP;
+  extern unsigned IOCTL_EVIOCSKEYCODE_V2;
+  extern unsigned IOCTL_FS_IOC_GETFLAGS;
+  extern unsigned IOCTL_FS_IOC_GETVERSION;
+  extern unsigned IOCTL_FS_IOC_SETFLAGS;
+  extern unsigned IOCTL_FS_IOC_SETVERSION;
+  extern unsigned IOCTL_GIO_CMAP;
+  extern unsigned IOCTL_GIO_FONT;
+  extern unsigned IOCTL_GIO_UNIMAP;
+  extern unsigned IOCTL_GIO_UNISCRNMAP;
+  extern unsigned IOCTL_KDADDIO;
+  extern unsigned IOCTL_KDDELIO;
+  extern unsigned IOCTL_KDGETKEYCODE;
+  extern unsigned IOCTL_KDGKBDIACR;
+  extern unsigned IOCTL_KDGKBENT;
+  extern unsigned IOCTL_KDGKBLED;
+  extern unsigned IOCTL_KDGKBMETA;
+  extern unsigned IOCTL_KDGKBSENT;
+  extern unsigned IOCTL_KDMAPDISP;
+  extern unsigned IOCTL_KDSETKEYCODE;
+  extern unsigned IOCTL_KDSIGACCEPT;
+  extern unsigned IOCTL_KDSKBDIACR;
+  extern unsigned IOCTL_KDSKBENT;
+  extern unsigned IOCTL_KDSKBLED;
+  extern unsigned IOCTL_KDSKBMETA;
+  extern unsigned IOCTL_KDSKBSENT;
+  extern unsigned IOCTL_KDUNMAPDISP;
+  extern unsigned IOCTL_LPABORT;
+  extern unsigned IOCTL_LPABORTOPEN;
+  extern unsigned IOCTL_LPCAREFUL;
+  extern unsigned IOCTL_LPCHAR;
+  extern unsigned IOCTL_LPGETIRQ;
+  extern unsigned IOCTL_LPGETSTATUS;
+  extern unsigned IOCTL_LPRESET;
+  extern unsigned IOCTL_LPSETIRQ;
+  extern unsigned IOCTL_LPTIME;
+  extern unsigned IOCTL_LPWAIT;
+  extern unsigned IOCTL_MTIOCGETCONFIG;
+  extern unsigned IOCTL_MTIOCSETCONFIG;
+  extern unsigned IOCTL_PIO_CMAP;
+  extern unsigned IOCTL_PIO_FONT;
+  extern unsigned IOCTL_PIO_UNIMAP;
+  extern unsigned IOCTL_PIO_UNIMAPCLR;
+  extern unsigned IOCTL_PIO_UNISCRNMAP;
+  extern unsigned IOCTL_SCSI_IOCTL_GET_IDLUN;
+  extern unsigned IOCTL_SCSI_IOCTL_PROBE_HOST;
+  extern unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE;
+  extern unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE;
+  extern unsigned IOCTL_SIOCAIPXITFCRT;
+  extern unsigned IOCTL_SIOCAIPXPRISLT;
+  extern unsigned IOCTL_SIOCAX25ADDUID;
+  extern unsigned IOCTL_SIOCAX25DELUID;
+  extern unsigned IOCTL_SIOCAX25GETPARMS;
+  extern unsigned IOCTL_SIOCAX25GETUID;
+  extern unsigned IOCTL_SIOCAX25NOUID;
+  extern unsigned IOCTL_SIOCAX25SETPARMS;
+  extern unsigned IOCTL_SIOCDEVPLIP;
+  extern unsigned IOCTL_SIOCIPXCFGDATA;
+  extern unsigned IOCTL_SIOCNRDECOBS;
+  extern unsigned IOCTL_SIOCNRGETPARMS;
+  extern unsigned IOCTL_SIOCNRRTCTL;
+  extern unsigned IOCTL_SIOCNRSETPARMS;
+  extern unsigned IOCTL_SNDCTL_DSP_GETISPACE;
+  extern unsigned IOCTL_SNDCTL_DSP_GETOSPACE;
+  extern unsigned IOCTL_TIOCGSERIAL;
+  extern unsigned IOCTL_TIOCSERGETMULTI;
+  extern unsigned IOCTL_TIOCSERSETMULTI;
+  extern unsigned IOCTL_TIOCSSERIAL;
+#endif  // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  extern unsigned IOCTL_GIO_SCRNMAP;
+  extern unsigned IOCTL_KDDISABIO;
+  extern unsigned IOCTL_KDENABIO;
+  extern unsigned IOCTL_KDGETLED;
+  extern unsigned IOCTL_KDGETMODE;
+  extern unsigned IOCTL_KDGKBMODE;
+  extern unsigned IOCTL_KDGKBTYPE;
+  extern unsigned IOCTL_KDMKTONE;
+  extern unsigned IOCTL_KDSETLED;
+  extern unsigned IOCTL_KDSETMODE;
+  extern unsigned IOCTL_KDSKBMODE;
+  extern unsigned IOCTL_KIOCSOUND;
+  extern unsigned IOCTL_PIO_SCRNMAP;
+#endif
+
+  extern const int errno_EINVAL;
+  extern const int errno_EOWNERDEAD;
+
+  extern const int si_SEGV_MAPERR;
+  extern const int si_SEGV_ACCERR;
+}  // namespace __sanitizer
+
+#define CHECK_TYPE_SIZE(TYPE) \
+  COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE))
+
+#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER)                       \
+  COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \
+                 sizeof(((CLASS *) NULL)->MEMBER));                \
+  COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) ==          \
+                 offsetof(CLASS, MEMBER))
+
+// For sigaction, which is a function and struct at the same time,
+// and thus requires explicit "struct" in sizeof() expression.
+#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER)                       \
+  COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \
+                 sizeof(((struct CLASS *) NULL)->MEMBER));                \
+  COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) ==          \
+                 offsetof(struct CLASS, MEMBER))
+
+#endif
diff --git a/lsan/include/sanitizer_common/sanitizer_posix.h b/lsan/include/sanitizer_common/sanitizer_posix.h
new file mode 100644 (file)
index 0000000..8dd259e
--- /dev/null
@@ -0,0 +1,81 @@
+//===-- sanitizer_posix.h -------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and declares some useful POSIX-specific functions.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_POSIX_H
+#define SANITIZER_POSIX_H
+
+// ----------- ATTENTION -------------
+// This header should NOT include any other headers from sanitizer runtime.
+#include "sanitizer_internal_defs.h"
+
+#if !SANITIZER_POSIX
+// Make it hard to accidentally use any of functions declared in this file:
+#error This file should only be included on POSIX
+#endif
+
+namespace __sanitizer {
+
+// I/O
+// Don't use directly, use __sanitizer::OpenFile() instead.
+uptr internal_open(const char *filename, int flags);
+uptr internal_open(const char *filename, int flags, u32 mode);
+uptr internal_close(fd_t fd);
+
+uptr internal_read(fd_t fd, void *buf, uptr count);
+uptr internal_write(fd_t fd, const void *buf, uptr count);
+
+// Memory
+uptr internal_mmap(void *addr, uptr length, int prot, int flags,
+                   int fd, OFF_T offset);
+uptr internal_munmap(void *addr, uptr length);
+int internal_mprotect(void *addr, uptr length, int prot);
+
+// OS
+uptr internal_filesize(fd_t fd);  // -1 on error.
+uptr internal_stat(const char *path, void *buf);
+uptr internal_lstat(const char *path, void *buf);
+uptr internal_fstat(fd_t fd, void *buf);
+uptr internal_dup2(int oldfd, int newfd);
+uptr internal_readlink(const char *path, char *buf, uptr bufsize);
+uptr internal_unlink(const char *path);
+uptr internal_rename(const char *oldpath, const char *newpath);
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
+
+uptr internal_ptrace(int request, int pid, void *addr, void *data);
+uptr internal_waitpid(int pid, int *status, int options);
+
+int internal_fork();
+
+// These functions call appropriate pthread_ functions directly, bypassing
+// the interceptor. They are weak and may not be present in some tools.
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_create(void *th, void *attr, void *(*callback)(void *),
+                        void *param);
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_join(void *th, void **ret);
+
+#define DEFINE_REAL_PTHREAD_FUNCTIONS                                          \
+  namespace __sanitizer {                                                      \
+  int real_pthread_create(void *th, void *attr, void *(*callback)(void *),     \
+                          void *param) {                                       \
+    return REAL(pthread_create)(th, attr, callback, param);                    \
+  }                                                                            \
+  int real_pthread_join(void *th, void **ret) {                                \
+    return REAL(pthread_join(th, ret));                                        \
+  }                                                                            \
+  }  // namespace __sanitizer
+
+int my_pthread_attr_getstack(void *attr, void **addr, uptr *size);
+
+int internal_sigaction(int signum, const void *act, void *oldact);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_POSIX_H
diff --git a/lsan/include/sanitizer_common/sanitizer_procmaps.h b/lsan/include/sanitizer_common/sanitizer_procmaps.h
new file mode 100644 (file)
index 0000000..7477abf
--- /dev/null
@@ -0,0 +1,97 @@
+//===-- sanitizer_procmaps.h ------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer.
+//
+// Information about the process mappings.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_PROCMAPS_H
+#define SANITIZER_PROCMAPS_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+
+namespace __sanitizer {
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+struct ProcSelfMapsBuff {
+  char *data;
+  uptr mmaped_size;
+  uptr len;
+};
+
+// Reads process memory map in an OS-specific way.
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
+#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
+
+class MemoryMappingLayout {
+ public:
+  explicit MemoryMappingLayout(bool cache_enabled);
+  ~MemoryMappingLayout();
+  bool Next(uptr *start, uptr *end, uptr *offset,
+            char filename[], uptr filename_size, uptr *protection);
+  void Reset();
+  // In some cases, e.g. when running under a sandbox on Linux, ASan is unable
+  // to obtain the memory mappings. It should fall back to pre-cached data
+  // instead of aborting.
+  static void CacheMemoryMappings();
+
+  // Stores the list of mapped objects into an array.
+  uptr DumpListOfModules(LoadedModule *modules, uptr max_modules,
+                         string_predicate_t filter);
+
+  // Memory protection masks.
+  static const uptr kProtectionRead = 1;
+  static const uptr kProtectionWrite = 2;
+  static const uptr kProtectionExecute = 4;
+  static const uptr kProtectionShared = 8;
+
+ private:
+  void LoadFromCache();
+
+  // FIXME: Hide implementation details for different platforms in
+  // platform-specific files.
+# if SANITIZER_FREEBSD || SANITIZER_LINUX
+  ProcSelfMapsBuff proc_self_maps_;
+  const char *current_;
+
+  // Static mappings cache.
+  static ProcSelfMapsBuff cached_proc_self_maps_;
+  static StaticSpinMutex cache_lock_;  // protects cached_proc_self_maps_.
+# elif SANITIZER_MAC
+  template<u32 kLCSegment, typename SegmentCommand>
+  bool NextSegmentLoad(uptr *start, uptr *end, uptr *offset,
+                       char filename[], uptr filename_size,
+                       uptr *protection);
+  int current_image_;
+  u32 current_magic_;
+  u32 current_filetype_;
+  int current_load_cmd_count_;
+  char *current_load_cmd_addr_;
+# endif
+};
+
+typedef void (*fill_profile_f)(uptr start, uptr rss, bool file,
+                               /*out*/uptr *stats, uptr stats_size);
+
+// Parse the contents of /proc/self/smaps and generate a memory profile.
+// |cb| is a tool-specific callback that fills the |stats| array containing
+// |stats_size| elements.
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size);
+
+// Returns code range for the specified module.
+bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end);
+
+bool IsDecimal(char c);
+uptr ParseDecimal(const char **p);
+bool IsHex(char c);
+uptr ParseHex(const char **p);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_PROCMAPS_H
diff --git a/lsan/include/sanitizer_common/sanitizer_quarantine.h b/lsan/include/sanitizer_common/sanitizer_quarantine.h
new file mode 100644 (file)
index 0000000..a635871
--- /dev/null
@@ -0,0 +1,183 @@
+//===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Memory quarantine for AddressSanitizer and potentially other tools.
+// Quarantine caches some specified amount of memory in per-thread caches,
+// then evicts to global FIFO queue. When the queue reaches specified threshold,
+// oldest memory is recycled.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_QUARANTINE_H
+#define SANITIZER_QUARANTINE_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_list.h"
+
+namespace __sanitizer {
+
+template<typename Node> class QuarantineCache;
+
+struct QuarantineBatch {
+  static const uptr kSize = 1021;
+  QuarantineBatch *next;
+  uptr size;
+  uptr count;
+  void *batch[kSize];
+};
+
+COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13));  // 8Kb.
+
+// The callback interface is:
+// void Callback::Recycle(Node *ptr);
+// void *cb.Allocate(uptr size);
+// void cb.Deallocate(void *ptr);
+template<typename Callback, typename Node>
+class Quarantine {
+ public:
+  typedef QuarantineCache<Callback> Cache;
+
+  explicit Quarantine(LinkerInitialized)
+      : cache_(LINKER_INITIALIZED) {
+  }
+
+  void Init(uptr size, uptr cache_size) {
+    atomic_store(&max_size_, size, memory_order_release);
+    atomic_store(&min_size_, size / 10 * 9,
+                 memory_order_release); // 90% of max size.
+    max_cache_size_ = cache_size;
+  }
+
+  uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); }
+
+  void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
+    c->Enqueue(cb, ptr, size);
+    if (c->Size() > max_cache_size_)
+      Drain(c, cb);
+  }
+
+  void NOINLINE Drain(Cache *c, Callback cb) {
+    {
+      SpinMutexLock l(&cache_mutex_);
+      cache_.Transfer(c);
+    }
+    if (cache_.Size() > GetSize() && recycle_mutex_.TryLock())
+      Recycle(cb);
+  }
+
+ private:
+  // Read-only data.
+  char pad0_[kCacheLineSize];
+  atomic_uintptr_t max_size_;
+  atomic_uintptr_t min_size_;
+  uptr max_cache_size_;
+  char pad1_[kCacheLineSize];
+  SpinMutex cache_mutex_;
+  SpinMutex recycle_mutex_;
+  Cache cache_;
+  char pad2_[kCacheLineSize];
+
+  void NOINLINE Recycle(Callback cb) {
+    Cache tmp;
+    uptr min_size = atomic_load(&min_size_, memory_order_acquire);
+    {
+      SpinMutexLock l(&cache_mutex_);
+      while (cache_.Size() > min_size) {
+        QuarantineBatch *b = cache_.DequeueBatch();
+        tmp.EnqueueBatch(b);
+      }
+    }
+    recycle_mutex_.Unlock();
+    DoRecycle(&tmp, cb);
+  }
+
+  void NOINLINE DoRecycle(Cache *c, Callback cb) {
+    while (QuarantineBatch *b = c->DequeueBatch()) {
+      const uptr kPrefetch = 16;
+      for (uptr i = 0; i < kPrefetch; i++)
+        PREFETCH(b->batch[i]);
+      for (uptr i = 0; i < b->count; i++) {
+        PREFETCH(b->batch[i + kPrefetch]);
+        cb.Recycle((Node*)b->batch[i]);
+      }
+      cb.Deallocate(b);
+    }
+  }
+};
+
+// Per-thread cache of memory blocks.
+template<typename Callback>
+class QuarantineCache {
+ public:
+  explicit QuarantineCache(LinkerInitialized) {
+  }
+
+  QuarantineCache()
+      : size_() {
+    list_.clear();
+  }
+
+  uptr Size() const {
+    return atomic_load(&size_, memory_order_relaxed);
+  }
+
+  void Enqueue(Callback cb, void *ptr, uptr size) {
+    if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) {
+      AllocBatch(cb);
+      size += sizeof(QuarantineBatch);  // Count the batch in Quarantine size.
+    }
+    QuarantineBatch *b = list_.back();
+    CHECK(b);
+    b->batch[b->count++] = ptr;
+    b->size += size;
+    SizeAdd(size);
+  }
+
+  void Transfer(QuarantineCache *c) {
+    list_.append_back(&c->list_);
+    SizeAdd(c->Size());
+    atomic_store(&c->size_, 0, memory_order_relaxed);
+  }
+
+  void EnqueueBatch(QuarantineBatch *b) {
+    list_.push_back(b);
+    SizeAdd(b->size);
+  }
+
+  QuarantineBatch *DequeueBatch() {
+    if (list_.empty())
+      return nullptr;
+    QuarantineBatch *b = list_.front();
+    list_.pop_front();
+    SizeSub(b->size);
+    return b;
+  }
+
+ private:
+  IntrusiveList<QuarantineBatch> list_;
+  atomic_uintptr_t size_;
+
+  void SizeAdd(uptr add) {
+    atomic_store(&size_, Size() + add, memory_order_relaxed);
+  }
+  void SizeSub(uptr sub) {
+    atomic_store(&size_, Size() - sub, memory_order_relaxed);
+  }
+
+  NOINLINE QuarantineBatch* AllocBatch(Callback cb) {
+    QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
+    CHECK(b);
+    b->count = 0;
+    b->size = 0;
+    list_.push_back(b);
+    return b;
+  }
+};
+} // namespace __sanitizer
+
+#endif // SANITIZER_QUARANTINE_H
diff --git a/lsan/include/sanitizer_common/sanitizer_report_decorator.h b/lsan/include/sanitizer_common/sanitizer_report_decorator.h
new file mode 100644 (file)
index 0000000..e9be29f
--- /dev/null
@@ -0,0 +1,45 @@
+//===-- sanitizer_report_decorator.h ----------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tags to decorate the sanitizer reports.
+// Currently supported tags:
+//   * None.
+//   * ANSI color sequences.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_REPORT_DECORATOR_H
+#define SANITIZER_REPORT_DECORATOR_H
+
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+class SanitizerCommonDecorator {
+  // FIXME: This is not portable. It assumes the special strings are printed to
+  // stdout, which is not the case on Windows (see SetConsoleTextAttribute()).
+ public:
+  SanitizerCommonDecorator() : ansi_(ColorizeReports()) {}
+  const char *Bold()    const { return ansi_ ? "\033[1m" : ""; }
+  const char *Default() const { return ansi_ ? "\033[1m\033[0m"  : ""; }
+  const char *Warning()    { return Red(); }
+  const char *EndWarning() { return Default(); }
+ protected:
+  const char *Black()   const { return ansi_ ? "\033[1m\033[30m" : ""; }
+  const char *Red()     const { return ansi_ ? "\033[1m\033[31m" : ""; }
+  const char *Green()   const { return ansi_ ? "\033[1m\033[32m" : ""; }
+  const char *Yellow()  const { return ansi_ ? "\033[1m\033[33m" : ""; }
+  const char *Blue()    const { return ansi_ ? "\033[1m\033[34m" : ""; }
+  const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; }
+  const char *Cyan()    const { return ansi_ ? "\033[1m\033[36m" : ""; }
+  const char *White()   const { return ansi_ ? "\033[1m\033[37m" : ""; }
+ private:
+  bool ansi_;
+};
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_REPORT_DECORATOR_H
diff --git a/lsan/include/sanitizer_common/sanitizer_stackdepot.h b/lsan/include/sanitizer_common/sanitizer_stackdepot.h
new file mode 100644 (file)
index 0000000..dfb5349
--- /dev/null
@@ -0,0 +1,70 @@
+//===-- sanitizer_stackdepot.h ----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_STACKDEPOT_H
+#define SANITIZER_STACKDEPOT_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_stacktrace.h"
+
+namespace __sanitizer {
+
+// StackDepot efficiently stores huge amounts of stack traces.
+struct StackDepotNode;
+struct StackDepotHandle {
+  StackDepotNode *node_;
+  StackDepotHandle() : node_(nullptr) {}
+  explicit StackDepotHandle(StackDepotNode *node) : node_(node) {}
+  bool valid() { return node_; }
+  u32 id();
+  int use_count();
+  void inc_use_count_unsafe();
+};
+
+const int kStackDepotMaxUseCount = 1U << 20;
+
+StackDepotStats *StackDepotGetStats();
+u32 StackDepotPut(StackTrace stack);
+StackDepotHandle StackDepotPut_WithHandle(StackTrace stack);
+// Retrieves a stored stack trace by the id.
+StackTrace StackDepotGet(u32 id);
+
+void StackDepotLockAll();
+void StackDepotUnlockAll();
+
+// Instantiating this class creates a snapshot of StackDepot which can be
+// efficiently queried with StackDepotGet(). You can use it concurrently with
+// StackDepot, but the snapshot is only guaranteed to contain those stack traces
+// which were stored before it was instantiated.
+class StackDepotReverseMap {
+ public:
+  StackDepotReverseMap();
+  StackTrace Get(u32 id);
+
+ private:
+  struct IdDescPair {
+    u32 id;
+    StackDepotNode *desc;
+
+    static bool IdComparator(const IdDescPair &a, const IdDescPair &b);
+  };
+
+  InternalMmapVector<IdDescPair> map_;
+
+  // Disallow evil constructors.
+  StackDepotReverseMap(const StackDepotReverseMap&);
+  void operator=(const StackDepotReverseMap&);
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKDEPOT_H
diff --git a/lsan/include/sanitizer_common/sanitizer_stackdepotbase.h b/lsan/include/sanitizer_common/sanitizer_stackdepotbase.h
new file mode 100644 (file)
index 0000000..ab49328
--- /dev/null
@@ -0,0 +1,176 @@
+//===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of a mapping from arbitrary values to unique 32-bit
+// identifiers.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_STACKDEPOTBASE_H
+#define SANITIZER_STACKDEPOTBASE_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_persistent_allocator.h"
+
+namespace __sanitizer {
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+class StackDepotBase {
+ public:
+  typedef typename Node::args_type args_type;
+  typedef typename Node::handle_type handle_type;
+  // Maps stack trace to an unique id.
+  handle_type Put(args_type args, bool *inserted = nullptr);
+  // Retrieves a stored stack trace by the id.
+  args_type Get(u32 id);
+
+  StackDepotStats *GetStats() { return &stats; }
+
+  void LockAll();
+  void UnlockAll();
+
+ private:
+  static Node *find(Node *s, args_type args, u32 hash);
+  static Node *lock(atomic_uintptr_t *p);
+  static void unlock(atomic_uintptr_t *p, Node *s);
+
+  static const int kTabSize = 1 << kTabSizeLog;  // Hash table size.
+  static const int kPartBits = 8;
+  static const int kPartShift = sizeof(u32) * 8 - kPartBits - kReservedBits;
+  static const int kPartCount =
+      1 << kPartBits;  // Number of subparts in the table.
+  static const int kPartSize = kTabSize / kPartCount;
+  static const int kMaxId = 1 << kPartShift;
+
+  atomic_uintptr_t tab[kTabSize];   // Hash table of Node's.
+  atomic_uint32_t seq[kPartCount];  // Unique id generators.
+
+  StackDepotStats stats;
+
+  friend class StackDepotReverseMap;
+};
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(Node *s,
+                                                             args_type args,
+                                                             u32 hash) {
+  // Searches linked list s for the stack, returns its id.
+  for (; s; s = s->link) {
+    if (s->eq(hash, args)) {
+      return s;
+    }
+  }
+  return nullptr;
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::lock(
+    atomic_uintptr_t *p) {
+  // Uses the pointer lsb as mutex.
+  for (int i = 0;; i++) {
+    uptr cmp = atomic_load(p, memory_order_relaxed);
+    if ((cmp & 1) == 0 &&
+        atomic_compare_exchange_weak(p, &cmp, cmp | 1, memory_order_acquire))
+      return (Node *)cmp;
+    if (i < 10)
+      proc_yield(10);
+    else
+      internal_sched_yield();
+  }
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::unlock(
+    atomic_uintptr_t *p, Node *s) {
+  DCHECK_EQ((uptr)s & 1, 0);
+  atomic_store(p, (uptr)s, memory_order_release);
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::handle_type
+StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
+                                                      bool *inserted) {
+  if (inserted) *inserted = false;
+  if (!Node::is_valid(args)) return handle_type();
+  uptr h = Node::hash(args);
+  atomic_uintptr_t *p = &tab[h % kTabSize];
+  uptr v = atomic_load(p, memory_order_consume);
+  Node *s = (Node *)(v & ~1);
+  // First, try to find the existing stack.
+  Node *node = find(s, args, h);
+  if (node) return node->get_handle();
+  // If failed, lock, retry and insert new.
+  Node *s2 = lock(p);
+  if (s2 != s) {
+    node = find(s2, args, h);
+    if (node) {
+      unlock(p, s2);
+      return node->get_handle();
+    }
+  }
+  uptr part = (h % kTabSize) / kPartSize;
+  u32 id = atomic_fetch_add(&seq[part], 1, memory_order_relaxed) + 1;
+  stats.n_uniq_ids++;
+  CHECK_LT(id, kMaxId);
+  id |= part << kPartShift;
+  CHECK_NE(id, 0);
+  CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
+  uptr memsz = Node::storage_size(args);
+  s = (Node *)PersistentAlloc(memsz);
+  stats.allocated += memsz;
+  s->id = id;
+  s->store(args, h);
+  s->link = s2;
+  unlock(p, s);
+  if (inserted) *inserted = true;
+  return s->get_handle();
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::args_type
+StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
+  if (id == 0) {
+    return args_type();
+  }
+  CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
+  // High kPartBits contain part id, so we need to scan at most kPartSize lists.
+  uptr part = id >> kPartShift;
+  for (int i = 0; i != kPartSize; i++) {
+    uptr idx = part * kPartSize + i;
+    CHECK_LT(idx, kTabSize);
+    atomic_uintptr_t *p = &tab[idx];
+    uptr v = atomic_load(p, memory_order_consume);
+    Node *s = (Node *)(v & ~1);
+    for (; s; s = s->link) {
+      if (s->id == id) {
+        return s->load();
+      }
+    }
+  }
+  return args_type();
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
+  for (int i = 0; i < kTabSize; ++i) {
+    lock(&tab[i]);
+  }
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
+  for (int i = 0; i < kTabSize; ++i) {
+    atomic_uintptr_t *p = &tab[i];
+    uptr s = atomic_load(p, memory_order_relaxed);
+    unlock(p, (Node *)(s & ~1UL));
+  }
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKDEPOTBASE_H
diff --git a/lsan/include/sanitizer_common/sanitizer_stacktrace.h b/lsan/include/sanitizer_common/sanitizer_stacktrace.h
new file mode 100644 (file)
index 0000000..7f22455
--- /dev/null
@@ -0,0 +1,134 @@
+//===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_STACKTRACE_H
+#define SANITIZER_STACKTRACE_H
+
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+static const u32 kStackTraceMax = 256;
+
+#if SANITIZER_LINUX &&  (defined(__sparc__) || defined(__mips__))
+# define SANITIZER_CAN_FAST_UNWIND 0
+#elif SANITIZER_WINDOWS
+# define SANITIZER_CAN_FAST_UNWIND 0
+#else
+# define SANITIZER_CAN_FAST_UNWIND 1
+#endif
+
+// Fast unwind is the only option on Mac for now; we will need to
+// revisit this macro when slow unwind works on Mac, see
+// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+#if SANITIZER_MAC
+# define SANITIZER_CAN_SLOW_UNWIND 0
+#else
+# define SANITIZER_CAN_SLOW_UNWIND 1
+#endif
+
+struct StackTrace {
+  const uptr *trace;
+  u32 size;
+  u32 tag;
+
+  static const int TAG_UNKNOWN = 0;
+  static const int TAG_ALLOC = 1;
+  static const int TAG_DEALLOC = 2;
+  static const int TAG_CUSTOM = 100; // Tool specific tags start here.
+
+  StackTrace() : trace(nullptr), size(0), tag(0) {}
+  StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {}
+  StackTrace(const uptr *trace, u32 size, u32 tag)
+      : trace(trace), size(size), tag(tag) {}
+
+  // Prints a symbolized stacktrace, followed by an empty line.
+  void Print() const;
+
+  static bool WillUseFastUnwind(bool request_fast_unwind) {
+    if (!SANITIZER_CAN_FAST_UNWIND)
+      return false;
+    else if (!SANITIZER_CAN_SLOW_UNWIND)
+      return true;
+    return request_fast_unwind;
+  }
+
+  static uptr GetCurrentPc();
+  static inline uptr GetPreviousInstructionPc(uptr pc);
+  static uptr GetNextInstructionPc(uptr pc);
+  typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
+                                    int out_size);
+};
+
+// Performance-critical, must be in the header.
+ALWAYS_INLINE
+uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
+#if defined(__arm__)
+  // Cancel Thumb bit.
+  pc = pc & (~1);
+#endif
+#if defined(__powerpc__) || defined(__powerpc64__)
+  // PCs are always 4 byte aligned.
+  return pc - 4;
+#elif defined(__sparc__) || defined(__mips__)
+  return pc - 8;
+#else
+  return pc - 1;
+#endif
+}
+
+// StackTrace that owns the buffer used to store the addresses.
+struct BufferedStackTrace : public StackTrace {
+  uptr trace_buffer[kStackTraceMax];
+  uptr top_frame_bp;  // Optional bp of a top frame.
+
+  BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
+
+  void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
+  void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
+              uptr stack_bottom, bool request_fast_unwind);
+
+ private:
+  void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
+                       u32 max_depth);
+  void SlowUnwindStack(uptr pc, u32 max_depth);
+  void SlowUnwindStackWithContext(uptr pc, void *context,
+                                  u32 max_depth);
+  void PopStackFrames(uptr count);
+  uptr LocatePcInTrace(uptr pc);
+
+  BufferedStackTrace(const BufferedStackTrace &);
+  void operator=(const BufferedStackTrace &);
+};
+
+}  // namespace __sanitizer
+
+// Use this macro if you want to print stack trace with the caller
+// of the current function in the top frame.
+#define GET_CALLER_PC_BP_SP \
+  uptr bp = GET_CURRENT_FRAME();              \
+  uptr pc = GET_CALLER_PC();                  \
+  uptr local_stack;                           \
+  uptr sp = (uptr)&local_stack
+
+#define GET_CALLER_PC_BP \
+  uptr bp = GET_CURRENT_FRAME();              \
+  uptr pc = GET_CALLER_PC();
+
+// Use this macro if you want to print stack trace with the current
+// function in the top frame.
+#define GET_CURRENT_PC_BP_SP \
+  uptr bp = GET_CURRENT_FRAME();              \
+  uptr pc = StackTrace::GetCurrentPc();   \
+  uptr local_stack;                           \
+  uptr sp = (uptr)&local_stack
+
+
+#endif  // SANITIZER_STACKTRACE_H
diff --git a/lsan/include/sanitizer_common/sanitizer_stacktrace_printer.h b/lsan/include/sanitizer_common/sanitizer_stacktrace_printer.h
new file mode 100644 (file)
index 0000000..a553568
--- /dev/null
@@ -0,0 +1,62 @@
+//===-- sanitizer_stacktrace_printer.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizers' run-time libraries.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_STACKTRACE_PRINTER_H
+#define SANITIZER_STACKTRACE_PRINTER_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Render the contents of "info" structure, which represents the contents of
+// stack frame "frame_no" and appends it to the "buffer". "format" is a
+// string with placeholders, which is copied to the output with
+// placeholders substituted with the contents of "info". For example,
+// format string
+//   "  frame %n: function %F at %S"
+// will be turned into
+//   "  frame 10: function foo::bar() at my/file.cc:10"
+// You may additionally pass "strip_path_prefix" to strip prefixes of paths to
+// source files and modules, and "strip_func_prefix" to strip prefixes of
+// function names.
+// Here's the full list of available placeholders:
+//   %% - represents a '%' character;
+//   %n - frame number (copy of frame_no);
+//   %p - PC in hex format;
+//   %m - path to module (binary or shared object);
+//   %o - offset in the module in hex format;
+//   %f - function name;
+//   %q - offset in the function in hex format (*if available*);
+//   %s - path to source file;
+//   %l - line in the source file;
+//   %c - column in the source file;
+//   %F - if function is known to be <foo>, prints "in <foo>", possibly
+//        followed by the offset in this function, but only if source file
+//        is unknown;
+//   %S - prints file/line/column information;
+//   %L - prints location information: file/line/column, if it is known, or
+//        module+offset if it is known, or (<unknown module>) string.
+//   %M - prints module basename and offset, if it is known, or PC.
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+                 const AddressInfo &info, bool vs_style,
+                 const char *strip_path_prefix = "",
+                 const char *strip_func_prefix = "");
+
+void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+                          int line, int column, bool vs_style,
+                          const char *strip_path_prefix);
+
+void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+                          uptr offset, const char *strip_path_prefix);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_STACKTRACE_PRINTER_H
diff --git a/lsan/include/sanitizer_common/sanitizer_stoptheworld.h b/lsan/include/sanitizer_common/sanitizer_stoptheworld.h
new file mode 100644 (file)
index 0000000..c324526
--- /dev/null
@@ -0,0 +1,67 @@
+//===-- sanitizer_stoptheworld.h --------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the StopTheWorld function which suspends the execution of the current
+// process and runs the user-supplied callback in the same address space.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_STOPTHEWORLD_H
+#define SANITIZER_STOPTHEWORLD_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+typedef int SuspendedThreadID;
+
+// Holds the list of suspended threads and provides an interface to dump their
+// register contexts.
+class SuspendedThreadsList {
+ public:
+  SuspendedThreadsList()
+    : thread_ids_(1024) {}
+  SuspendedThreadID GetThreadID(uptr index) const {
+    CHECK_LT(index, thread_ids_.size());
+    return thread_ids_[index];
+  }
+  int GetRegistersAndSP(uptr index, uptr *buffer, uptr *sp) const;
+  // The buffer in GetRegistersAndSP should be at least this big.
+  static uptr RegisterCount();
+  uptr thread_count() const { return thread_ids_.size(); }
+  bool Contains(SuspendedThreadID thread_id) const {
+    for (uptr i = 0; i < thread_ids_.size(); i++) {
+      if (thread_ids_[i] == thread_id)
+        return true;
+    }
+    return false;
+  }
+  void Append(SuspendedThreadID thread_id) {
+    thread_ids_.push_back(thread_id);
+  }
+
+ private:
+  InternalMmapVector<SuspendedThreadID> thread_ids_;
+
+  // Prohibit copy and assign.
+  SuspendedThreadsList(const SuspendedThreadsList&);
+  void operator=(const SuspendedThreadsList&);
+};
+
+typedef void (*StopTheWorldCallback)(
+    const SuspendedThreadsList &suspended_threads_list,
+    void *argument);
+
+// Suspend all threads in the current process and run the callback on the list
+// of suspended threads. This function will resume the threads before returning.
+// The callback should not call any libc functions. The callback must not call
+// exit() nor _exit() and instead return to the caller.
+// This function should NOT be called from multiple threads simultaneously.
+void StopTheWorld(StopTheWorldCallback callback, void *argument);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_STOPTHEWORLD_H
diff --git a/lsan/include/sanitizer_common/sanitizer_suppressions.h b/lsan/include/sanitizer_common/sanitizer_suppressions.h
new file mode 100644 (file)
index 0000000..efec926
--- /dev/null
@@ -0,0 +1,54 @@
+//===-- sanitizer_suppressions.h --------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Suppression parsing/matching code.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SUPPRESSIONS_H
+#define SANITIZER_SUPPRESSIONS_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+struct Suppression {
+  const char *type;
+  char *templ;
+  atomic_uint32_t hit_count;
+  uptr weight;
+};
+
+class SuppressionContext {
+ public:
+  // Create new SuppressionContext capable of parsing given suppression types.
+  SuppressionContext(const char *supprression_types[],
+                     int suppression_types_num);
+
+  void ParseFromFile(const char *filename);
+  void Parse(const char *str);
+
+  bool Match(const char *str, const char *type, Suppression **s);
+  uptr SuppressionCount() const;
+  bool HasSuppressionType(const char *type) const;
+  const Suppression *SuppressionAt(uptr i) const;
+  void GetMatched(InternalMmapVector<Suppression *> *matched);
+
+ private:
+  static const int kMaxSuppressionTypes = 16;
+  const char **const suppression_types_;
+  const int suppression_types_num_;
+
+  InternalMmapVector<Suppression> suppressions_;
+  bool has_suppression_type_[kMaxSuppressionTypes];
+  bool can_parse_;
+};
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_SUPPRESSIONS_H
diff --git a/lsan/include/sanitizer_common/sanitizer_symbolizer.h b/lsan/include/sanitizer_common/sanitizer_symbolizer.h
new file mode 100644 (file)
index 0000000..0a443a7
--- /dev/null
@@ -0,0 +1,178 @@
+//===-- sanitizer_symbolizer.h ----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Symbolizer is used by sanitizers to map instruction address to a location in
+// source code at run-time. Symbolizer either uses __sanitizer_symbolize_*
+// defined in the program, or (if they are missing) tries to find and
+// launch "llvm-symbolizer" commandline tool in a separate process and
+// communicate with it.
+//
+// Generally we should try to avoid calling system library functions during
+// symbolization (and use their replacements from sanitizer_libc.h instead).
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_H
+#define SANITIZER_SYMBOLIZER_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_mutex.h"
+
+namespace __sanitizer {
+
+struct AddressInfo {
+  // Owns all the string members. Storage for them is
+  // (de)allocated using sanitizer internal allocator.
+  uptr address;
+
+  char *module;
+  uptr module_offset;
+
+  static const uptr kUnknown = ~(uptr)0;
+  char *function;
+  uptr function_offset;
+
+  char *file;
+  int line;
+  int column;
+
+  AddressInfo();
+  // Deletes all strings and resets all fields.
+  void Clear();
+  void FillModuleInfo(const char *mod_name, uptr mod_offset);
+};
+
+// Linked list of symbolized frames (each frame is described by AddressInfo).
+struct SymbolizedStack {
+  SymbolizedStack *next;
+  AddressInfo info;
+  static SymbolizedStack *New(uptr addr);
+  // Deletes current, and all subsequent frames in the linked list.
+  // The object cannot be accessed after the call to this function.
+  void ClearAll();
+
+ private:
+  SymbolizedStack();
+};
+
+// For now, DataInfo is used to describe global variable.
+struct DataInfo {
+  // Owns all the string members. Storage for them is
+  // (de)allocated using sanitizer internal allocator.
+  char *module;
+  uptr module_offset;
+  char *name;
+  uptr start;
+  uptr size;
+
+  DataInfo();
+  void Clear();
+};
+
+class SymbolizerTool;
+
+class Symbolizer final {
+ public:
+  /// Initialize and return platform-specific implementation of symbolizer
+  /// (if it wasn't already initialized).
+  static Symbolizer *GetOrInit();
+  // Returns a list of symbolized frames for a given address (containing
+  // all inlined functions, if necessary).
+  SymbolizedStack *SymbolizePC(uptr address);
+  bool SymbolizeData(uptr address, DataInfo *info);
+
+  // The module names Symbolizer returns are stable and unique for every given
+  // module.  It is safe to store and compare them as pointers.
+  bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+                                   uptr *module_address);
+  const char *GetModuleNameForPc(uptr pc) {
+    const char *module_name = nullptr;
+    uptr unused;
+    if (GetModuleNameAndOffsetForPC(pc, &module_name, &unused))
+      return module_name;
+    return nullptr;
+  }
+
+  // Release internal caches (if any).
+  void Flush();
+  // Attempts to demangle the provided C++ mangled name.
+  const char *Demangle(const char *name);
+  void PrepareForSandboxing();
+
+  // Allow user to install hooks that would be called before/after Symbolizer
+  // does the actual file/line info fetching. Specific sanitizers may need this
+  // to distinguish system library calls made in user code from calls made
+  // during in-process symbolization.
+  typedef void (*StartSymbolizationHook)();
+  typedef void (*EndSymbolizationHook)();
+  // May be called at most once.
+  void AddHooks(StartSymbolizationHook start_hook,
+                EndSymbolizationHook end_hook);
+
+ private:
+  // GetModuleNameAndOffsetForPC has to return a string to the caller.
+  // Since the corresponding module might get unloaded later, we should create
+  // our owned copies of the strings that we can safely return.
+  // ModuleNameOwner does not provide any synchronization, thus calls to
+  // its method should be protected by |mu_|.
+  class ModuleNameOwner {
+   public:
+    explicit ModuleNameOwner(BlockingMutex *synchronized_by)
+        : storage_(kInitialCapacity), last_match_(nullptr),
+          mu_(synchronized_by) {}
+    const char *GetOwnedCopy(const char *str);
+
+   private:
+    static const uptr kInitialCapacity = 1000;
+    InternalMmapVector<const char*> storage_;
+    const char *last_match_;
+
+    BlockingMutex *mu_;
+  } module_names_;
+
+  /// Platform-specific function for creating a Symbolizer object.
+  static Symbolizer *PlatformInit();
+
+  bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
+                                         uptr *module_offset);
+  LoadedModule *FindModuleForAddress(uptr address);
+  LoadedModule modules_[kMaxNumberOfModules];
+  uptr n_modules_;
+  // If stale, need to reload the modules before looking up addresses.
+  bool modules_fresh_;
+
+  // Platform-specific default demangler, must not return nullptr.
+  const char *PlatformDemangle(const char *name);
+  void PlatformPrepareForSandboxing();
+
+  static Symbolizer *symbolizer_;
+  static StaticSpinMutex init_mu_;
+
+  // Mutex locked from public methods of |Symbolizer|, so that the internals
+  // (including individual symbolizer tools and platform-specific methods) are
+  // always synchronized.
+  BlockingMutex mu_;
+
+  typedef IntrusiveList<SymbolizerTool>::Iterator Iterator;
+  IntrusiveList<SymbolizerTool> tools_;
+
+  explicit Symbolizer(IntrusiveList<SymbolizerTool> tools);
+
+  static LowLevelAllocator symbolizer_allocator_;
+
+  StartSymbolizationHook start_hook_;
+  EndSymbolizationHook end_hook_;
+  class SymbolizerScope {
+   public:
+    explicit SymbolizerScope(const Symbolizer *sym);
+    ~SymbolizerScope();
+   private:
+    const Symbolizer *sym_;
+  };
+};
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_SYMBOLIZER_H
diff --git a/lsan/include/sanitizer_common/sanitizer_symbolizer_internal.h b/lsan/include/sanitizer_common/sanitizer_symbolizer_internal.h
new file mode 100644 (file)
index 0000000..a87964b
--- /dev/null
@@ -0,0 +1,149 @@
+//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header for internal classes and functions to be used by implementations of
+// symbolizers.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_INTERNAL_H
+#define SANITIZER_SYMBOLIZER_INTERNAL_H
+
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Parsing helpers, 'str' is searched for delimiter(s) and a string or uptr
+// is extracted. When extracting a string, a newly allocated (using
+// InternalAlloc) and null-terminataed buffer is returned. They return a pointer
+// to the next characted after the found delimiter.
+const char *ExtractToken(const char *str, const char *delims, char **result);
+const char *ExtractInt(const char *str, const char *delims, int *result);
+const char *ExtractUptr(const char *str, const char *delims, uptr *result);
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+                                      char **result);
+
+const char *DemangleCXXABI(const char *name);
+
+// SymbolizerTool is an interface that is implemented by individual "tools"
+// that can perform symbolication (external llvm-symbolizer, libbacktrace,
+// Windows DbgHelp symbolizer, etc.).
+class SymbolizerTool {
+ public:
+  // The main |Symbolizer| class implements a "fallback chain" of symbolizer
+  // tools. In a request to symbolize an address, if one tool returns false,
+  // the next tool in the chain will be tried.
+  SymbolizerTool *next;
+
+  SymbolizerTool() : next(nullptr) { }
+
+  // Can't declare pure virtual functions in sanitizer runtimes:
+  // __cxa_pure_virtual might be unavailable.
+
+  // The |stack| parameter is inout. It is pre-filled with the address,
+  // module base and module offset values and is to be used to construct
+  // other stack frames.
+  virtual bool SymbolizePC(uptr addr, SymbolizedStack *stack) {
+    UNIMPLEMENTED();
+  }
+
+  // The |info| parameter is inout. It is pre-filled with the module base
+  // and module offset values.
+  virtual bool SymbolizeData(uptr addr, DataInfo *info) {
+    UNIMPLEMENTED();
+  }
+
+  virtual void Flush() {}
+
+  // Return nullptr to fallback to the default platform-specific demangler.
+  virtual const char *Demangle(const char *name) {
+    return nullptr;
+  }
+};
+
+// SymbolizerProcess encapsulates communication between the tool and
+// external symbolizer program, running in a different subprocess.
+// SymbolizerProcess may not be used from two threads simultaneously.
+class SymbolizerProcess {
+ public:
+  explicit SymbolizerProcess(const char *path, bool use_forkpty = false);
+  const char *SendCommand(const char *command);
+
+ protected:
+  virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
+    UNIMPLEMENTED();
+  }
+
+  /// The maximum number of arguments required to invoke a tool process.
+  enum { kArgVMax = 6 };
+
+  /// Fill in an argv array to invoke the child process.
+  virtual void GetArgV(const char *path_to_binary,
+                       const char *(&argv)[kArgVMax]) const {
+    UNIMPLEMENTED();
+  }
+
+  virtual bool ReadFromSymbolizer(char *buffer, uptr max_length);
+
+ private:
+  bool Restart();
+  const char *SendCommandImpl(const char *command);
+  bool WriteToSymbolizer(const char *buffer, uptr length);
+  bool StartSymbolizerSubprocess();
+
+  const char *path_;
+  fd_t input_fd_;
+  fd_t output_fd_;
+
+  static const uptr kBufferSize = 16 * 1024;
+  char buffer_[kBufferSize];
+
+  static const uptr kMaxTimesRestarted = 5;
+  static const int kSymbolizerStartupTimeMillis = 10;
+  uptr times_restarted_;
+  bool failed_to_start_;
+  bool reported_invalid_path_;
+  bool use_forkpty_;
+};
+
+class LLVMSymbolizerProcess;
+
+// This tool invokes llvm-symbolizer in a subprocess. It should be as portable
+// as the llvm-symbolizer tool is.
+class LLVMSymbolizer : public SymbolizerTool {
+ public:
+  explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+
+  bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+  const char *SendCommand(bool is_data, const char *module_name,
+                          uptr module_offset);
+
+  LLVMSymbolizerProcess *symbolizer_process_;
+  static const uptr kBufferSize = 16 * 1024;
+  char buffer_[kBufferSize];
+};
+
+// Parses one or more two-line strings in the following format:
+//   <function_name>
+//   <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format.  Returns true if any useful debug
+// information was found.
+void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res);
+
+// Parses a two-line string in the following format:
+//   <symbol_name>
+//   <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+void ParseSymbolizeDataOutput(const char *str, DataInfo *info);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_SYMBOLIZER_INTERNAL_H
diff --git a/lsan/include/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/lsan/include/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
new file mode 100644 (file)
index 0000000..ab1d6f9
--- /dev/null
@@ -0,0 +1,48 @@
+//===-- sanitizer_symbolizer_libbacktrace.h ---------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Header for libbacktrace symbolizer.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_LIBBACKTRACE_H
+#define SANITIZER_SYMBOLIZER_LIBBACKTRACE_H
+
+#include "sanitizer_platform.h"
+#include "sanitizer_common.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_symbolizer_internal.h"
+
+#ifndef SANITIZER_LIBBACKTRACE
+# define SANITIZER_LIBBACKTRACE 0
+#endif
+
+#ifndef SANITIZER_CP_DEMANGLE
+# define SANITIZER_CP_DEMANGLE 0
+#endif
+
+namespace __sanitizer {
+
+class LibbacktraceSymbolizer : public SymbolizerTool {
+ public:
+  static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc);
+
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+
+  bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+  // May return NULL if demangling failed.
+  const char *Demangle(const char *name) override;
+
+ private:
+  explicit LibbacktraceSymbolizer(void *state) : state_(state) {}
+
+  void *state_;  // Leaked.
+};
+
+}  // namespace __sanitizer
+#endif  // SANITIZER_SYMBOLIZER_LIBBACKTRACE_H
diff --git a/lsan/include/sanitizer_common/sanitizer_symbolizer_mac.h b/lsan/include/sanitizer_common/sanitizer_symbolizer_mac.h
new file mode 100644 (file)
index 0000000..240c538
--- /dev/null
@@ -0,0 +1,46 @@
+//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Header for Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_SYMBOLIZER_MAC_H
+#define SANITIZER_SYMBOLIZER_MAC_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+class DlAddrSymbolizer : public SymbolizerTool {
+ public:
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+  bool SymbolizeData(uptr addr, DataInfo *info) override;
+};
+
+class AtosSymbolizerProcess;
+
+class AtosSymbolizer : public SymbolizerTool {
+ public:
+  explicit AtosSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+  bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+  AtosSymbolizerProcess *process_;
+};
+
+} // namespace __sanitizer
+
+#endif  // SANITIZER_MAC
+
+#endif // SANITIZER_SYMBOLIZER_MAC_H
diff --git a/lsan/include/sanitizer_common/sanitizer_syscall_generic.inc b/lsan/include/sanitizer_common/sanitizer_syscall_generic.inc
new file mode 100644 (file)
index 0000000..3bc50d9
--- /dev/null
@@ -0,0 +1,32 @@
+//===-- sanitizer_syscall_generic.inc ---------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic implementations of internal_syscall and internal_iserror.
+//
+//===----------------------------------------------------------------------===//
+
+#if SANITIZER_FREEBSD || SANITIZER_MAC
+# define SYSCALL(name) SYS_ ## name
+#else
+# define SYSCALL(name) __NR_ ## name
+#endif
+
+#if (SANITIZER_FREEBSD || SANITIZER_MAC) && defined(__x86_64__)
+# define internal_syscall __syscall
+# else
+# define internal_syscall syscall
+#endif
+
+bool internal_iserror(uptr retval, int *rverrno) {
+  if (retval == (uptr)-1) {
+    if (rverrno)
+      *rverrno = errno;
+    return true;
+  } else {
+    return false;
+  }
+}
diff --git a/lsan/include/sanitizer_common/sanitizer_syscall_linux_aarch64.inc b/lsan/include/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
new file mode 100644 (file)
index 0000000..64d6322
--- /dev/null
@@ -0,0 +1,136 @@
+//===-- sanitizer_syscall_linux_aarch64.inc --------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementations of internal_syscall and internal_iserror for Linux/aarch64.
+//
+//===----------------------------------------------------------------------===//
+
+#define SYSCALL(name) __NR_ ## name
+
+static uptr __internal_syscall(u64 nr) {
+  register u64 x8 asm("x8") = nr;
+  register u64 x0 asm("x0");
+  asm volatile("svc 0"
+               : "=r"(x0)
+               : "r"(x8)
+               : "memory", "cc");
+  return x0;
+}
+#define __internal_syscall0(n) \
+  (__internal_syscall)(n)
+
+static uptr __internal_syscall(u64 nr, u64 arg1) {
+  register u64 x8 asm("x8") = nr;
+  register u64 x0 asm("x0") = arg1;
+  asm volatile("svc 0"
+               : "=r"(x0)
+               : "r"(x8), "0"(x0)
+               : "memory", "cc");
+  return x0;
+}
+#define __internal_syscall1(n, a1) \
+  (__internal_syscall)(n, (u64)(a1))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2) {
+  register u64 x8 asm("x8") = nr;
+  register u64 x0 asm("x0") = arg1;
+  register u64 x1 asm("x1") = arg2;
+  asm volatile("svc 0"
+               : "=r"(x0)
+               : "r"(x8), "0"(x0), "r"(x1)
+               : "memory", "cc");
+  return x0;
+}
+#define __internal_syscall2(n, a1, a2) \
+  (__internal_syscall)(n, (u64)(a1), (long)(a2))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3) {
+  register u64 x8 asm("x8") = nr;
+  register u64 x0 asm("x0") = arg1;
+  register u64 x1 asm("x1") = arg2;
+  register u64 x2 asm("x2") = arg3;
+  asm volatile("svc 0"
+               : "=r"(x0)
+               : "r"(x8), "0"(x0), "r"(x1), "r"(x2)
+               : "memory", "cc");
+  return x0;
+}
+#define __internal_syscall3(n, a1, a2, a3) \
+  (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+                               u64 arg4) {
+  register u64 x8 asm("x8") = nr;
+  register u64 x0 asm("x0") = arg1;
+  register u64 x1 asm("x1") = arg2;
+  register u64 x2 asm("x2") = arg3;
+  register u64 x3 asm("x3") = arg4;
+  asm volatile("svc 0"
+               : "=r"(x0)
+               : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3)
+               : "memory", "cc");
+  return x0;
+}
+#define __internal_syscall4(n, a1, a2, a3, a4) \
+  (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+                               u64 arg4, long arg5) {
+  register u64 x8 asm("x8") = nr;
+  register u64 x0 asm("x0") = arg1;
+  register u64 x1 asm("x1") = arg2;
+  register u64 x2 asm("x2") = arg3;
+  register u64 x3 asm("x3") = arg4;
+  register u64 x4 asm("x4") = arg5;
+  asm volatile("svc 0"
+               : "=r"(x0)
+               : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4)
+               : "memory", "cc");
+  return x0;
+}
+#define __internal_syscall5(n, a1, a2, a3, a4, a5) \
+  (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
+                       (u64)(a5))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+                               u64 arg4, long arg5, long arg6) {
+  register u64 x8 asm("x8") = nr;
+  register u64 x0 asm("x0") = arg1;
+  register u64 x1 asm("x1") = arg2;
+  register u64 x2 asm("x2") = arg3;
+  register u64 x3 asm("x3") = arg4;
+  register u64 x4 asm("x4") = arg5;
+  register u64 x5 asm("x5") = arg6;
+  asm volatile("svc 0"
+               : "=r"(x0)
+               : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)
+               : "memory", "cc");
+  return x0;
+}
+#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \
+  (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
+                       (u64)(a5), (long)(a6))
+
+#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n
+#define __SYSCALL_NARGS(...) \
+  __SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, )
+#define __SYSCALL_CONCAT_X(a, b) a##b
+#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b)
+#define __SYSCALL_DISP(b, ...) \
+  __SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
+
+#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
+
+// Helper function used to avoid cobbler errno.
+bool internal_iserror(uptr retval, int *rverrno) {
+  if (retval >= (uptr)-4095) {
+    if (rverrno)
+      *rverrno = -retval;
+    return true;
+  }
+  return false;
+}
diff --git a/lsan/include/sanitizer_common/sanitizer_syscall_linux_x86_64.inc b/lsan/include/sanitizer_common/sanitizer_syscall_linux_x86_64.inc
new file mode 100644 (file)
index 0000000..b610d66
--- /dev/null
@@ -0,0 +1,89 @@
+//===-- sanitizer_syscall_linux_x86_64.inc ----------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementations of internal_syscall and internal_iserror for Linux/x86_64.
+//
+//===----------------------------------------------------------------------===//
+
+#define SYSCALL(name) __NR_ ## name
+
+static uptr internal_syscall(u64 nr) {
+  u64 retval;
+  asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11",
+               "memory", "cc");
+  return retval;
+}
+
+template <typename T1>
+static uptr internal_syscall(u64 nr, T1 arg1) {
+  u64 retval;
+  asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1) :
+               "rcx", "r11", "memory", "cc");
+  return retval;
+}
+
+template <typename T1, typename T2>
+static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2) {
+  u64 retval;
+  asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1),
+               "S"((u64)arg2) : "rcx", "r11", "memory", "cc");
+  return retval;
+}
+
+template <typename T1, typename T2, typename T3>
+static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3) {
+  u64 retval;
+  asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1),
+               "S"((u64)arg2), "d"((u64)arg3) : "rcx", "r11", "memory", "cc");
+  return retval;
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
+  u64 retval;
+  asm volatile("mov %5, %%r10;"
+               "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1),
+               "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4) :
+               "rcx", "r11", "r10", "memory", "cc");
+  return retval;
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4,
+                             T5 arg5) {
+  u64 retval;
+  asm volatile("mov %5, %%r10;"
+               "mov %6, %%r8;"
+               "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1),
+               "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5) :
+               "rcx", "r11", "r10", "r8", "memory", "cc");
+  return retval;
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6>
+static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4,
+                             T5 arg5, T6 arg6) {
+  u64 retval;
+  asm volatile("mov %5, %%r10;"
+               "mov %6, %%r8;"
+               "mov %7, %%r9;"
+               "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1),
+               "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5),
+               "r"((u64)arg6) : "rcx", "r11", "r10", "r8", "r9",
+               "memory", "cc");
+  return retval;
+}
+
+bool internal_iserror(uptr retval, int *rverrno) {
+  if (retval >= (uptr)-4095) {
+    if (rverrno)
+      *rverrno = -retval;
+    return true;
+  }
+  return false;
+}
diff --git a/lsan/include/sanitizer_common/sanitizer_thread_registry.h b/lsan/include/sanitizer_common/sanitizer_thread_registry.h
new file mode 100644 (file)
index 0000000..dc7399e
--- /dev/null
@@ -0,0 +1,148 @@
+//===-- sanitizer_thread_registry.h -----------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizer tools.
+//
+// General thread bookkeeping functionality.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_THREAD_REGISTRY_H
+#define SANITIZER_THREAD_REGISTRY_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_list.h"
+#include "sanitizer_mutex.h"
+
+namespace __sanitizer {
+
+enum ThreadStatus {
+  ThreadStatusInvalid,   // Non-existent thread, data is invalid.
+  ThreadStatusCreated,   // Created but not yet running.
+  ThreadStatusRunning,   // The thread is currently running.
+  ThreadStatusFinished,  // Joinable thread is finished but not yet joined.
+  ThreadStatusDead       // Joined, but some info is still available.
+};
+
+// Generic thread context. Specific sanitizer tools may inherit from it.
+// If thread is dead, context may optionally be reused for a new thread.
+class ThreadContextBase {
+ public:
+  explicit ThreadContextBase(u32 tid);
+  ~ThreadContextBase();  // Should never be called.
+
+  const u32 tid;  // Thread ID. Main thread should have tid = 0.
+  u64 unique_id;  // Unique thread ID.
+  u32 reuse_count;  // Number of times this tid was reused.
+  uptr os_id;     // PID (used for reporting).
+  uptr user_id;   // Some opaque user thread id (e.g. pthread_t).
+  char name[64];  // As annotated by user.
+
+  ThreadStatus status;
+  bool detached;
+
+  u32 parent_tid;
+  ThreadContextBase *next;  // For storing thread contexts in a list.
+
+  void SetName(const char *new_name);
+
+  void SetDead();
+  void SetJoined(void *arg);
+  void SetFinished();
+  void SetStarted(uptr _os_id, void *arg);
+  void SetCreated(uptr _user_id, u64 _unique_id, bool _detached,
+                  u32 _parent_tid, void *arg);
+  void Reset();
+
+  // The following methods may be overriden by subclasses.
+  // Some of them take opaque arg that may be optionally be used
+  // by subclasses.
+  virtual void OnDead() {}
+  virtual void OnJoined(void *arg) {}
+  virtual void OnFinished() {}
+  virtual void OnStarted(void *arg) {}
+  virtual void OnCreated(void *arg) {}
+  virtual void OnReset() {}
+  virtual void OnDetached(void *arg) {}
+};
+
+typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid);
+
+class ThreadRegistry {
+ public:
+  static const u32 kUnknownTid;
+
+  ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
+                 u32 thread_quarantine_size, u32 max_reuse = 0);
+  void GetNumberOfThreads(uptr *total = nullptr, uptr *running = nullptr,
+                          uptr *alive = nullptr);
+  uptr GetMaxAliveThreads();
+
+  void Lock() { mtx_.Lock(); }
+  void CheckLocked() { mtx_.CheckLocked(); }
+  void Unlock() { mtx_.Unlock(); }
+
+  // Should be guarded by ThreadRegistryLock.
+  ThreadContextBase *GetThreadLocked(u32 tid) {
+    DCHECK_LT(tid, n_contexts_);
+    return threads_[tid];
+  }
+
+  u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg);
+
+  typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg);
+  // Invokes callback with a specified arg for each thread context.
+  // Should be guarded by ThreadRegistryLock.
+  void RunCallbackForEachThreadLocked(ThreadCallback cb, void *arg);
+
+  typedef bool (*FindThreadCallback)(ThreadContextBase *tctx, void *arg);
+  // Finds a thread using the provided callback. Returns kUnknownTid if no
+  // thread is found.
+  u32 FindThread(FindThreadCallback cb, void *arg);
+  // Should be guarded by ThreadRegistryLock. Return 0 if no thread
+  // is found.
+  ThreadContextBase *FindThreadContextLocked(FindThreadCallback cb,
+                                             void *arg);
+  ThreadContextBase *FindThreadContext(FindThreadCallback cb, void *arg);
+  ThreadContextBase *FindThreadContextByOsIDLocked(uptr os_id);
+  ThreadContextBase *FindThreadContextByOsID(uptr os_id);
+
+  void SetThreadName(u32 tid, const char *name);
+  void SetThreadNameByUserId(uptr user_id, const char *name);
+  void DetachThread(u32 tid, void *arg);
+  void JoinThread(u32 tid, void *arg);
+  void FinishThread(u32 tid);
+  void StartThread(u32 tid, uptr os_id, void *arg);
+
+ private:
+  const ThreadContextFactory context_factory_;
+  const u32 max_threads_;
+  const u32 thread_quarantine_size_;
+  const u32 max_reuse_;
+
+  BlockingMutex mtx_;
+
+  u32 n_contexts_;      // Number of created thread contexts,
+                        // at most max_threads_.
+  u64 total_threads_;   // Total number of created threads. May be greater than
+                        // max_threads_ if contexts were reused.
+  uptr alive_threads_;  // Created or running.
+  uptr max_alive_threads_;
+  uptr running_threads_;
+
+  ThreadContextBase **threads_;  // Array of thread contexts is leaked.
+  IntrusiveList<ThreadContextBase> dead_threads_;
+  IntrusiveList<ThreadContextBase> invalid_threads_;
+
+  void QuarantinePush(ThreadContextBase *tctx);
+  ThreadContextBase *QuarantinePop();
+};
+
+typedef GenericScopedLock<ThreadRegistry> ThreadRegistryLock;
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_THREAD_REGISTRY_H
diff --git a/lsan/include/sanitizer_common/sanitizer_tls_get_addr.h b/lsan/include/sanitizer_common/sanitizer_tls_get_addr.h
new file mode 100644 (file)
index 0000000..e4f8c0c
--- /dev/null
@@ -0,0 +1,59 @@
+//===-- sanitizer_tls_get_addr.h --------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle the __tls_get_addr call.
+//
+// All this magic is specific to glibc and is required to workaround
+// the lack of interface that would tell us about the Dynamic TLS (DTLS).
+// https://sourceware.org/bugzilla/show_bug.cgi?id=16291
+//
+// The matters get worse because the glibc implementation changed between
+// 2.18 and 2.19:
+// https://groups.google.com/forum/#!topic/address-sanitizer/BfwYD8HMxTM
+//
+// Before 2.19, every DTLS chunk is allocated with __libc_memalign,
+// which we intercept and thus know where is the DTLS.
+// Since 2.19, DTLS chunks are allocated with __signal_safe_memalign,
+// which is an internal function that wraps a mmap call, neither of which
+// we can intercept. Luckily, __signal_safe_memalign has a simple parseable
+// header which we can use.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_TLS_GET_ADDR_H
+#define SANITIZER_TLS_GET_ADDR_H
+
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+struct DTLS {
+  // Array of DTLS chunks for the current Thread.
+  // If beg == 0, the chunk is unused.
+  struct DTV {
+    uptr beg, size;
+  };
+
+  uptr dtv_size;
+  DTV *dtv;  // dtv_size elements, allocated by MmapOrDie.
+
+  // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cc
+  uptr last_memalign_size;
+  uptr last_memalign_ptr;
+};
+
+// Returns pointer and size of a linker-allocated TLS block.
+// Each block is returned exactly once.
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin,
+                                uptr static_tls_end);
+void DTLS_on_libc_memalign(void *ptr, uptr size);
+DTLS *DTLS_Get();
+void DTLS_Destroy();  // Make sure to call this before the thread is destroyed.
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_TLS_GET_ADDR_H
diff --git a/lsan/src/interception_linux.cc b/lsan/src/interception_linux.cc
new file mode 100644 (file)
index 0000000..4848ee3
--- /dev/null
@@ -0,0 +1,34 @@
+//===-- interception_linux.cc -----------------------------------*- C++ -*-===//
+//
+// 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.
+//===----------------------------------------------------------------------===//
+
+#if defined(__linux__) || defined(__FreeBSD__)
+#include "interception/interception.h"
+
+#include <dlfcn.h>   // for dlsym() and dlvsym()
+
+namespace __interception {
+bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,
+    uptr real, uptr wrapper) {
+  *func_addr = (uptr)dlsym(RTLD_NEXT, func_name);
+  return real == wrapper;
+}
+
+#if !defined(__ANDROID__)  // android does not have dlvsym
+void *GetFuncAddrVer(const char *func_name, const char *ver) {
+  return dlvsym(RTLD_NEXT, func_name, ver);
+}
+#endif  // !defined(__ANDROID__)
+
+}  // namespace __interception
+
+
+#endif  // __linux__ || __FreeBSD__
diff --git a/lsan/src/lsan.cc b/lsan/src/lsan.cc
new file mode 100644 (file)
index 0000000..ee52bc8
--- /dev/null
@@ -0,0 +1,143 @@
+//=-- lsan.cc -------------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Standalone LSan RTL.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan.h"
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "lsan_allocator.h"
+#include "lsan_common.h"
+#include "lsan_thread.h"
+
+bool lsan_inited;
+bool lsan_init_is_running;
+
+namespace __lsan {
+
+///// Interface to the common LSan module. /////
+bool WordIsPoisoned(uptr addr) {
+  return false;
+}
+
+#if LSAN_DYNAMIC
+// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
+// (and thus normal initializers from .preinit_array or modules haven't run).
+
+class LsanInitializer {
+  public:  // NOLINT
+    LsanInitializer() {
+      __lsan_init();
+   }
+};
+
+static LsanInitializer lsan_initializer;
+#endif  // LSAN_DYNAMIC
+
+}  // namespace __lsan
+
+using namespace __lsan;  // NOLINT
+
+static void InitializeFlags() {
+  // Set all the default values.
+  SetCommonFlagsDefaults();
+  {
+    CommonFlags cf;
+    cf.CopyFrom(*common_flags());
+    cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
+    cf.malloc_context_size = 30;
+    cf.detect_leaks = true;
+    cf.exitcode = 0;
+    OverrideCommonFlags(cf);
+  }
+
+  Flags *f = flags();
+  f->SetDefaults();
+
+  FlagParser parser;
+  RegisterLsanFlags(&parser, f);
+  RegisterCommonFlags(&parser);
+
+  // Read from command line.
+  const char *options = GetEnv("LSAN_OPTIONS");
+  // If environment variable LSAN_OPTIONS is not set use predefined file to
+  // init options.
+  bool getFlagsFromFile = options == 0;
+  char *lsanFlags = 0;
+  uptr len = GetPageSizeCached();
+  uptr read_len = 0;
+  if (getFlagsFromFile) {
+    ReadFileToBuffer("/LSAN_OPTIONS", &lsanFlags, &len, &read_len,
+                     GetPageSizeCached());
+    options = lsanFlags;
+  }
+
+  parser.ParseString(options);
+
+  SetVerbosity(common_flags()->verbosity);
+
+  if (Verbosity()) ReportUnrecognizedFlags();
+
+  if (common_flags()->help) parser.PrintFlagDescriptions();
+  __sanitizer_set_report_path("/tmp/lsan.log");
+
+  if (getFlagsFromFile)
+    UnmapOrDie(lsanFlags, len);
+}
+
+extern "C"
+int mount(const char *source, const char *target, const char *filesystemtype,
+          unsigned long mountflags, const void *data);
+
+bool MaybeMountProcFS() {
+  if (FileExists("/proc/self/maps")) return true;
+
+  if (0 != mount("proc", "/proc", "proc", 0, 0)) {
+    Report("Failed to mount /proc, Asan is likely to fail\n");
+    return false;
+  }
+  return true;
+}
+
+extern "C" void __lsan_init() {
+  CHECK(!lsan_init_is_running);
+  if (LIKELY(lsan_inited))
+    return;
+  MaybeMountProcFS();
+  lsan_init_is_running = true;
+  SanitizerToolName = "LeakSanitizer";
+  CacheBinaryName();
+  InitializeFlags();
+  InitCommonLsan();
+  InitializeAllocator();
+  InitTlsSize();
+  InitializeInterceptors();
+  InitializeThreadRegistry();
+  u32 tid = ThreadCreate(0, 0, true);
+  CHECK_EQ(tid, 0);
+  ThreadStart(tid, GetTid());
+  SetCurrentThread(tid);
+
+  if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
+    Atexit(DoLeakCheck);
+
+  InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+
+  lsan_inited = true;
+  lsan_init_is_running = false;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+  GET_STACK_TRACE_FATAL;
+  stack.Print();
+}
diff --git a/lsan/src/lsan_allocator.cc b/lsan/src/lsan_allocator.cc
new file mode 100644 (file)
index 0000000..58f44ad
--- /dev/null
@@ -0,0 +1,271 @@
+//=-- lsan_allocator.cc ---------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// See lsan_allocator.h for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan_allocator.h"
+
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "lsan_common.h"
+
+extern "C" void *memset(void *ptr, int value, uptr num);
+
+namespace __lsan {
+
+struct ChunkMetadata {
+  u8 allocated : 8;  // Must be first.
+  ChunkTag tag : 2;
+  uptr requested_size : 32;
+  uptr padding : 22;
+  u32 stack_trace_id;
+};
+
+#if SANITIZER_WORDSIZE == 64
+#if defined(__mips64) || defined(__aarch64__)
+static const uptr kMaxAllowedMallocSize = 4UL << 30;
+static const uptr kRegionSizeLog = 20;
+#undef SANITIZER_MMAP_RANGE_SIZE
+#define SANITIZER_MMAP_RANGE_SIZE (1UL << 40)
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+typedef CompactSizeClassMap SizeClassMap;
+typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE,
+    sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap>
+    PrimaryAllocator;
+#else
+static const uptr kMaxAllowedMallocSize = 8UL << 30;
+static const uptr kAllocatorSpace = 0x600000000000ULL;
+static const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize,
+        sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator;
+#endif
+#endif
+
+#if defined(__arm__) || defined(__i386__)
+typedef CompactSizeClassMap SizeClassMap;
+static const uptr kRegionSizeLog = 20;
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+typedef FlatByteMap<kNumRegions> ByteMap;
+typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE,
+     sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap>
+     PrimaryAllocator;
+static const uptr kMaxAllowedMallocSize = 1UL << 30;
+#endif
+
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+          SecondaryAllocator> Allocator;
+
+static Allocator allocator;
+static THREADLOCAL AllocatorCache cache;
+
+void InitializeAllocator() {
+  allocator.InitLinkerInitialized(common_flags()->allocator_may_return_null);
+}
+
+void AllocatorThreadFinish() {
+  allocator.SwallowCache(&cache);
+}
+
+static ChunkMetadata *Metadata(const void *p) {
+  return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
+}
+
+static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) {
+  if (!p) return;
+  ChunkMetadata *m = Metadata(p);
+  CHECK(m);
+  m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
+  m->stack_trace_id = StackDepotPut(stack);
+  m->requested_size = size;
+  atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
+}
+
+static void RegisterDeallocation(void *p) {
+  if (!p) return;
+  ChunkMetadata *m = Metadata(p);
+  CHECK(m);
+  atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed);
+}
+
+void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
+               bool cleared) {
+  if (size == 0)
+    size = 1;
+  if (size > kMaxAllowedMallocSize) {
+    Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size);
+    return nullptr;
+  }
+  void *p = allocator.Allocate(&cache, size, alignment, false);
+  // Do not rely on the allocator to clear the memory (it's slow).
+  if (cleared && allocator.FromPrimary(p))
+    memset(p, 0, size);
+  RegisterAllocation(stack, p, size);
+  if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
+  return p;
+}
+
+void Deallocate(void *p) {
+  if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
+  RegisterDeallocation(p);
+  allocator.Deallocate(&cache, p);
+}
+
+void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
+                 uptr alignment) {
+  RegisterDeallocation(p);
+  if (new_size > kMaxAllowedMallocSize) {
+    Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size);
+    allocator.Deallocate(&cache, p);
+    return nullptr;
+  }
+  p = allocator.Reallocate(&cache, p, new_size, alignment);
+  RegisterAllocation(stack, p, new_size);
+  return p;
+}
+
+int PointerIsMine(void *p) {
+  return allocator.PointerIsMine(p);
+}
+
+void GetAllocatorCacheRange(uptr *begin, uptr *end) {
+  *begin = (uptr)&cache;
+  *end = *begin + sizeof(cache);
+}
+
+uptr GetMallocUsableSize(const void *p) {
+  ChunkMetadata *m = Metadata(p);
+  if (!m) return 0;
+  return m->requested_size;
+}
+
+///// Interface to the common LSan module. /////
+
+void LockAllocator() {
+  allocator.ForceLock();
+}
+
+void UnlockAllocator() {
+  allocator.ForceUnlock();
+}
+
+void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
+  *begin = (uptr)&allocator;
+  *end = *begin + sizeof(allocator);
+}
+
+uptr PointsIntoChunk(void* p) {
+  uptr addr = reinterpret_cast<uptr>(p);
+  uptr chunk = reinterpret_cast<uptr>(allocator.GetBlockBeginFastLocked(p));
+  if (!chunk) return 0;
+  // LargeMmapAllocator considers pointers to the meta-region of a chunk to be
+  // valid, but we don't want that.
+  if (addr < chunk) return 0;
+  ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
+  CHECK(m);
+  if (!allocator.PointsIntoChunk(p))
+    return 0;
+  if (m->allocated != 1)
+    return 0;
+  if (addr < chunk + m->requested_size)
+    return chunk;
+  if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr))
+    return chunk;
+  return 0;
+}
+
+uptr GetUserBegin(uptr chunk) {
+  return chunk;
+}
+
+LsanMetadata::LsanMetadata(uptr chunk) {
+  metadata_ = Metadata(reinterpret_cast<void *>(chunk));
+  CHECK(metadata_);
+}
+
+bool LsanMetadata::allocated() const {
+  return reinterpret_cast<ChunkMetadata *>(metadata_)->allocated;
+}
+
+ChunkTag LsanMetadata::tag() const {
+  return reinterpret_cast<ChunkMetadata *>(metadata_)->tag;
+}
+
+void LsanMetadata::set_tag(ChunkTag value) {
+  reinterpret_cast<ChunkMetadata *>(metadata_)->tag = value;
+}
+
+uptr LsanMetadata::requested_size() const {
+  return reinterpret_cast<ChunkMetadata *>(metadata_)->requested_size;
+}
+
+u32 LsanMetadata::stack_trace_id() const {
+  return reinterpret_cast<ChunkMetadata *>(metadata_)->stack_trace_id;
+}
+
+void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+  allocator.ForEachChunk(callback, arg);
+}
+
+IgnoreObjectResult IgnoreObjectLocked(const void *p) {
+  void *chunk = allocator.GetBlockBegin(p);
+  if (!chunk || p < chunk) return kIgnoreObjectInvalid;
+  ChunkMetadata *m = Metadata(chunk);
+  CHECK(m);
+  if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) {
+    if (m->tag == kIgnored)
+      return kIgnoreObjectAlreadyIgnored;
+    m->tag = kIgnored;
+    return kIgnoreObjectSuccess;
+  } else {
+    return kIgnoreObjectInvalid;
+  }
+}
+} // namespace __lsan
+
+using namespace __lsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_current_allocated_bytes() {
+  uptr stats[AllocatorStatCount];
+  allocator.GetStats(stats);
+  return stats[AllocatorStatAllocated];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_heap_size() {
+  uptr stats[AllocatorStatCount];
+  allocator.GetStats(stats);
+  return stats[AllocatorStatMapped];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_free_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_unmapped_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_allocated_size(const void *p) {
+  return GetMallocUsableSize(p);
+}
+} // extern "C"
diff --git a/lsan/src/lsan_common.cc b/lsan/src/lsan_common.cc
new file mode 100644 (file)
index 0000000..6a5d85f
--- /dev/null
@@ -0,0 +1,741 @@
+//=-- lsan_common.cc ------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Implementation of common leak checking functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan_common.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+
+#if CAN_SANITIZE_LEAKS
+namespace __lsan {
+
+// This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and
+// also to protect the global list of root regions.
+BlockingMutex global_mutex(LINKER_INITIALIZED);
+
+THREADLOCAL int disable_counter;
+bool DisabledInThisThread() { return disable_counter > 0; }
+
+Flags lsan_flags;
+
+void Flags::SetDefaults() {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
+}
+
+void RegisterLsanFlags(FlagParser *parser, Flags *f) {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) \
+  RegisterFlag(parser, #Name, Description, &f->Name);
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
+}
+
+#define LOG_POINTERS(...)                           \
+  do {                                              \
+    if (flags()->log_pointers) Report(__VA_ARGS__); \
+  } while (0);
+
+#define LOG_THREADS(...)                           \
+  do {                                             \
+    if (flags()->log_threads) Report(__VA_ARGS__); \
+  } while (0);
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kSuppressionLeak[] = "leak";
+static const char *kSuppressionTypes[] = { kSuppressionLeak };
+
+void InitializeSuppressions() {
+  CHECK_EQ(nullptr, suppression_ctx);
+  suppression_ctx = new (suppression_placeholder) // NOLINT
+      SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+  suppression_ctx->ParseFromFile(flags()->suppressions);
+  if (&__lsan_default_suppressions)
+    suppression_ctx->Parse(__lsan_default_suppressions());
+}
+
+static SuppressionContext *GetSuppressionContext() {
+  CHECK(suppression_ctx);
+  return suppression_ctx;
+}
+
+struct RootRegion {
+  const void *begin;
+  uptr size;
+};
+
+InternalMmapVector<RootRegion> *root_regions;
+
+void InitializeRootRegions() {
+  CHECK(!root_regions);
+  ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)];
+  root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
+}
+
+void InitCommonLsan() {
+  InitializeRootRegions();
+  if (common_flags()->detect_leaks) {
+    // Initialization which can fail or print warnings should only be done if
+    // LSan is actually enabled.
+    InitializeSuppressions();
+    InitializePlatformSpecificModules();
+  }
+}
+
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
+ public:
+  Decorator() : SanitizerCommonDecorator() { }
+  const char *Error() { return Red(); }
+  const char *Leak() { return Blue(); }
+  const char *End() { return Default(); }
+};
+
+static inline bool CanBeAHeapPointer(uptr p) {
+  // Since our heap is located in mmap-ed memory, we can assume a sensible lower
+  // bound on heap addresses.
+  const uptr kMinAddress = 4 * 4096;
+  if (p < kMinAddress) return false;
+#if defined(__x86_64__)
+  // Accept only canonical form user-space addresses.
+  return ((p >> 47) == 0);
+#elif defined(__mips64)
+  return ((p >> 40) == 0);
+#elif defined(__aarch64__)
+  unsigned runtimeVMA =
+    (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+  return ((p >> runtimeVMA) == 0);
+#else
+  return true;
+#endif
+}
+
+// Scans the memory range, looking for byte patterns that point into allocator
+// chunks. Marks those chunks with |tag| and adds them to |frontier|.
+// There are two usage modes for this function: finding reachable chunks
+// (|tag| = kReachable) and finding indirectly leaked chunks
+// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
+// so |frontier| = 0.
+void ScanRangeForPointers(uptr begin, uptr end,
+                          Frontier *frontier,
+                          const char *region_type, ChunkTag tag) {
+  CHECK(tag == kReachable || tag == kIndirectlyLeaked);
+  const uptr alignment = flags()->pointer_alignment();
+  LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
+  uptr pp = begin;
+  if (pp % alignment)
+    pp = pp + alignment - pp % alignment;
+  for (; pp + sizeof(void *) <= end; pp += alignment) {  // NOLINT
+    void *p = *reinterpret_cast<void **>(pp);
+    if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue;
+    uptr chunk = PointsIntoChunk(p);
+    if (!chunk) continue;
+    // Pointers to self don't count. This matters when tag == kIndirectlyLeaked.
+    if (chunk == begin) continue;
+    LsanMetadata m(chunk);
+    if (m.tag() == kReachable || m.tag() == kIgnored) continue;
+
+    // Do this check relatively late so we can log only the interesting cases.
+    if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
+      LOG_POINTERS(
+          "%p is poisoned: ignoring %p pointing into chunk %p-%p of size "
+          "%zu.\n",
+          pp, p, chunk, chunk + m.requested_size(), m.requested_size());
+      continue;
+    }
+
+    m.set_tag(tag);
+    LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
+                 chunk, chunk + m.requested_size(), m.requested_size());
+    if (frontier)
+      frontier->push_back(chunk);
+  }
+}
+
+void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) {
+  Frontier *frontier = reinterpret_cast<Frontier *>(arg);
+  ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable);
+}
+
+// Scans thread data (stacks and TLS) for heap pointers.
+static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
+                           Frontier *frontier) {
+  InternalScopedBuffer<uptr> registers(SuspendedThreadsList::RegisterCount());
+  uptr registers_begin = reinterpret_cast<uptr>(registers.data());
+  uptr registers_end = registers_begin + registers.size();
+  for (uptr i = 0; i < suspended_threads.thread_count(); i++) {
+    uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i));
+    LOG_THREADS("Processing thread %d.\n", os_id);
+    uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
+    bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
+                                              &tls_begin, &tls_end,
+                                              &cache_begin, &cache_end);
+    if (!thread_found) {
+      // If a thread can't be found in the thread registry, it's probably in the
+      // process of destruction. Log this event and move on.
+      LOG_THREADS("Thread %d not found in registry.\n", os_id);
+      continue;
+    }
+    uptr sp;
+    bool have_registers =
+        (suspended_threads.GetRegistersAndSP(i, registers.data(), &sp) == 0);
+    if (!have_registers) {
+      Report("Unable to get registers from thread %d.\n");
+      // If unable to get SP, consider the entire stack to be reachable.
+      sp = stack_begin;
+    }
+
+    if (flags()->use_registers && have_registers)
+      ScanRangeForPointers(registers_begin, registers_end, frontier,
+                           "REGISTERS", kReachable);
+
+    if (flags()->use_stacks) {
+      LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp);
+      if (sp < stack_begin || sp >= stack_end) {
+        // SP is outside the recorded stack range (e.g. the thread is running a
+        // signal handler on alternate stack). Again, consider the entire stack
+        // range to be reachable.
+        LOG_THREADS("WARNING: stack pointer not in stack range.\n");
+      } else {
+        // Shrink the stack range to ignore out-of-scope values.
+        stack_begin = sp;
+      }
+      ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
+                           kReachable);
+      ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
+    }
+
+#if !defined(__arm__) && !defined(__i386__)
+    if (flags()->use_tls) {
+      LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
+      if (cache_begin == cache_end) {
+        ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
+      } else {
+        // Because LSan should not be loaded with dlopen(), we can assume
+        // that allocator cache will be part of static TLS image.
+        CHECK_LE(tls_begin, cache_begin);
+        CHECK_GE(tls_end, cache_end);
+        if (tls_begin < cache_begin)
+          ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
+                               kReachable);
+        if (tls_end > cache_end)
+          ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable);
+      }
+    }
+#endif
+  }
+}
+
+static void ProcessRootRegion(Frontier *frontier, uptr root_begin,
+                              uptr root_end) {
+  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+  uptr begin, end, prot;
+  while (proc_maps.Next(&begin, &end,
+                        /*offset*/ nullptr, /*filename*/ nullptr,
+                        /*filename_size*/ 0, &prot)) {
+    uptr intersection_begin = Max(root_begin, begin);
+    uptr intersection_end = Min(end, root_end);
+    if (intersection_begin >= intersection_end) continue;
+    bool is_readable = prot & MemoryMappingLayout::kProtectionRead;
+    LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
+                 root_begin, root_end, begin, end,
+                 is_readable ? "readable" : "unreadable");
+    if (is_readable)
+      ScanRangeForPointers(intersection_begin, intersection_end, frontier,
+                           "ROOT", kReachable);
+  }
+}
+
+// Scans root regions for heap pointers.
+static void ProcessRootRegions(Frontier *frontier) {
+  if (!flags()->use_root_regions) return;
+  CHECK(root_regions);
+  for (uptr i = 0; i < root_regions->size(); i++) {
+    RootRegion region = (*root_regions)[i];
+    uptr begin_addr = reinterpret_cast<uptr>(region.begin);
+    ProcessRootRegion(frontier, begin_addr, begin_addr + region.size);
+  }
+}
+
+static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
+  while (frontier->size()) {
+    uptr next_chunk = frontier->back();
+    frontier->pop_back();
+    LsanMetadata m(next_chunk);
+    ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier,
+                         "HEAP", tag);
+  }
+}
+
+// ForEachChunk callback. If the chunk is marked as leaked, marks all chunks
+// which are reachable from it as indirectly leaked.
+static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
+  return;
+  chunk = GetUserBegin(chunk);
+  LsanMetadata m(chunk);
+  if (m.allocated() && m.tag() != kReachable) {
+    ScanRangeForPointers(chunk, chunk + m.requested_size(),
+                         /* frontier */ nullptr, "HEAP", kIndirectlyLeaked);
+  }
+}
+
+// ForEachChunk callback. If chunk is marked as ignored, adds its address to
+// frontier.
+static void CollectIgnoredCb(uptr chunk, void *arg) {
+  CHECK(arg);
+  chunk = GetUserBegin(chunk);
+  LsanMetadata m(chunk);
+  if (m.allocated() && m.tag() == kIgnored) {
+    LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
+                 chunk, chunk + m.requested_size(), m.requested_size());
+    reinterpret_cast<Frontier *>(arg)->push_back(chunk);
+  }
+}
+
+// Sets the appropriate tag on each chunk.
+static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
+  // Holds the flood fill frontier.
+  Frontier frontier(1);
+
+  ForEachChunk(CollectIgnoredCb, &frontier);
+  ProcessGlobalRegions(&frontier);
+  ProcessThreads(suspended_threads, &frontier);
+  ProcessRootRegions(&frontier);
+  FloodFillTag(&frontier, kReachable);
+
+  // The check here is relatively expensive, so we do this in a separate flood
+  // fill. That way we can skip the check for chunks that are reachable
+  // otherwise.
+  LOG_POINTERS("Processing platform-specific allocations.\n");
+  CHECK_EQ(0, frontier.size());
+  ProcessPlatformSpecificAllocations(&frontier);
+  FloodFillTag(&frontier, kReachable);
+
+  // Iterate over leaked chunks and mark those that are reachable from other
+  // leaked chunks.
+  LOG_POINTERS("Scanning leaked chunks.\n");
+  ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
+}
+
+// ForEachChunk callback. Resets the tags to pre-leak-check state.
+static void ResetTagsCb(uptr chunk, void *arg) {
+  (void)arg;
+  chunk = GetUserBegin(chunk);
+  LsanMetadata m(chunk);
+  if (m.allocated() && m.tag() != kIgnored)
+    m.set_tag(kDirectlyLeaked);
+}
+
+static void PrintStackTraceById(u32 stack_trace_id) {
+  CHECK(stack_trace_id);
+  StackDepotGet(stack_trace_id).Print();
+}
+
+// ForEachChunk callback. Aggregates information about unreachable chunks into
+// a LeakReport.
+static void CollectLeaksCb(uptr chunk, void *arg) {
+  CHECK(arg);
+  LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg);
+  chunk = GetUserBegin(chunk);
+  LsanMetadata m(chunk);
+  if (!m.allocated()) return;
+  if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
+    u32 resolution = flags()->resolution;
+    u32 stack_trace_id = 0;
+    if (resolution > 0) {
+      StackTrace stack = StackDepotGet(m.stack_trace_id());
+      stack.size = Min(stack.size, resolution);
+      stack_trace_id = StackDepotPut(stack);
+    } else {
+      stack_trace_id = m.stack_trace_id();
+    }
+    leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(),
+                                m.tag());
+  }
+}
+
+static void PrintMatchedSuppressions() {
+  InternalMmapVector<Suppression *> matched(1);
+  GetSuppressionContext()->GetMatched(&matched);
+  if (!matched.size())
+    return;
+  const char *line = "-----------------------------------------------------";
+  Printf("%s\n", line);
+  Printf("Suppressions used:\n");
+  Printf("  count      bytes template\n");
+  for (uptr i = 0; i < matched.size(); i++)
+    Printf("%7zu %10zu %s\n", static_cast<uptr>(atomic_load_relaxed(
+        &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ);
+  Printf("%s\n\n", line);
+}
+
+struct CheckForLeaksParam {
+  bool success;
+  LeakReport leak_report;
+} param;
+
+static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
+                                  void *arg) {
+  CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
+  CHECK(param);
+  CHECK(!param->success);
+  ClassifyAllChunks(suspended_threads);
+  ForEachChunk(CollectLeaksCb, &param->leak_report);
+  // Clean up for subsequent leak checks. This assumes we did not overwrite any
+  // kIgnored tags.
+  ForEachChunk(ResetTagsCb, nullptr);
+  param->success = true;
+}
+
+static bool CheckForLeaks() {
+  if (&__lsan_is_turned_off && __lsan_is_turned_off())
+      return false;
+  EnsureMainThreadIDIsCorrect();
+  param.success = false;
+  param.leak_report.Clear();
+  LockThreadRegistry();
+  LockAllocator();
+  DoStopTheWorld(CheckForLeaksCallback, &param);
+  UnlockAllocator();
+  UnlockThreadRegistry();
+
+  if (!param.success) {
+    Report("LeakSanitizer has encountered a fatal error.\n");
+    Die();
+  }
+  uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount();
+  if (unsuppressed_count > 0) {
+    Decorator d;
+    Printf("\n"
+           "================================================================="
+           "\n");
+    Printf("%s", d.Error());
+    Report("ERROR: LeakSanitizer: detected memory leaks\n");
+    Printf("%s", d.End());
+  }
+  if (unsuppressed_count > 0) {
+    param.leak_report.PrintSummary();
+    return true;
+  }
+  return false;
+}
+
+void DoLeakCheck() {
+  BlockingMutexLock l(&global_mutex);
+  static bool already_done;
+  if (already_done) return;
+  already_done = true;
+  bool have_leaks = CheckForLeaks();
+  if (!have_leaks) {
+    return;
+  }
+  if (common_flags()->exitcode) {
+    Die();
+  }
+}
+
+static int DoRecoverableLeakCheck() {
+  BlockingMutexLock l(&global_mutex);
+  bool have_leaks = CheckForLeaks();
+  return have_leaks ? 1 : 0;
+}
+
+static Suppression *GetSuppressionForAddr(uptr addr) {
+  Suppression *s = nullptr;
+
+  // Suppress by module name.
+  SuppressionContext *suppressions = GetSuppressionContext();
+  if (const char *module_name =
+          Symbolizer::GetOrInit()->GetModuleNameForPc(addr))
+    if (suppressions->Match(module_name, kSuppressionLeak, &s))
+      return s;
+
+  // Suppress by file or function name.
+  SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
+  for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+    if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) ||
+        suppressions->Match(cur->info.file, kSuppressionLeak, &s)) {
+      break;
+    }
+  }
+  frames->ClearAll();
+  return s;
+}
+
+static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
+  StackTrace stack = StackDepotGet(stack_trace_id);
+  for (uptr i = 0; i < stack.size; i++) {
+    Suppression *s = GetSuppressionForAddr(
+        StackTrace::GetPreviousInstructionPc(stack.trace[i]));
+    if (s) return s;
+  }
+  return nullptr;
+}
+
+///// LeakReport implementation. /////
+
+// A hard limit on the number of distinct leaks, to avoid quadratic complexity
+// in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks
+// in real-world applications.
+// FIXME: Get rid of this limit by changing the implementation of LeakReport to
+// use a hash table.
+const uptr kMaxLeaksConsidered = 5000;
+
+void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id,
+                                uptr leaked_size, ChunkTag tag) {
+  CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
+  bool is_directly_leaked = (tag == kDirectlyLeaked);
+  uptr i;
+  for (i = 0; i < leaks_.size(); i++) {
+    if (leaks_[i].stack_trace_id == stack_trace_id &&
+        leaks_[i].is_directly_leaked == is_directly_leaked) {
+      leaks_[i].hit_count++;
+      leaks_[i].total_size += leaked_size;
+      break;
+    }
+  }
+  if (i == leaks_.size()) {
+    if (leaks_.size() == kMaxLeaksConsidered) return;
+    Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id,
+                  is_directly_leaked, /* is_suppressed */ false };
+    leaks_.push_back(leak);
+  }
+  if (flags()->report_objects) {
+    LeakedObject obj = {leaks_[i].id, *((u32 *)chunk), chunk, leaked_size};
+    leaked_objects_.push_back(obj);
+  }
+}
+
+static bool LeakComparator(const Leak &leak1, const Leak &leak2) {
+  if (leak1.is_directly_leaked == leak2.is_directly_leaked)
+    return leak1.total_size > leak2.total_size;
+  else
+    return leak1.is_directly_leaked;
+}
+
+void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) {
+  CHECK(leaks_.size() <= kMaxLeaksConsidered);
+  Printf("\n");
+  if (leaks_.size() == kMaxLeaksConsidered)
+    Printf("Too many leaks! Only the first %zu leaks encountered will be "
+           "reported.\n",
+           kMaxLeaksConsidered);
+
+  uptr unsuppressed_count = UnsuppressedLeakCount();
+  if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count)
+    Printf("The %zu top leak(s):\n", num_leaks_to_report);
+  InternalSort(&leaks_, leaks_.size(), LeakComparator);
+  uptr leaks_reported = 0;
+  for (uptr i = 0; i < leaks_.size(); i++) {
+    if (leaks_[i].is_suppressed) continue;
+    PrintReportForLeak(i);
+    leaks_reported++;
+    if (leaks_reported == num_leaks_to_report) break;
+  }
+  if (leaks_reported < unsuppressed_count) {
+    uptr remaining = unsuppressed_count - leaks_reported;
+    Printf("Omitting %zu more leak(s).\n", remaining);
+  }
+}
+
+void LeakReport::PrintReportForLeak(uptr index) {
+  Decorator d;
+  Printf("%s", d.Leak());
+  Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
+         leaks_[index].is_directly_leaked ? "Direct" : "Indirect",
+         leaks_[index].total_size, leaks_[index].hit_count);
+  Printf("%s", d.End());
+
+  PrintStackTraceById(leaks_[index].stack_trace_id);
+}
+
+void LeakReport::PrintLeakedObjectsForLeak(uptr index) {
+  u32 leak_id = leaks_[index].id;
+  for (uptr j = 0; j < leaked_objects_.size(); j++) {
+    if (leaked_objects_[j].leak_id == leak_id)
+      Printf("%p (%zu bytes)\n", leaked_objects_[j].addr,
+             leaked_objects_[j].size);
+  }
+}
+
+void LeakReport::PrintSummary() {
+  CHECK(leaks_.size() <= kMaxLeaksConsidered);
+  uptr bytes = 0, allocations = 0;
+  for (uptr i = 0; i < leaks_.size(); i++) {
+      if (leaks_[i].is_suppressed) continue;
+      bytes += leaks_[i].total_size;
+      allocations += leaks_[i].hit_count;
+  }
+  InternalScopedString summary(kMaxSummaryLength);
+  summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes,
+                 allocations);
+  ReportErrorSummary(summary.data());
+}
+
+void LeakReport::ApplySuppressions() {
+  for (uptr i = 0; i < leaks_.size(); i++) {
+    Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id);
+    if (s) {
+      s->weight += leaks_[i].total_size;
+      atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) +
+          leaks_[i].hit_count);
+      leaks_[i].is_suppressed = true;
+    }
+  }
+}
+
+uptr LeakReport::UnsuppressedLeakCount() {
+  uptr result = leaks_.size();
+  return result;
+}
+
+void LeakReport::Clear() {
+  leaks_.clear();
+  leaked_objects_.clear();
+}
+
+uptr LeakReport::LeakedObjectsNum() {
+  return leaked_objects_.size();
+}
+
+const void *LeakReport::GetLeakedObjectsPtr() {
+  return reinterpret_cast<const void *>(leaked_objects_.data());
+}
+
+} // namespace __lsan
+#endif // CAN_SANITIZE_LEAKS
+
+using namespace __lsan;  // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_ignore_object(const void *p) {
+#if CAN_SANITIZE_LEAKS
+  if (!common_flags()->detect_leaks)
+    return;
+  // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not
+  // locked.
+  BlockingMutexLock l(&global_mutex);
+  IgnoreObjectResult res = IgnoreObjectLocked(p);
+  if (res == kIgnoreObjectInvalid)
+    VReport(1, "__lsan_ignore_object(): no heap object found at %p", p);
+  if (res == kIgnoreObjectAlreadyIgnored)
+    VReport(1, "__lsan_ignore_object(): "
+           "heap object at %p is already being ignored\n", p);
+  if (res == kIgnoreObjectSuccess)
+    VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_register_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+  BlockingMutexLock l(&global_mutex);
+  CHECK(root_regions);
+  RootRegion region = {begin, size};
+  root_regions->push_back(region);
+  VReport(1, "Registered root region at %p of size %llu\n", begin, size);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_unregister_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+  BlockingMutexLock l(&global_mutex);
+  CHECK(root_regions);
+  bool removed = false;
+  for (uptr i = 0; i < root_regions->size(); i++) {
+    RootRegion region = (*root_regions)[i];
+    if (region.begin == begin && region.size == size) {
+      removed = true;
+      uptr last_index = root_regions->size() - 1;
+      (*root_regions)[i] = (*root_regions)[last_index];
+      root_regions->pop_back();
+      VReport(1, "Unregistered root region at %p of size %llu\n", begin, size);
+      break;
+    }
+  }
+  if (!removed) {
+    Report(
+        "__lsan_unregister_root_region(): region at %p of size %llu has not "
+        "been registered.\n",
+        begin, size);
+    Die();
+  }
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_disable() {
+#if CAN_SANITIZE_LEAKS
+  __lsan::disable_counter++;
+#endif
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_enable() {
+#if CAN_SANITIZE_LEAKS
+  if (!__lsan::disable_counter && common_flags()->detect_leaks) {
+    Report("Unmatched call to __lsan_enable().\n");
+    Die();
+  }
+  __lsan::disable_counter--;
+#endif
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_do_leak_check() {
+#if CAN_SANITIZE_LEAKS
+  if (common_flags()->detect_leaks)
+    __lsan::DoLeakCheck();
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __lsan_do_recoverable_leak_check() {
+#if CAN_SANITIZE_LEAKS
+  if (common_flags()->detect_leaks)
+    return __lsan::DoRecoverableLeakCheck();
+#endif // CAN_SANITIZE_LEAKS
+  return 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __lsan_get_leaked_objects_num() {
+#if CAN_SANITIZE_LEAKS
+  return param.leak_report.LeakedObjectsNum();
+#endif // CAN_SANITIZE_LEAKS
+  return 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+const void *__lsan_get_leaked_objects_ptr() {
+#if CAN_SANITIZE_LEAKS
+  return param.leak_report.GetLeakedObjectsPtr();
+#endif // CAN_SANITIZE_LEAKS
+  return 0;
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+int __lsan_is_turned_off() {
+  return 0;
+}
+#endif
+} // extern "C"
diff --git a/lsan/src/lsan_common_linux.cc b/lsan/src/lsan_common_linux.cc
new file mode 100644 (file)
index 0000000..0456dce
--- /dev/null
@@ -0,0 +1,177 @@
+//=-- lsan_common_linux.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Implementation of common leak checking functionality. Linux-specific code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "lsan_common.h"
+
+#if CAN_SANITIZE_LEAKS && SANITIZER_LINUX
+#include <link.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __lsan {
+
+static const char kLinkerName[] = "ld";
+// We request 2 modules matching "ld", so we can print a warning if there's more
+// than one match. But only the first one is actually used.
+static char linker_placeholder[2 * sizeof(LoadedModule)] ALIGNED(64);
+static LoadedModule *linker = nullptr;
+
+static bool IsLinker(const char* full_name) {
+  return LibraryNameIs(full_name, kLinkerName);
+}
+
+void InitializePlatformSpecificModules() {
+  internal_memset(linker_placeholder, 0, sizeof(linker_placeholder));
+  uptr num_matches = GetListOfModules(
+      reinterpret_cast<LoadedModule *>(linker_placeholder), 2, IsLinker);
+  if (num_matches == 1) {
+    linker = reinterpret_cast<LoadedModule *>(linker_placeholder);
+    return;
+  }
+  if (num_matches == 0)
+    VReport(1, "LeakSanitizer: Dynamic linker not found. "
+            "TLS will not be handled correctly.\n");
+  else if (num_matches > 1)
+    VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
+            "TLS will not be handled correctly.\n", kLinkerName);
+  linker = nullptr;
+}
+
+static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
+                                        void *data) {
+  Frontier *frontier = reinterpret_cast<Frontier *>(data);
+  for (uptr j = 0; j < info->dlpi_phnum; j++) {
+    const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);
+    // We're looking for .data and .bss sections, which reside in writeable,
+    // loadable segments.
+    if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||
+        (phdr->p_memsz == 0))
+      continue;
+    uptr begin = info->dlpi_addr + phdr->p_vaddr;
+    uptr end = begin + phdr->p_memsz;
+    uptr allocator_begin = 0, allocator_end = 0;
+    GetAllocatorGlobalRange(&allocator_begin, &allocator_end);
+    if (begin <= allocator_begin && allocator_begin < end) {
+      CHECK_LE(allocator_begin, allocator_end);
+      CHECK_LT(allocator_end, end);
+      if (begin < allocator_begin)
+        ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL",
+                             kReachable);
+      if (allocator_end < end)
+        ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL",
+                             kReachable);
+    } else {
+      ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable);
+    }
+  }
+  return 0;
+}
+
+// Scans global variables for heap pointers.
+void ProcessGlobalRegions(Frontier *frontier) {
+  if (!flags()->use_globals) return;
+  dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
+}
+
+static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
+  CHECK(stack_id);
+  StackTrace stack = map->Get(stack_id);
+  // The top frame is our malloc/calloc/etc. The next frame is the caller.
+  if (stack.size >= 2)
+    return stack.trace[1];
+  return 0;
+}
+
+struct ProcessPlatformAllocParam {
+  Frontier *frontier;
+  StackDepotReverseMap *stack_depot_reverse_map;
+};
+
+// ForEachChunk callback. Identifies unreachable chunks which must be treated as
+// reachable. Marks them as reachable and adds them to the frontier.
+static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) {
+  CHECK(arg);
+  ProcessPlatformAllocParam *param =
+      reinterpret_cast<ProcessPlatformAllocParam *>(arg);
+  chunk = GetUserBegin(chunk);
+  LsanMetadata m(chunk);
+  if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
+    u32 stack_id = m.stack_trace_id();
+    uptr caller_pc = 0;
+    if (stack_id > 0)
+      caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map);
+    // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark
+    // it as reachable, as we can't properly report its allocation stack anyway.
+    if (caller_pc == 0 || linker->containsAddress(caller_pc)) {
+      m.set_tag(kReachable);
+      param->frontier->push_back(chunk);
+    }
+  }
+}
+
+// Handles dynamically allocated TLS blocks by treating all chunks allocated
+// from ld-linux.so as reachable.
+// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules.
+// They are allocated with a __libc_memalign() call in allocate_and_init()
+// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those
+// blocks, but we can make sure they come from our own allocator by intercepting
+// __libc_memalign(). On top of that, there is no easy way to reach them. Their
+// addresses are stored in a dynamically allocated array (the DTV) which is
+// referenced from the static TLS. Unfortunately, we can't just rely on the DTV
+// being reachable from the static TLS, and the dynamic TLS being reachable from
+// the DTV. This is because the initial DTV is allocated before our interception
+// mechanism kicks in, and thus we don't recognize it as allocated memory. We
+// can't special-case it either, since we don't know its size.
+// Our solution is to include in the root set all allocations made from
+// ld-linux.so (which is where allocate_and_init() is implemented). This is
+// guaranteed to include all dynamic TLS blocks (and possibly other allocations
+// which we don't care about).
+void ProcessPlatformSpecificAllocations(Frontier *frontier) {
+  if (!flags()->use_tls) return;
+  if (!linker) return;
+  StackDepotReverseMap stack_depot_reverse_map;
+  ProcessPlatformAllocParam arg = {frontier, &stack_depot_reverse_map};
+  ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg);
+}
+
+struct DoStopTheWorldParam {
+  StopTheWorldCallback callback;
+  void *argument;
+};
+
+static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
+                                  void *data) {
+  DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
+  StopTheWorld(param->callback, param->argument);
+  return 1;
+}
+
+// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
+// of the threads is frozen while holding the libdl lock, the tracer will hang
+// in dl_iterate_phdr() forever.
+// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
+// tracer task and the thread that spawned it. Thus, if we run the tracer task
+// while holding the libdl lock in the parent thread, we can safely reenter it
+// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
+// callback in the parent thread.
+void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
+  DoStopTheWorldParam param = {callback, argument};
+  dl_iterate_phdr(DoStopTheWorldCallback, &param);
+}
+
+} // namespace __lsan
+
+#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX
diff --git a/lsan/src/lsan_interceptors.cc b/lsan/src/lsan_interceptors.cc
new file mode 100644 (file)
index 0000000..9363c37
--- /dev/null
@@ -0,0 +1,229 @@
+//=-- lsan_interceptors.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Interceptors for standalone LSan.
+//
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "lsan.h"
+#include "lsan_allocator.h"
+#include "lsan_thread.h"
+#include <stddef.h>
+
+using namespace __lsan;
+
+extern "C" {
+int pthread_attr_init(void *attr);
+int pthread_attr_destroy(void *attr);
+int pthread_attr_getdetachstate(void *attr, int *v);
+int pthread_key_create(unsigned *key, void (*destructor)(void* v));
+int pthread_setspecific(unsigned key, const void *v);
+}
+
+#define ENSURE_LSAN_INITED do {   \
+  CHECK(!lsan_init_is_running);   \
+  if (!lsan_inited)               \
+    __lsan_init();                \
+} while (0)
+
+///// Malloc/free interceptors. /////
+
+const bool kAlwaysClearMemory = true;
+
+namespace std {
+  struct nothrow_t;
+}
+
+INTERCEPTOR(void*, malloc, uptr size) {
+  ENSURE_LSAN_INITED;
+  GET_STACK_TRACE_MALLOC;
+  int tid = GetTid();
+  if (ThreadIsMine(tid))
+    return Allocate(stack, size, 1, kAlwaysClearMemory);
+  else
+    return REAL(malloc)(size);
+}
+
+INTERCEPTOR(void, free, void *p) {
+  ENSURE_LSAN_INITED;
+  if (!PointerIsMine(p))
+    REAL(free)(p);
+  else
+    Deallocate(p);
+}
+
+INTERCEPTOR(void *, calloc, uptr nmemb, uptr size) {
+  if (lsan_init_is_running) {
+    // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+    const uptr kCallocPoolSize = 1024;
+    static uptr calloc_memory_for_dlsym[kCallocPoolSize];
+    static uptr allocated;
+    uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
+    void *mem = (void *)&calloc_memory_for_dlsym[allocated];
+    allocated += size_in_words;
+    CHECK(allocated < kCallocPoolSize);
+    return mem;
+  }
+
+  if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return nullptr;
+  ENSURE_LSAN_INITED;
+  GET_STACK_TRACE_MALLOC;
+  int tid = GetTid();
+  if (ThreadIsMine(tid)) {
+    size *= nmemb;
+    return Allocate(stack, size, 1, true);
+  } else {
+    return REAL(calloc)(nmemb, size);
+  }
+}
+
+INTERCEPTOR(void*, realloc, void *q, uptr size) {
+  ENSURE_LSAN_INITED;
+  GET_STACK_TRACE_MALLOC;
+  int tid = GetTid();
+  if (ThreadIsMine(tid)) {
+    if (q && !PointerIsMine(q))
+      return REAL(realloc)(q, size);
+    else
+      return Reallocate(stack, q, size, 1);
+  } else {
+    return REAL(realloc)(q, size);
+  }
+}
+
+#define OPERATOR_NEW_BODY                                \
+  ENSURE_LSAN_INITED;                                    \
+  GET_STACK_TRACE_MALLOC;                                \
+  int tid = GetTid();                                    \
+  if (ThreadIsMine(tid))                                 \
+    return Allocate(stack, size, 1, kAlwaysClearMemory); \
+  else                                                   \
+    return REAL(malloc)(size);
+
+INTERCEPTOR_ATTRIBUTE
+void *__interceptor_operator_new(size_t size) { OPERATOR_NEW_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void *__interceptor_operator_new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+
+#define OPERATOR_DELETE_BODY \
+  ENSURE_LSAN_INITED;        \
+  if (!PointerIsMine(ptr))   \
+    REAL(free)(ptr);         \
+  else                       \
+    Deallocate(ptr);
+
+INTERCEPTOR_ATTRIBUTE
+void __interceptor_operator_delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void __interceptor_operator_delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
+
+///// Thread initialization and finalization. /////
+
+static unsigned g_thread_finalize_key;
+
+static void thread_finalize(void *v) {
+  uptr iter = (uptr)v;
+  if (iter > 1) {
+    if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) {
+      Report("LeakSanitizer: failed to set thread key.\n");
+      Die();
+    }
+    return;
+  }
+  ThreadFinish();
+}
+
+struct ThreadParam {
+  void *(*callback)(void *arg);
+  void *param;
+  atomic_uintptr_t tid;
+};
+
+extern "C" void *__lsan_thread_start_func(void *arg) {
+  ThreadParam *p = (ThreadParam*)arg;
+  void* (*callback)(void *arg) = p->callback;
+  void *param = p->param;
+  // Wait until the last iteration to maximize the chance that we are the last
+  // destructor to run.
+  if (pthread_setspecific(g_thread_finalize_key,
+                          (void*)GetPthreadDestructorIterations())) {
+    Report("LeakSanitizer: failed to set thread key.\n");
+    Die();
+  }
+  int tid = 0;
+  while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
+    internal_sched_yield();
+  SetCurrentThread(tid);
+  ThreadStart(tid, GetTid());
+  atomic_store(&p->tid, 0, memory_order_release);
+  return callback(param);
+}
+
+INTERCEPTOR(int, pthread_create, void *th, void *attr,
+            void *(*callback)(void *), void *param) {
+  ENSURE_LSAN_INITED;
+  EnsureMainThreadIDIsCorrect();
+  __sanitizer_pthread_attr_t myattr;
+  if (!attr) {
+    pthread_attr_init(&myattr);
+    attr = &myattr;
+  }
+  AdjustStackSize(attr);
+  int detached = 0;
+  pthread_attr_getdetachstate(attr, &detached);
+  ThreadParam p;
+  p.callback = callback;
+  p.param = param;
+  atomic_store(&p.tid, 0, memory_order_relaxed);
+  int res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p);
+  if (res == 0) {
+    int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th, detached);
+    CHECK_NE(tid, 0);
+    atomic_store(&p.tid, tid, memory_order_release);
+    while (atomic_load(&p.tid, memory_order_acquire) != 0)
+      internal_sched_yield();
+  }
+  if (attr == &myattr)
+    pthread_attr_destroy(&myattr);
+  return res;
+}
+
+INTERCEPTOR(int, pthread_join, void *th, void **ret) {
+  ENSURE_LSAN_INITED;
+  int tid = ThreadTid((uptr)th);
+  int res = REAL(pthread_join)(th, ret);
+  if ((res == 0) && ThreadIsMine(tid))
+    ThreadJoin(tid);
+  return res;
+}
+
+namespace __lsan {
+
+void InitializeInterceptors() {
+  INTERCEPT_FUNCTION(malloc);
+  INTERCEPT_FUNCTION(free);
+  INTERCEPT_FUNCTION(calloc);
+  INTERCEPT_FUNCTION(realloc);
+  INTERCEPT_FUNCTION(pthread_create);
+  INTERCEPT_FUNCTION(pthread_join);
+
+  if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) {
+    Report("LeakSanitizer: failed to create thread key.\n");
+    Die();
+  }
+}
+
+} // namespace __lsan
diff --git a/lsan/src/lsan_thread.cc b/lsan/src/lsan_thread.cc
new file mode 100644 (file)
index 0000000..f2d3b68
--- /dev/null
@@ -0,0 +1,162 @@
+//=-- lsan_thread.cc ------------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// See lsan_thread.h for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan_thread.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_thread_registry.h"
+#include "lsan_allocator.h"
+
+namespace __lsan {
+
+const u32 kInvalidTid = (u32) -1;
+
+static ThreadRegistry *thread_registry;
+static THREADLOCAL u32 current_thread_tid = kInvalidTid;
+
+static ThreadContextBase *CreateThreadContext(u32 tid) {
+  void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext");
+  return new(mem) ThreadContext(tid);
+}
+
+static const uptr kMaxThreads = 1 << 13;
+static const uptr kThreadQuarantineSize = 64;
+
+void InitializeThreadRegistry() {
+  static char thread_registry_placeholder[sizeof(ThreadRegistry)] ALIGNED(64);
+  thread_registry = new(thread_registry_placeholder)
+    ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize);
+}
+
+u32 GetCurrentThread() {
+  return current_thread_tid;
+}
+
+void SetCurrentThread(u32 tid) {
+  current_thread_tid = tid;
+}
+
+ThreadContext::ThreadContext(int tid)
+  : ThreadContextBase(tid),
+    stack_begin_(0),
+    stack_end_(0),
+    cache_begin_(0),
+    cache_end_(0),
+    tls_begin_(0),
+    tls_end_(0) {}
+
+struct OnStartedArgs {
+  uptr stack_begin, stack_end,
+       cache_begin, cache_end,
+       tls_begin, tls_end;
+};
+
+void ThreadContext::OnStarted(void *arg) {
+  OnStartedArgs *args = reinterpret_cast<OnStartedArgs *>(arg);
+  stack_begin_ = args->stack_begin;
+  stack_end_ = args->stack_end;
+  tls_begin_ = args->tls_begin;
+  tls_end_ = args->tls_end;
+  cache_begin_ = args->cache_begin;
+  cache_end_ = args->cache_end;
+}
+
+void ThreadContext::OnFinished() {
+  AllocatorThreadFinish();
+}
+
+u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) {
+  return thread_registry->CreateThread(user_id, detached, parent_tid,
+                                       /* arg */ nullptr);
+}
+
+void ThreadStart(u32 tid, uptr os_id) {
+  OnStartedArgs args;
+  uptr stack_size = 0;
+  uptr tls_size = 0;
+  GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size,
+                       &args.tls_begin, &tls_size);
+  args.stack_end = args.stack_begin + stack_size;
+  args.tls_end = args.tls_begin + tls_size;
+  GetAllocatorCacheRange(&args.cache_begin, &args.cache_end);
+  thread_registry->StartThread(tid, os_id, &args);
+}
+
+void ThreadFinish() {
+  thread_registry->FinishThread(GetCurrentThread());
+}
+
+ThreadContext *CurrentThreadContext() {
+  if (!thread_registry) return nullptr;
+  if (GetCurrentThread() == kInvalidTid)
+    return nullptr;
+  // No lock needed when getting current thread.
+  return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread());
+}
+
+static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
+  uptr uid = (uptr)arg;
+  if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) {
+    return true;
+  }
+  return false;
+}
+
+u32 ThreadTid(uptr uid) {
+  return thread_registry->FindThread(FindThreadByUid, (void*)uid);
+}
+
+void ThreadJoin(u32 tid) {
+  CHECK_NE(tid, kInvalidTid);
+  thread_registry->JoinThread(tid, /* arg */nullptr);
+}
+
+void EnsureMainThreadIDIsCorrect() {
+  if (GetCurrentThread() == 0)
+    CurrentThreadContext()->os_id = GetTid();
+}
+
+///// Interface to the common LSan module. /////
+
+bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
+                           uptr *tls_begin, uptr *tls_end,
+                           uptr *cache_begin, uptr *cache_end) {
+  ThreadContext *context = static_cast<ThreadContext *>(
+      thread_registry->FindThreadContextByOsIDLocked(os_id));
+  if (!context) return false;
+  *stack_begin = context->stack_begin();
+  *stack_end = context->stack_end();
+  *tls_begin = context->tls_begin();
+  *tls_end = context->tls_end();
+  *cache_begin = context->cache_begin();
+  *cache_end = context->cache_end();
+  return true;
+}
+
+bool ThreadIsMine(u32 os_id) {
+  return thread_registry->FindThreadContextByOsID(os_id);
+}
+
+void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
+                            void *arg) {
+}
+
+void LockThreadRegistry() {
+  thread_registry->Lock();
+}
+
+void UnlockThreadRegistry() {
+  thread_registry->Unlock();
+}
+
+} // namespace __lsan
diff --git a/lsan/src/sanitizer_allocator.cc b/lsan/src/sanitizer_allocator.cc
new file mode 100644 (file)
index 0000000..b5e0bab
--- /dev/null
@@ -0,0 +1,149 @@
+//===-- sanitizer_allocator.cc --------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// This allocator is used inside run-times.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+// ThreadSanitizer for Go uses libc malloc/free.
+#if defined(SANITIZER_GO) || defined(SANITIZER_USE_MALLOC)
+# if SANITIZER_LINUX && !SANITIZER_ANDROID
+extern "C" void *__libc_malloc(uptr size);
+extern "C" void __libc_free(void *ptr);
+#  define LIBC_MALLOC __libc_malloc
+#  define LIBC_FREE __libc_free
+# else
+#  include <stdlib.h>
+#  define LIBC_MALLOC malloc
+#  define LIBC_FREE free
+# endif
+
+static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) {
+  (void)cache;
+  return LIBC_MALLOC(size);
+}
+
+static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
+  (void)cache;
+  LIBC_FREE(ptr);
+}
+
+InternalAllocator *internal_allocator() {
+  return 0;
+}
+
+#else // SANITIZER_GO
+
+static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
+static atomic_uint8_t internal_allocator_initialized;
+static StaticSpinMutex internal_alloc_init_mu;
+
+static InternalAllocatorCache internal_allocator_cache;
+static StaticSpinMutex internal_allocator_cache_mu;
+
+InternalAllocator *internal_allocator() {
+  InternalAllocator *internal_allocator_instance =
+      reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder);
+  if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) {
+    SpinMutexLock l(&internal_alloc_init_mu);
+    if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==
+        0) {
+      internal_allocator_instance->Init(/* may_return_null*/ false);
+      atomic_store(&internal_allocator_initialized, 1, memory_order_release);
+    }
+  }
+  return internal_allocator_instance;
+}
+
+static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) {
+  if (cache == 0) {
+    SpinMutexLock l(&internal_allocator_cache_mu);
+    return internal_allocator()->Allocate(&internal_allocator_cache, size, 8,
+                                          false);
+  }
+  return internal_allocator()->Allocate(cache, size, 8, false);
+}
+
+static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
+  if (!cache) {
+    SpinMutexLock l(&internal_allocator_cache_mu);
+    return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);
+  }
+  internal_allocator()->Deallocate(cache, ptr);
+}
+
+#endif // SANITIZER_GO
+
+const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
+
+void *InternalAlloc(uptr size, InternalAllocatorCache *cache) {
+  if (size + sizeof(u64) < size)
+    return nullptr;
+  void *p = RawInternalAlloc(size + sizeof(u64), cache);
+  if (!p)
+    return nullptr;
+  ((u64*)p)[0] = kBlockMagic;
+  return (char*)p + sizeof(u64);
+}
+
+void InternalFree(void *addr, InternalAllocatorCache *cache) {
+  if (!addr)
+    return;
+  addr = (char*)addr - sizeof(u64);
+  CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
+  ((u64*)addr)[0] = 0;
+  RawInternalFree(addr, cache);
+}
+
+// LowLevelAllocator
+static LowLevelAllocateCallback low_level_alloc_callback;
+
+void *LowLevelAllocator::Allocate(uptr size) {
+  // Align allocation size.
+  size = RoundUpTo(size, 8);
+  if (allocated_end_ - allocated_current_ < (sptr)size) {
+    uptr size_to_allocate = Max(size, GetPageSizeCached());
+    allocated_current_ =
+        (char*)MmapOrDie(size_to_allocate, __func__);
+    allocated_end_ = allocated_current_ + size_to_allocate;
+    if (low_level_alloc_callback) {
+      low_level_alloc_callback((uptr)allocated_current_,
+                               size_to_allocate);
+    }
+  }
+  CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
+  void *res = allocated_current_;
+  allocated_current_ += size;
+  return res;
+}
+
+void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) {
+  low_level_alloc_callback = callback;
+}
+
+bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) {
+  if (!size) return false;
+  uptr max = (uptr)-1L;
+  return (max / size) < n;
+}
+
+void NORETURN ReportAllocatorCannotReturnNull() {
+  Report("%s's allocator is terminating the process instead of returning 0\n",
+         SanitizerToolName);
+  Report("If you don't like this behavior set allocator_may_return_null=1\n");
+  CHECK(0);
+  Die();
+}
+
+} // namespace __sanitizer
diff --git a/lsan/src/sanitizer_common.cc b/lsan/src/sanitizer_common.cc
new file mode 100644 (file)
index 0000000..d4ecf25
--- /dev/null
@@ -0,0 +1,480 @@
+//===-- sanitizer_common.cc -----------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+const char *SanitizerToolName = "SanitizerTool";
+
+atomic_uint32_t current_verbosity;
+
+uptr GetPageSizeCached() {
+  static uptr PageSize;
+  if (!PageSize)
+    PageSize = GetPageSize();
+  return PageSize;
+}
+
+StaticSpinMutex report_file_mu;
+ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
+
+void RawWrite(const char *buffer) {
+  report_file.Write(buffer, internal_strlen(buffer));
+}
+
+void ReportFile::ReopenIfNecessary() {
+  mu->CheckLocked();
+  if (fd == kStdoutFd || fd == kStderrFd) return;
+
+  uptr pid = internal_getpid();
+  // If in tracer, use the parent's file.
+  if (pid == stoptheworld_tracer_pid)
+    pid = stoptheworld_tracer_ppid;
+  if (fd != kInvalidFd) {
+    // If the report file is already opened by the current process,
+    // do nothing. Otherwise the report file was opened by the parent
+    // process, close it now.
+    if (fd_pid == pid)
+      return;
+    else
+      CloseFile(fd);
+  }
+
+  const char *exe_name = GetProcessName();
+  if (common_flags()->log_exe_name && exe_name) {
+    internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
+                      exe_name, pid);
+  } else {
+    internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
+  }
+  fd = OpenFile(full_path, WrOnly);
+  if (fd == kInvalidFd) {
+    const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
+    WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
+    WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
+    Die();
+  }
+  fd_pid = pid;
+}
+
+void ReportFile::SetReportPath(const char *path) {
+  if (!path)
+    return;
+  uptr len = internal_strlen(path);
+  if (len > sizeof(path_prefix) - 100) {
+    Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
+           path[0], path[1], path[2], path[3],
+           path[4], path[5], path[6], path[7]);
+    Die();
+  }
+
+  SpinMutexLock l(mu);
+  if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
+    CloseFile(fd);
+  fd = kInvalidFd;
+  if (internal_strcmp(path, "stdout") == 0) {
+    fd = kStdoutFd;
+  } else if (internal_strcmp(path, "stderr") == 0) {
+    fd = kStderrFd;
+  } else {
+    internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
+  }
+}
+
+// PID of the tracer task in StopTheWorld. It shares the address space with the
+// main process, but has a different PID and thus requires special handling.
+uptr stoptheworld_tracer_pid = 0;
+// Cached pid of parent process - if the parent process dies, we want to keep
+// writing to the same log file.
+uptr stoptheworld_tracer_ppid = 0;
+
+static const int kMaxNumOfInternalDieCallbacks = 5;
+static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
+
+bool AddDieCallback(DieCallbackType callback) {
+  for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+    if (InternalDieCallbacks[i] == nullptr) {
+      InternalDieCallbacks[i] = callback;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool RemoveDieCallback(DieCallbackType callback) {
+  for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+    if (InternalDieCallbacks[i] == callback) {
+      internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
+                       sizeof(InternalDieCallbacks[0]) *
+                           (kMaxNumOfInternalDieCallbacks - i - 1));
+      InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
+      return true;
+    }
+  }
+  return false;
+}
+
+static DieCallbackType UserDieCallback;
+void SetUserDieCallback(DieCallbackType callback) {
+  UserDieCallback = callback;
+}
+
+void NORETURN Die() {
+  if (UserDieCallback)
+    UserDieCallback();
+  for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
+    if (InternalDieCallbacks[i])
+      InternalDieCallbacks[i]();
+  }
+  if (common_flags()->abort_on_error)
+    Abort();
+  internal__exit(common_flags()->exitcode);
+}
+
+static CheckFailedCallbackType CheckFailedCallback;
+void SetCheckFailedCallback(CheckFailedCallbackType callback) {
+  CheckFailedCallback = callback;
+}
+
+void NORETURN CheckFailed(const char *file, int line, const char *cond,
+                          u64 v1, u64 v2) {
+  if (CheckFailedCallback) {
+    CheckFailedCallback(file, line, cond, v1, v2);
+  }
+  Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
+                                                            v1, v2);
+  Die();
+}
+
+void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
+                                      const char *mmap_type, error_t err) {
+  static int recursion_count;
+  if (recursion_count) {
+    // The Report() and CHECK calls below may call mmap recursively and fail.
+    // If we went into recursion, just die.
+    RawWrite("ERROR: Failed to mmap\n");
+    Die();
+  }
+  recursion_count++;
+  Report("ERROR: %s failed to "
+         "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
+         SanitizerToolName, mmap_type, size, size, mem_type, err);
+#ifndef SANITIZER_GO
+  DumpProcessMap();
+#endif
+  UNREACHABLE("unable to mmap");
+}
+
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+                      uptr *read_len, uptr max_len, error_t *errno_p) {
+  uptr PageSize = GetPageSizeCached();
+  uptr kMinFileLen = PageSize;
+  *buff = nullptr;
+  *buff_size = 0;
+  *read_len = 0;
+  // The files we usually open are not seekable, so try different buffer sizes.
+  for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
+    fd_t fd = OpenFile(file_name, RdOnly, errno_p);
+    if (fd == kInvalidFd) return false;
+    UnmapOrDie(*buff, *buff_size);
+    *buff = (char*)MmapOrDie(size, __func__);
+    *buff_size = size;
+    *read_len = 0;
+    // Read up to one page at a time.
+    bool reached_eof = false;
+    while (*read_len + PageSize <= size) {
+      uptr just_read;
+      if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) {
+        UnmapOrDie(*buff, *buff_size);
+        return false;
+      }
+      if (just_read == 0) {
+        reached_eof = true;
+        break;
+      }
+      *read_len += just_read;
+    }
+    CloseFile(fd);
+    if (reached_eof)  // We've read the whole file.
+      break;
+  }
+  return true;
+}
+
+typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
+
+template<class T>
+static inline bool CompareLess(const T &a, const T &b) {
+  return a < b;
+}
+
+void SortArray(uptr *array, uptr size) {
+  InternalSort<uptr*, UptrComparisonFunction>(&array, size, CompareLess);
+}
+
+// We want to map a chunk of address space aligned to 'alignment'.
+// We do it by maping a bit more and then unmaping redundant pieces.
+// We probably can do it with fewer syscalls in some OS-dependent way.
+void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
+// uptr PageSize = GetPageSizeCached();
+  CHECK(IsPowerOfTwo(size));
+  CHECK(IsPowerOfTwo(alignment));
+  uptr map_size = size + alignment;
+  uptr map_res = (uptr)MmapOrDie(map_size, mem_type);
+  uptr map_end = map_res + map_size;
+  uptr res = map_res;
+  if (res & (alignment - 1))  // Not aligned.
+    res = (map_res + alignment) & ~(alignment - 1);
+  uptr end = res + size;
+  if (res != map_res)
+    UnmapOrDie((void*)map_res, res - map_res);
+  if (end != map_end)
+    UnmapOrDie((void*)end, map_end - end);
+  return (void*)res;
+}
+
+const char *StripPathPrefix(const char *filepath,
+                            const char *strip_path_prefix) {
+  if (!filepath) return nullptr;
+  if (!strip_path_prefix) return filepath;
+  const char *res = filepath;
+  if (const char *pos = internal_strstr(filepath, strip_path_prefix))
+    res = pos + internal_strlen(strip_path_prefix);
+  if (res[0] == '.' && res[1] == '/')
+    res += 2;
+  return res;
+}
+
+const char *StripModuleName(const char *module) {
+  if (!module)
+    return nullptr;
+  if (SANITIZER_WINDOWS) {
+    // On Windows, both slash and backslash are possible.
+    // Pick the one that goes last.
+    if (const char *bslash_pos = internal_strrchr(module, '\\'))
+      return StripModuleName(bslash_pos + 1);
+  }
+  if (const char *slash_pos = internal_strrchr(module, '/')) {
+    return slash_pos + 1;
+  }
+  return module;
+}
+
+void ReportErrorSummary(const char *error_message) {
+  if (!common_flags()->print_summary)
+    return;
+  InternalScopedString buff(kMaxSummaryLength);
+  buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
+  __sanitizer_report_error_summary(buff.data());
+}
+
+#ifndef SANITIZER_GO
+void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
+  if (!common_flags()->print_summary)
+    return;
+  InternalScopedString buff(kMaxSummaryLength);
+  buff.append("%s ", error_type);
+  RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
+              common_flags()->strip_path_prefix);
+  ReportErrorSummary(buff.data());
+}
+#endif
+
+void LoadedModule::set(const char *module_name, uptr base_address) {
+  clear();
+  full_name_ = internal_strdup(module_name);
+  base_address_ = base_address;
+}
+
+void LoadedModule::clear() {
+  InternalFree(full_name_);
+  full_name_ = nullptr;
+  while (!ranges_.empty()) {
+    AddressRange *r = ranges_.front();
+    ranges_.pop_front();
+    InternalFree(r);
+  }
+}
+
+void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
+  void *mem = InternalAlloc(sizeof(AddressRange));
+  AddressRange *r = new(mem) AddressRange(beg, end, executable);
+  ranges_.push_back(r);
+}
+
+bool LoadedModule::containsAddress(uptr address) const {
+  for (Iterator iter = ranges(); iter.hasNext();) {
+    const AddressRange *r = iter.next();
+    if (r->beg <= address && address < r->end)
+      return true;
+  }
+  return false;
+}
+
+static atomic_uintptr_t g_total_mmaped;
+
+void IncreaseTotalMmap(uptr size) {
+  if (!common_flags()->mmap_limit_mb) return;
+  uptr total_mmaped =
+      atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
+  // Since for now mmap_limit_mb is not a user-facing flag, just kill
+  // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
+  RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
+}
+
+void DecreaseTotalMmap(uptr size) {
+  if (!common_flags()->mmap_limit_mb) return;
+  atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
+}
+
+bool TemplateMatch(const char *templ, const char *str) {
+  if ((!str) || str[0] == 0)
+    return false;
+  bool start = false;
+  if (templ && templ[0] == '^') {
+    start = true;
+    templ++;
+  }
+  bool asterisk = false;
+  while (templ && templ[0]) {
+    if (templ[0] == '*') {
+      templ++;
+      start = false;
+      asterisk = true;
+      continue;
+    }
+    if (templ[0] == '$')
+      return str[0] == 0 || asterisk;
+    if (str[0] == 0)
+      return false;
+    char *tpos = (char*)internal_strchr(templ, '*');
+    char *tpos1 = (char*)internal_strchr(templ, '$');
+    if ((!tpos) || (tpos1 && tpos1 < tpos))
+      tpos = tpos1;
+    if (tpos)
+      tpos[0] = 0;
+    const char *str0 = str;
+    const char *spos = internal_strstr(str, templ);
+    str = spos + internal_strlen(templ);
+    templ = tpos;
+    if (tpos)
+      tpos[0] = tpos == tpos1 ? '$' : '*';
+    if (!spos)
+      return false;
+    if (start && spos != str0)
+      return false;
+    start = false;
+    asterisk = false;
+  }
+  return true;
+}
+
+static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
+
+char *FindPathToBinary(const char *name) {
+  const char *path = GetEnv("PATH");
+  if (!path)
+    return nullptr;
+  uptr name_len = internal_strlen(name);
+  InternalScopedBuffer<char> buffer(kMaxPathLength);
+  const char *beg = path;
+  while (true) {
+    const char *end = internal_strchrnul(beg, kPathSeparator);
+    uptr prefix_len = end - beg;
+    if (prefix_len + name_len + 2 <= kMaxPathLength) {
+      internal_memcpy(buffer.data(), beg, prefix_len);
+      buffer[prefix_len] = '/';
+      internal_memcpy(&buffer[prefix_len + 1], name, name_len);
+      buffer[prefix_len + 1 + name_len] = '\0';
+      if (FileExists(buffer.data()))
+        return internal_strdup(buffer.data());
+    }
+    if (*end == '\0') break;
+    beg = end + 1;
+  }
+  return nullptr;
+}
+
+static char binary_name_cache_str[kMaxPathLength];
+static char process_name_cache_str[kMaxPathLength];
+
+const char *GetProcessName() {
+  return process_name_cache_str;
+}
+
+static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
+  ReadLongProcessName(buf, buf_len);
+  char *s = const_cast<char *>(StripModuleName(buf));
+  uptr len = internal_strlen(s);
+  if (s != buf) {
+    internal_memmove(buf, s, len);
+    buf[len] = '\0';
+  }
+  return len;
+}
+
+void UpdateProcessName() {
+  ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
+}
+
+// Call once to make sure that binary_name_cache_str is initialized
+void CacheBinaryName() {
+  if (binary_name_cache_str[0] != '\0')
+    return;
+  ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
+  ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
+}
+
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
+  CacheBinaryName();
+  uptr name_len = internal_strlen(binary_name_cache_str);
+  name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
+  if (buf_len == 0)
+    return 0;
+  internal_memcpy(buf, binary_name_cache_str, name_len);
+  buf[name_len] = '\0';
+  return name_len;
+}
+
+void PrintCmdline() {
+  char **argv = GetArgv();
+  if (!argv) return;
+  Printf("\nCommand: ");
+  for (uptr i = 0; argv[i]; ++i)
+    Printf("%s ", argv[i]);
+  Printf("\n\n");
+}
+
+} // namespace __sanitizer
+
+using namespace __sanitizer;  // NOLINT
+
+extern "C" {
+void __sanitizer_set_report_path(const char *path) {
+  report_file.SetReportPath(path);
+}
+
+void __sanitizer_report_error_summary(const char *error_summary) {
+  Printf("%s\n", error_summary);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_set_death_callback(void (*callback)(void)) {
+  SetUserDieCallback(callback);
+}
+} // extern "C"
diff --git a/lsan/src/sanitizer_common_libcdep.cc b/lsan/src/sanitizer_common_libcdep.cc
new file mode 100644 (file)
index 0000000..5a76c4e
--- /dev/null
@@ -0,0 +1,134 @@
+//===-- sanitizer_common_libcdep.cc ---------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_stackdepot.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+
+#if SANITIZER_POSIX
+#include "sanitizer_posix.h"
+#endif
+
+namespace __sanitizer {
+
+bool ReportFile::SupportsColors() {
+  SpinMutexLock l(mu);
+  ReopenIfNecessary();
+  return SupportsColoredOutput(fd);
+}
+
+bool ColorizeReports() {
+  // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color
+  // printing on Windows.
+  if (SANITIZER_WINDOWS)
+    return false;
+
+  const char *flag = common_flags()->color;
+  return internal_strcmp(flag, "always") == 0 ||
+         (internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors());
+}
+
+static void (*sandboxing_callback)();
+void SetSandboxingCallback(void (*f)()) {
+  sandboxing_callback = f;
+}
+
+void ReportErrorSummary(const char *error_type, StackTrace *stack) {
+  if (!common_flags()->print_summary)
+    return;
+  if (stack->size == 0) {
+    ReportErrorSummary(error_type);
+    return;
+  }
+  // Currently, we include the first stack frame into the report summary.
+  // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
+  uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
+  SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
+  ReportErrorSummary(error_type, frame->info);
+  frame->ClearAll();
+}
+
+static void (*SoftRssLimitExceededCallback)(bool exceeded);
+void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
+  CHECK_EQ(SoftRssLimitExceededCallback, nullptr);
+  SoftRssLimitExceededCallback = Callback;
+}
+
+void BackgroundThread(void *arg) {
+  uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
+  uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
+  uptr prev_reported_rss = 0;
+  uptr prev_reported_stack_depot_size = 0;
+  bool reached_soft_rss_limit = false;
+  while (true) {
+    SleepForMillis(100);
+    uptr current_rss_mb = GetRSS() >> 20;
+    if (Verbosity()) {
+      // If RSS has grown 10% since last time, print some information.
+      if (prev_reported_rss * 11 / 10 < current_rss_mb) {
+        Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
+        prev_reported_rss = current_rss_mb;
+      }
+      // If stack depot has grown 10% since last time, print it too.
+      StackDepotStats *stack_depot_stats = StackDepotGetStats();
+      if (prev_reported_stack_depot_size * 11 / 10 <
+          stack_depot_stats->allocated) {
+        Printf("%s: StackDepot: %zd ids; %zdM allocated\n",
+               SanitizerToolName,
+               stack_depot_stats->n_uniq_ids,
+               stack_depot_stats->allocated >> 20);
+        prev_reported_stack_depot_size = stack_depot_stats->allocated;
+      }
+    }
+    // Check RSS against the limit.
+    if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
+      Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
+             SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
+      DumpProcessMap();
+      Die();
+    }
+    if (soft_rss_limit_mb) {
+      if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
+        reached_soft_rss_limit = true;
+        Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
+               SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
+        if (SoftRssLimitExceededCallback)
+          SoftRssLimitExceededCallback(true);
+      } else if (soft_rss_limit_mb >= current_rss_mb &&
+                 reached_soft_rss_limit) {
+        reached_soft_rss_limit = false;
+        if (SoftRssLimitExceededCallback)
+          SoftRssLimitExceededCallback(false);
+      }
+    }
+  }
+}
+
+void MaybeStartBackgroudThread() {
+#if SANITIZER_LINUX  // Need to implement/test on other platforms.
+  // Start the background thread if one of the rss limits is given.
+  if (!common_flags()->hard_rss_limit_mb &&
+      !common_flags()->soft_rss_limit_mb) return;
+  if (!&real_pthread_create) return;  // Can't spawn the thread anyway.
+  internal_start_thread(BackgroundThread, nullptr);
+#endif
+}
+
+}  // namespace __sanitizer
+
+void NOINLINE
+__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args) {
+  PrepareForSandboxing(args);
+  if (sandboxing_callback)
+    sandboxing_callback();
+}
diff --git a/lsan/src/sanitizer_coverage_libcdep.cc b/lsan/src/sanitizer_coverage_libcdep.cc
new file mode 100644 (file)
index 0000000..c678804
--- /dev/null
@@ -0,0 +1,956 @@
+//===-- sanitizer_coverage.cc ---------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Sanitizer Coverage.
+// This file implements run-time support for a poor man's coverage tool.
+//
+// Compiler instrumentation:
+// For every interesting basic block the compiler injects the following code:
+// if (Guard < 0) {
+//    __sanitizer_cov(&Guard);
+// }
+// At the module start up time __sanitizer_cov_module_init sets the guards
+// to consecutive negative numbers (-1, -2, -3, ...).
+// It's fine to call __sanitizer_cov more than once for a given block.
+//
+// Run-time:
+//  - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC).
+//    and atomically set Guard to -Guard.
+//  - __sanitizer_cov_dump: dump the coverage data to disk.
+//  For every module of the current process that has coverage data
+//  this will create a file module_name.PID.sancov.
+//
+// The file format is simple: the first 8 bytes is the magic,
+// one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the
+// magic defines the size of the following offsets.
+// The rest of the data is the offsets in the module.
+//
+// Eventually, this coverage implementation should be obsoleted by a more
+// powerful general purpose Clang/LLVM coverage instrumentation.
+// Consider this implementation as prototype.
+//
+// FIXME: support (or at least test with) dlclose.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+#include "sanitizer_flags.h"
+
+static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL;
+static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL;
+
+static atomic_uint32_t dump_once_guard;  // Ensure that CovDump runs only once.
+
+static atomic_uintptr_t coverage_counter;
+static atomic_uintptr_t caller_callee_counter;
+
+static void ResetGlobalCounters() {
+  return atomic_store(&coverage_counter, 0, memory_order_relaxed);
+  return atomic_store(&caller_callee_counter, 0, memory_order_relaxed);
+}
+
+// pc_array is the array containing the covered PCs.
+// To make the pc_array thread- and async-signal-safe it has to be large enough.
+// 128M counters "ought to be enough for anybody" (4M on 32-bit).
+
+// With coverage_direct=1 in ASAN_OPTIONS, pc_array memory is mapped to a file.
+// In this mode, __sanitizer_cov_dump does nothing, and CovUpdateMapping()
+// dump current memory layout to another file.
+
+static bool cov_sandboxed = false;
+static fd_t cov_fd = kInvalidFd;
+static unsigned int cov_max_block_size = 0;
+static bool coverage_enabled = false;
+static const char *coverage_dir;
+
+namespace __sanitizer {
+
+class CoverageData {
+ public:
+  void Init();
+  void Enable();
+  void Disable();
+  void ReInit();
+  void BeforeFork();
+  void AfterFork(int child_pid);
+  void Extend(uptr npcs);
+  void Add(uptr pc, u32 *guard);
+  void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+                 uptr cache_size);
+  void DumpCallerCalleePairs();
+  void DumpTrace();
+  void DumpAsBitSet();
+  void DumpCounters();
+  void DumpOffsets();
+  void DumpAll();
+
+  ALWAYS_INLINE
+  void TraceBasicBlock(s32 *id);
+
+  void InitializeGuardArray(s32 *guards);
+  void InitializeGuards(s32 *guards, uptr n, const char *module_name,
+                        uptr caller_pc);
+  void InitializeCounters(u8 *counters, uptr n);
+  void ReinitializeGuards();
+  uptr GetNumberOf8bitCounters();
+  uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset);
+
+  uptr *data();
+  uptr size();
+
+ private:
+  void DirectOpen();
+  void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end);
+
+  // Maximal size pc array may ever grow.
+  // We MmapNoReserve this space to ensure that the array is contiguous.
+  static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(
+      1 << (SANITIZER_ANDROID ? 24 : (SANITIZER_WINDOWS ? 27 : 26)),
+      1 << 27);
+  // The amount file mapping for the pc array is grown by.
+  static const uptr kPcArrayMmapSize = 64 * 1024;
+
+  // pc_array is allocated with MmapNoReserveOrDie and so it uses only as
+  // much RAM as it really needs.
+  uptr *pc_array;
+  // Index of the first available pc_array slot.
+  atomic_uintptr_t pc_array_index;
+  // Array size.
+  atomic_uintptr_t pc_array_size;
+  // Current file mapped size of the pc array.
+  uptr pc_array_mapped_size;
+  // Descriptor of the file mapped pc array.
+  fd_t pc_fd;
+
+  // Vector of coverage guard arrays, protected by mu.
+  InternalMmapVectorNoCtor<s32*> guard_array_vec;
+
+  struct NamedPcRange {
+    const char *copied_module_name;
+    uptr beg, end; // elements [beg,end) in pc_array.
+  };
+
+  // Vector of module and compilation unit pc ranges.
+  InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec;
+  InternalMmapVectorNoCtor<NamedPcRange> module_name_vec;
+
+  struct CounterAndSize {
+    u8 *counters;
+    uptr n;
+  };
+
+  InternalMmapVectorNoCtor<CounterAndSize> counters_vec;
+  uptr num_8bit_counters;
+
+  // Caller-Callee (cc) array, size and current index.
+  static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
+  uptr **cc_array;
+  atomic_uintptr_t cc_array_index;
+  atomic_uintptr_t cc_array_size;
+
+  // Tracing event array, size and current pointer.
+  // We record all events (basic block entries) in a global buffer of u32
+  // values. Each such value is the index in pc_array.
+  // So far the tracing is highly experimental:
+  //   - not thread-safe;
+  //   - does not support long traces;
+  //   - not tuned for performance.
+  static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30);
+  u32 *tr_event_array;
+  uptr tr_event_array_size;
+  u32 *tr_event_pointer;
+  static const uptr kTrPcArrayMaxSize    = FIRST_32_SECOND_64(1 << 22, 1 << 27);
+
+  StaticSpinMutex mu;
+};
+
+static CoverageData coverage_data;
+
+void CovUpdateMapping(const char *path, uptr caller_pc = 0);
+
+void CoverageData::DirectOpen() {
+  InternalScopedString path(kMaxPathLength);
+  internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw",
+                    coverage_dir, internal_getpid());
+  pc_fd = OpenFile(path.data(), RdWr);
+  if (pc_fd == kInvalidFd) {
+    Report("Coverage: failed to open %s for reading/writing\n", path.data());
+    Die();
+  }
+
+  pc_array_mapped_size = 0;
+  CovUpdateMapping(coverage_dir);
+}
+
+void CoverageData::Init() {
+  pc_fd = kInvalidFd;
+}
+
+void CoverageData::Enable() {
+  if (pc_array)
+    return;
+  pc_array = reinterpret_cast<uptr *>(
+      MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit"));
+  atomic_store(&pc_array_index, 0, memory_order_relaxed);
+  if (common_flags()->coverage_direct) {
+    atomic_store(&pc_array_size, 0, memory_order_relaxed);
+  } else {
+    atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
+  }
+
+  cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
+      sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
+  atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
+  atomic_store(&cc_array_index, 0, memory_order_relaxed);
+
+  // Allocate tr_event_array with a guard page at the end.
+  tr_event_array = reinterpret_cast<u32 *>(MmapNoReserveOrDie(
+      sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(),
+      "CovInit::tr_event_array"));
+  MprotectNoAccess(
+      reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]),
+      GetMmapGranularity());
+  tr_event_array_size = kTrEventArrayMaxSize;
+  tr_event_pointer = tr_event_array;
+
+  num_8bit_counters = 0;
+}
+
+void CoverageData::InitializeGuardArray(s32 *guards) {
+  Enable();  // Make sure coverage is enabled at this point.
+  s32 n = guards[0];
+  for (s32 j = 1; j <= n; j++) {
+    uptr idx = atomic_load_relaxed(&pc_array_index);
+    atomic_store_relaxed(&pc_array_index, idx + 1);
+    guards[j] = -static_cast<s32>(idx + 1);
+  }
+}
+
+void CoverageData::Disable() {
+  if (pc_array) {
+    UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize);
+    pc_array = nullptr;
+  }
+  if (cc_array) {
+    UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize);
+    cc_array = nullptr;
+  }
+  if (tr_event_array) {
+    UnmapOrDie(tr_event_array,
+               sizeof(tr_event_array[0]) * kTrEventArrayMaxSize +
+                   GetMmapGranularity());
+    tr_event_array = nullptr;
+    tr_event_pointer = nullptr;
+  }
+  if (pc_fd != kInvalidFd) {
+    CloseFile(pc_fd);
+    pc_fd = kInvalidFd;
+  }
+}
+
+void CoverageData::ReinitializeGuards() {
+  // Assuming single thread.
+  atomic_store(&pc_array_index, 0, memory_order_relaxed);
+  for (uptr i = 0; i < guard_array_vec.size(); i++)
+    InitializeGuardArray(guard_array_vec[i]);
+}
+
+void CoverageData::ReInit() {
+  Disable();
+  if (coverage_enabled) {
+    if (common_flags()->coverage_direct) {
+      // In memory-mapped mode we must extend the new file to the known array
+      // size.
+      uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
+      uptr npcs = size / sizeof(uptr);
+      Enable();
+      if (size) Extend(npcs);
+      if (coverage_enabled) CovUpdateMapping(coverage_dir);
+    } else {
+      Enable();
+    }
+  }
+  // Re-initialize the guards.
+  // We are single-threaded now, no need to grab any lock.
+  CHECK_EQ(atomic_load(&pc_array_index, memory_order_relaxed), 0);
+  ReinitializeGuards();
+}
+
+void CoverageData::BeforeFork() {
+  mu.Lock();
+}
+
+void CoverageData::AfterFork(int child_pid) {
+  // We are single-threaded so it's OK to release the lock early.
+  mu.Unlock();
+  if (child_pid == 0) ReInit();
+}
+
+// Extend coverage PC array to fit additional npcs elements.
+void CoverageData::Extend(uptr npcs) {
+  if (!common_flags()->coverage_direct) return;
+  SpinMutexLock l(&mu);
+
+  uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
+  size += npcs * sizeof(uptr);
+
+  if (coverage_enabled && size > pc_array_mapped_size) {
+    if (pc_fd == kInvalidFd) DirectOpen();
+    CHECK_NE(pc_fd, kInvalidFd);
+
+    uptr new_mapped_size = pc_array_mapped_size;
+    while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize;
+    CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize);
+
+    // Extend the file and map the new space at the end of pc_array.
+    uptr res = internal_ftruncate(pc_fd, new_mapped_size);
+    int err;
+    if (internal_iserror(res, &err)) {
+      Printf("failed to extend raw coverage file: %d\n", err);
+      Die();
+    }
+
+    uptr next_map_base = ((uptr)pc_array) + pc_array_mapped_size;
+    void *p = MapWritableFileToMemory((void *)next_map_base,
+                                      new_mapped_size - pc_array_mapped_size,
+                                      pc_fd, pc_array_mapped_size);
+    CHECK_EQ((uptr)p, next_map_base);
+    pc_array_mapped_size = new_mapped_size;
+  }
+
+  atomic_store(&pc_array_size, size, memory_order_release);
+}
+
+void CoverageData::InitializeCounters(u8 *counters, uptr n) {
+  if (!counters) return;
+  CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0);
+  n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned.
+  SpinMutexLock l(&mu);
+  counters_vec.push_back({counters, n});
+  num_8bit_counters += n;
+}
+
+void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg,
+                                       uptr range_end) {
+  auto sym = Symbolizer::GetOrInit();
+  if (!sym)
+    return;
+  const char *module_name = sym->GetModuleNameForPc(caller_pc);
+  if (!module_name) return;
+  if (module_name_vec.empty() ||
+      module_name_vec.back().copied_module_name != module_name)
+    module_name_vec.push_back({module_name, range_beg, range_end});
+  else
+    module_name_vec.back().end = range_end;
+}
+
+void CoverageData::InitializeGuards(s32 *guards, uptr n,
+                                    const char *comp_unit_name,
+                                    uptr caller_pc) {
+  // The array 'guards' has n+1 elements, we use the element zero
+  // to store 'n'.
+  CHECK_LT(n, 1 << 30);
+  guards[0] = static_cast<s32>(n);
+  InitializeGuardArray(guards);
+  SpinMutexLock l(&mu);
+  uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed);
+  uptr range_beg = range_end - n;
+  comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end});
+  guard_array_vec.push_back(guards);
+  UpdateModuleNameVec(caller_pc, range_beg, range_end);
+}
+
+static const uptr kBundleCounterBits = 16;
+
+// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64
+// we insert the global counter into the first 16 bits of the PC.
+uptr BundlePcAndCounter(uptr pc, uptr counter) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return pc;
+  static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1;
+  if (counter > kMaxCounter)
+    counter = kMaxCounter;
+  CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits));
+  return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits));
+}
+
+uptr UnbundlePc(uptr bundle) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return bundle;
+  return (bundle << kBundleCounterBits) >> kBundleCounterBits;
+}
+
+uptr UnbundleCounter(uptr bundle) {
+  if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+    return 0;
+  return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits);
+}
+
+// If guard is negative, atomically set it to -guard and store the PC in
+// pc_array.
+void CoverageData::Add(uptr pc, u32 *guard) {
+  atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
+  s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed);
+  if (guard_value >= 0) return;
+
+  atomic_store(atomic_guard, -guard_value, memory_order_relaxed);
+  if (!pc_array) return;
+
+  uptr idx = -guard_value - 1;
+  if (idx >= atomic_load(&pc_array_index, memory_order_acquire))
+    return;  // May happen after fork when pc_array_index becomes 0.
+  CHECK_LT(idx * sizeof(uptr),
+           atomic_load(&pc_array_size, memory_order_acquire));
+  uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+  pc_array[idx] = BundlePcAndCounter(pc, counter);
+}
+
+// Registers a pair caller=>callee.
+// When a given caller is seen for the first time, the callee_cache is added
+// to the global array cc_array, callee_cache[0] is set to caller and
+// callee_cache[1] is set to cache_size.
+// Then we are trying to add callee to callee_cache [2,cache_size) if it is
+// not there yet.
+// If the cache is full we drop the callee (may want to fix this later).
+void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+                             uptr cache_size) {
+  if (!cc_array) return;
+  atomic_uintptr_t *atomic_callee_cache =
+      reinterpret_cast<atomic_uintptr_t *>(callee_cache);
+  uptr zero = 0;
+  if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller,
+                                     memory_order_seq_cst)) {
+    uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed);
+    CHECK_LT(idx * sizeof(uptr),
+             atomic_load(&cc_array_size, memory_order_acquire));
+    callee_cache[1] = cache_size;
+    cc_array[idx] = callee_cache;
+  }
+  CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller);
+  for (uptr i = 2; i < cache_size; i++) {
+    uptr was = 0;
+    if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee,
+                                       memory_order_seq_cst)) {
+      atomic_fetch_add(&caller_callee_counter, 1, memory_order_relaxed);
+      return;
+    }
+    if (was == callee)  // Already have this callee.
+      return;
+  }
+}
+
+uptr CoverageData::GetNumberOf8bitCounters() {
+  return num_8bit_counters;
+}
+
+// Map every 8bit counter to a 8-bit bitset and clear the counter.
+uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) {
+  uptr num_new_bits = 0;
+  uptr cur = 0;
+  // For better speed we map 8 counters to 8 bytes of bitset at once.
+  static const uptr kBatchSize = 8;
+  CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0);
+  for (uptr i = 0, len = counters_vec.size(); i < len; i++) {
+    u8 *c = counters_vec[i].counters;
+    uptr n = counters_vec[i].n;
+    CHECK_EQ(n % 16, 0);
+    CHECK_EQ(cur % kBatchSize, 0);
+    CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0);
+    if (!bitset) {
+      internal_bzero_aligned16(c, n);
+      cur += n;
+      continue;
+    }
+    for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) {
+      CHECK_LT(cur, num_8bit_counters);
+      u64 *pc64 = reinterpret_cast<u64*>(c + j);
+      u64 *pb64 = reinterpret_cast<u64*>(bitset + cur);
+      u64 c64 = *pc64;
+      u64 old_bits_64 = *pb64;
+      u64 new_bits_64 = old_bits_64;
+      if (c64) {
+        *pc64 = 0;
+        for (uptr k = 0; k < kBatchSize; k++) {
+          u64 x = (c64 >> (8 * k)) & 0xff;
+          if (x) {
+            u64 bit = 0;
+            /**/ if (x >= 128) bit = 128;
+            else if (x >= 32) bit = 64;
+            else if (x >= 16) bit = 32;
+            else if (x >= 8) bit = 16;
+            else if (x >= 4) bit = 8;
+            else if (x >= 3) bit = 4;
+            else if (x >= 2) bit = 2;
+            else if (x >= 1) bit = 1;
+            u64 mask = bit << (8 * k);
+            if (!(new_bits_64 & mask)) {
+              num_new_bits++;
+              new_bits_64 |= mask;
+            }
+          }
+        }
+        *pb64 = new_bits_64;
+      }
+    }
+  }
+  CHECK_EQ(cur, num_8bit_counters);
+  return num_new_bits;
+}
+
+uptr *CoverageData::data() {
+  return pc_array;
+}
+
+uptr CoverageData::size() {
+  return atomic_load(&pc_array_index, memory_order_relaxed);
+}
+
+// Block layout for packed file format: header, followed by module name (no
+// trailing zero), followed by data blob.
+struct CovHeader {
+  int pid;
+  unsigned int module_name_length;
+  unsigned int data_length;
+};
+
+static void CovWritePacked(int pid, const char *module, const void *blob,
+                           unsigned int blob_size) {
+  if (cov_fd == kInvalidFd) return;
+  unsigned module_name_length = internal_strlen(module);
+  CovHeader header = {pid, module_name_length, blob_size};
+
+  if (cov_max_block_size == 0) {
+    // Writing to a file. Just go ahead.
+    WriteToFile(cov_fd, &header, sizeof(header));
+    WriteToFile(cov_fd, module, module_name_length);
+    WriteToFile(cov_fd, blob, blob_size);
+  } else {
+    // Writing to a socket. We want to split the data into appropriately sized
+    // blocks.
+    InternalScopedBuffer<char> block(cov_max_block_size);
+    CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data());
+    uptr header_size_with_module = sizeof(header) + module_name_length;
+    CHECK_LT(header_size_with_module, cov_max_block_size);
+    unsigned int max_payload_size =
+        cov_max_block_size - header_size_with_module;
+    char *block_pos = block.data();
+    internal_memcpy(block_pos, &header, sizeof(header));
+    block_pos += sizeof(header);
+    internal_memcpy(block_pos, module, module_name_length);
+    block_pos += module_name_length;
+    char *block_data_begin = block_pos;
+    const char *blob_pos = (const char *)blob;
+    while (blob_size > 0) {
+      unsigned int payload_size = Min(blob_size, max_payload_size);
+      blob_size -= payload_size;
+      internal_memcpy(block_data_begin, blob_pos, payload_size);
+      blob_pos += payload_size;
+      ((CovHeader *)block.data())->data_length = payload_size;
+      WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size);
+    }
+  }
+}
+
+// If packed = false: <name>.<pid>.<sancov> (name = module name).
+// If packed = true and name == 0: <pid>.<sancov>.<packed>.
+// If packed = true and name != 0: <name>.<sancov>.<packed> (name is
+// user-supplied).
+static fd_t CovOpenFile(InternalScopedString *path, bool packed,
+                       const char *name, const char *extension = "sancov") {
+  path->clear();
+  if (!packed) {
+    CHECK(name);
+    path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
+                extension);
+  } else {
+    if (!name)
+      path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
+                  extension);
+    else
+      path->append("%s/%s.%s.packed", coverage_dir, name, extension);
+  }
+  error_t err;
+  fd_t fd = OpenFile(path->data(), WrOnly, &err);
+  if (fd == kInvalidFd)
+    Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n",
+           path->data(), err);
+  return fd;
+}
+
+// Dump trace PCs and trace events into two separate files.
+void CoverageData::DumpTrace() {
+  uptr max_idx = tr_event_pointer - tr_event_array;
+  if (!max_idx) return;
+  auto sym = Symbolizer::GetOrInit();
+  if (!sym)
+    return;
+  InternalScopedString out(32 << 20);
+  for (uptr i = 0, n = size(); i < n; i++) {
+    const char *module_name = "<unknown>";
+    uptr module_address = 0;
+    sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
+                                     &module_address);
+    out.append("%s 0x%zx\n", module_name, module_address);
+  }
+  InternalScopedString path(kMaxPathLength);
+  fd_t fd = CovOpenFile(&path, false, "trace-points");
+  if (fd == kInvalidFd) return;
+  WriteToFile(fd, out.data(), out.length());
+  CloseFile(fd);
+
+  fd = CovOpenFile(&path, false, "trace-compunits");
+  if (fd == kInvalidFd) return;
+  out.clear();
+  for (uptr i = 0; i < comp_unit_name_vec.size(); i++)
+    out.append("%s\n", comp_unit_name_vec[i].copied_module_name);
+  WriteToFile(fd, out.data(), out.length());
+  CloseFile(fd);
+
+  fd = CovOpenFile(&path, false, "trace-events");
+  if (fd == kInvalidFd) return;
+  uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]);
+  u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array);
+  // The trace file could be huge, and may not be written with a single syscall.
+  while (bytes_to_write) {
+    uptr actually_written;
+    if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) &&
+        actually_written <= bytes_to_write) {
+      bytes_to_write -= actually_written;
+      event_bytes += actually_written;
+    } else {
+      break;
+    }
+  }
+  CloseFile(fd);
+  VReport(1, " CovDump: Trace: %zd PCs written\n", size());
+  VReport(1, " CovDump: Trace: %zd Events written\n", max_idx);
+}
+
+// This function dumps the caller=>callee pairs into a file as a sequence of
+// lines like "module_name offset".
+void CoverageData::DumpCallerCalleePairs() {
+  uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed);
+  if (!max_idx) return;
+  auto sym = Symbolizer::GetOrInit();
+  if (!sym)
+    return;
+  InternalScopedString out(32 << 20);
+  uptr total = 0;
+  for (uptr i = 0; i < max_idx; i++) {
+    uptr *cc_cache = cc_array[i];
+    CHECK(cc_cache);
+    uptr caller = cc_cache[0];
+    uptr n_callees = cc_cache[1];
+    const char *caller_module_name = "<unknown>";
+    uptr caller_module_address = 0;
+    sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name,
+                                     &caller_module_address);
+    for (uptr j = 2; j < n_callees; j++) {
+      uptr callee = cc_cache[j];
+      if (!callee) break;
+      total++;
+      const char *callee_module_name = "<unknown>";
+      uptr callee_module_address = 0;
+      sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name,
+                                       &callee_module_address);
+      out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name,
+                 caller_module_address, callee_module_name,
+                 callee_module_address);
+    }
+  }
+  InternalScopedString path(kMaxPathLength);
+  fd_t fd = CovOpenFile(&path, false, "caller-callee");
+  if (fd == kInvalidFd) return;
+  WriteToFile(fd, out.data(), out.length());
+  CloseFile(fd);
+  VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
+}
+
+// Record the current PC into the event buffer.
+// Every event is a u32 value (index in tr_pc_array_index) so we compute
+// it once and then cache in the provided 'cache' storage.
+//
+// This function will eventually be inlined by the compiler.
+void CoverageData::TraceBasicBlock(s32 *id) {
+  // Will trap here if
+  //  1. coverage is not enabled at run-time.
+  //  2. The array tr_event_array is full.
+  *tr_event_pointer = static_cast<u32>(*id - 1);
+  tr_event_pointer++;
+}
+
+void CoverageData::DumpCounters() {
+  if (!common_flags()->coverage_counters) return;
+  uptr n = coverage_data.GetNumberOf8bitCounters();
+  if (!n) return;
+  InternalScopedBuffer<u8> bitset(n);
+  coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data());
+  InternalScopedString path(kMaxPathLength);
+
+  for (uptr m = 0; m < module_name_vec.size(); m++) {
+    auto r = module_name_vec[m];
+    CHECK(r.copied_module_name);
+    CHECK_LE(r.beg, r.end);
+    CHECK_LE(r.end, size());
+    const char *base_name = StripModuleName(r.copied_module_name);
+    fd_t fd =
+        CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov");
+    if (fd == kInvalidFd) return;
+    WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg);
+    CloseFile(fd);
+    VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg,
+            base_name);
+  }
+}
+
+void CoverageData::DumpAsBitSet() {
+  if (!common_flags()->coverage_bitset) return;
+  if (!size()) return;
+  InternalScopedBuffer<char> out(size());
+  InternalScopedString path(kMaxPathLength);
+  for (uptr m = 0; m < module_name_vec.size(); m++) {
+    uptr n_set_bits = 0;
+    auto r = module_name_vec[m];
+    CHECK(r.copied_module_name);
+    CHECK_LE(r.beg, r.end);
+    CHECK_LE(r.end, size());
+    for (uptr i = r.beg; i < r.end; i++) {
+      uptr pc = UnbundlePc(pc_array[i]);
+      out[i] = pc ? '1' : '0';
+      if (pc)
+        n_set_bits++;
+    }
+    const char *base_name = StripModuleName(r.copied_module_name);
+    fd_t fd = CovOpenFile(&path, /* packed */false, base_name, "bitset-sancov");
+    if (fd == kInvalidFd) return;
+    WriteToFile(fd, out.data() + r.beg, r.end - r.beg);
+    CloseFile(fd);
+    VReport(1,
+            " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n",
+            r.end - r.beg, base_name, n_set_bits);
+  }
+}
+
+void CoverageData::DumpOffsets() {
+  auto sym = Symbolizer::GetOrInit();
+  if (!common_flags()->coverage_pcs) return;
+  CHECK_NE(sym, nullptr);
+  InternalMmapVector<uptr> offsets(0);
+  InternalScopedString path(kMaxPathLength);
+  for (uptr m = 0; m < module_name_vec.size(); m++) {
+    offsets.clear();
+    uptr num_words_for_magic = SANITIZER_WORDSIZE == 64 ? 1 : 2;
+    for (uptr i = 0; i < num_words_for_magic; i++)
+      offsets.push_back(0);
+    auto r = module_name_vec[m];
+    CHECK(r.copied_module_name);
+    CHECK_LE(r.beg, r.end);
+    CHECK_LE(r.end, size());
+    for (uptr i = r.beg; i < r.end; i++) {
+      uptr pc = UnbundlePc(pc_array[i]);
+      uptr counter = UnbundleCounter(pc_array[i]);
+      if (!pc) continue; // Not visited.
+      uptr offset = 0;
+      sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset);
+      offsets.push_back(BundlePcAndCounter(offset, counter));
+    }
+
+    CHECK_GE(offsets.size(), num_words_for_magic);
+    SortArray(offsets.data(), offsets.size());
+    for (uptr i = 0; i < offsets.size(); i++)
+      offsets[i] = UnbundlePc(offsets[i]);
+
+    uptr num_offsets = offsets.size() - num_words_for_magic;
+    u64 *magic_p = reinterpret_cast<u64*>(offsets.data());
+    CHECK_EQ(*magic_p, 0ULL);
+    // FIXME: we may want to write 32-bit offsets even in 64-mode
+    // if all the offsets are small enough.
+    *magic_p = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32;
+
+    const char *module_name = StripModuleName(r.copied_module_name);
+    if (cov_sandboxed) {
+      if (cov_fd != kInvalidFd) {
+        CovWritePacked(internal_getpid(), module_name, offsets.data(),
+                       offsets.size() * sizeof(offsets[0]));
+        VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets);
+      }
+    } else {
+      // One file per module per process.
+      fd_t fd = CovOpenFile(&path, false /* packed */, module_name);
+      if (fd == kInvalidFd) continue;
+      WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0]));
+      CloseFile(fd);
+      VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets);
+    }
+  }
+  if (cov_fd != kInvalidFd)
+    CloseFile(cov_fd);
+}
+
+void CoverageData::DumpAll() {
+  if (!coverage_enabled || common_flags()->coverage_direct) return;
+  if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
+    return;
+  DumpAsBitSet();
+  DumpCounters();
+  DumpTrace();
+  DumpOffsets();
+  DumpCallerCalleePairs();
+}
+
+void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+  if (!args) return;
+  if (!coverage_enabled) return;
+  cov_sandboxed = args->coverage_sandboxed;
+  if (!cov_sandboxed) return;
+  cov_max_block_size = args->coverage_max_block_size;
+  if (args->coverage_fd >= 0) {
+    cov_fd = (fd_t)args->coverage_fd;
+  } else {
+    InternalScopedString path(kMaxPathLength);
+    // Pre-open the file now. The sandbox won't allow us to do it later.
+    cov_fd = CovOpenFile(&path, true /* packed */, nullptr);
+  }
+}
+
+fd_t MaybeOpenCovFile(const char *name) {
+  CHECK(name);
+  if (!coverage_enabled) return kInvalidFd;
+  InternalScopedString path(kMaxPathLength);
+  return CovOpenFile(&path, true /* packed */, name);
+}
+
+void CovBeforeFork() {
+  coverage_data.BeforeFork();
+}
+
+void CovAfterFork(int child_pid) {
+  coverage_data.AfterFork(child_pid);
+}
+
+static void MaybeDumpCoverage() {
+  if (common_flags()->coverage)
+    __sanitizer_cov_dump();
+}
+
+void InitializeCoverage(bool enabled, const char *dir) {
+  if (coverage_enabled)
+    return;  // May happen if two sanitizer enable coverage in the same process.
+  coverage_enabled = enabled;
+  coverage_dir = dir;
+  coverage_data.Init();
+  if (enabled) coverage_data.Enable();
+  if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump);
+  AddDieCallback(MaybeDumpCoverage);
+}
+
+void ReInitializeCoverage(bool enabled, const char *dir) {
+  coverage_enabled = enabled;
+  coverage_dir = dir;
+  coverage_data.ReInit();
+}
+
+void CoverageUpdateMapping() {
+  if (coverage_enabled)
+    CovUpdateMapping(coverage_dir);
+}
+
+} // namespace __sanitizer
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) {
+  coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+                    guard);
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) {
+  atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
+  if (static_cast<s32>(
+          __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0)
+    __sanitizer_cov(guard);
+}
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) {
+  coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+                          callee, callee_cache16, 16);
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
+  coverage_enabled = true;
+  coverage_dir = common_flags()->coverage_dir;
+  coverage_data.Init();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
+  coverage_data.DumpAll();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters,
+                            const char *comp_unit_name) {
+  coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC());
+  coverage_data.InitializeCounters(counters, npcs);
+  if (!common_flags()->coverage_direct) return;
+  if (SANITIZER_ANDROID && coverage_enabled) {
+    // dlopen/dlclose interceptors do not work on Android, so we rely on
+    // Extend() calls to update .sancov.map.
+    CovUpdateMapping(coverage_dir, GET_CALLER_PC());
+  }
+  coverage_data.Extend(npcs);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+sptr __sanitizer_maybe_open_cov_file(const char *name) {
+  return (sptr)MaybeOpenCovFile(name);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_total_unique_coverage() {
+  return atomic_load(&coverage_counter, memory_order_relaxed);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_total_unique_caller_callee_pairs() {
+  return atomic_load(&caller_callee_counter, memory_order_relaxed);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_func_enter(s32 *id) {
+  coverage_data.TraceBasicBlock(id);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_basic_block(s32 *id) {
+  coverage_data.TraceBasicBlock(id);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_reset_coverage() {
+  ResetGlobalCounters();
+  coverage_data.ReinitializeGuards();
+  internal_bzero_aligned16(
+      coverage_data.data(),
+      RoundUpTo(coverage_data.size() * sizeof(coverage_data.data()[0]), 16));
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_coverage_guards(uptr **data) {
+  *data = coverage_data.data();
+  return coverage_data.size();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_number_of_counters() {
+  return coverage_data.GetNumberOf8bitCounters();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) {
+  return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset);
+}
+// Default empty implementations (weak). Users should redefine them.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_cov_trace_cmp() {}
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_cov_trace_switch() {}
+} // extern "C"
diff --git a/lsan/src/sanitizer_coverage_mapping_libcdep.cc b/lsan/src/sanitizer_coverage_mapping_libcdep.cc
new file mode 100644 (file)
index 0000000..ebac681
--- /dev/null
@@ -0,0 +1,125 @@
+//===-- sanitizer_coverage_mapping.cc -------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Mmap-based implementation of sanitizer coverage.
+//
+// This is part of the implementation of code coverage that does not require
+// __sanitizer_cov_dump() call. Data is stored in 2 files per process.
+//
+// $pid.sancov.map describes process memory layout in the following text-based
+// format:
+// <pointer size in bits>  // 1 line, 32 or 64
+// <mapping start> <mapping end> <base address> <dso name> // repeated
+// ...
+// Mapping lines are NOT sorted. This file is updated every time memory layout
+// is changed (i.e. in dlopen() and dlclose() interceptors).
+//
+// $pid.sancov.raw is a binary dump of PC values, sizeof(uptr) each. Again, not
+// sorted. This file is extended by 64Kb at a time and mapped into memory. It
+// contains one or more 0 words at the end, up to the next 64Kb aligned offset.
+//
+// To convert these 2 files to the usual .sancov format, run sancov.py rawunpack
+// $pid.sancov.raw.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+static const uptr kMaxTextSize = 64 * 1024;
+
+struct CachedMapping {
+ public:
+  bool NeedsUpdate(uptr pc) {
+    int new_pid = internal_getpid();
+    if (last_pid == new_pid && pc && pc >= last_range_start &&
+        pc < last_range_end)
+      return false;
+    last_pid = new_pid;
+    return true;
+  }
+
+  void SetModuleRange(uptr start, uptr end) {
+    last_range_start = start;
+    last_range_end = end;
+  }
+
+ private:
+  uptr last_range_start, last_range_end;
+  int last_pid;
+};
+
+static CachedMapping cached_mapping;
+static StaticSpinMutex mapping_mu;
+
+void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) {
+  if (!common_flags()->coverage_direct) return;
+
+  SpinMutexLock l(&mapping_mu);
+
+  if (!cached_mapping.NeedsUpdate(caller_pc))
+    return;
+
+  InternalScopedString text(kMaxTextSize);
+
+  {
+    InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
+    CHECK(modules.data());
+    int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules,
+                                     /* filter */ nullptr);
+
+    text.append("%d\n", sizeof(uptr) * 8);
+    for (int i = 0; i < n_modules; ++i) {
+      const char *module_name = StripModuleName(modules[i].full_name());
+      uptr base = modules[i].base_address();
+      for (auto iter = modules[i].ranges(); iter.hasNext();) {
+        const auto *range = iter.next();
+        if (range->executable) {
+          uptr start = range->beg;
+          uptr end = range->end;
+          text.append("%zx %zx %zx %s\n", start, end, base, module_name);
+          if (caller_pc && caller_pc >= start && caller_pc < end)
+            cached_mapping.SetModuleRange(start, end);
+        }
+      }
+      modules[i].clear();
+    }
+  }
+
+  error_t err;
+  InternalScopedString tmp_path(64 + internal_strlen(coverage_dir));
+  uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(),
+                               "%s/%zd.sancov.map.tmp", coverage_dir,
+                               internal_getpid());
+  CHECK_LE(res, tmp_path.size());
+  fd_t map_fd = OpenFile(tmp_path.data(), WrOnly, &err);
+  if (map_fd == kInvalidFd) {
+    Report("Coverage: failed to open %s for writing: %d\n", tmp_path.data(),
+           err);
+    Die();
+  }
+
+  if (!WriteToFile(map_fd, text.data(), text.length(), nullptr, &err)) {
+    Printf("sancov.map write failed: %d\n", err);
+    Die();
+  }
+  CloseFile(map_fd);
+
+  InternalScopedString path(64 + internal_strlen(coverage_dir));
+  res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map",
+                          coverage_dir, internal_getpid());
+  CHECK_LE(res, path.size());
+  if (!RenameFile(tmp_path.data(), path.data(), &err)) {
+    Printf("sancov.map rename failed: %d\n", err);
+    Die();
+  }
+}
+
+} // namespace __sanitizer
diff --git a/lsan/src/sanitizer_deadlock_detector1.cc b/lsan/src/sanitizer_deadlock_detector1.cc
new file mode 100644 (file)
index 0000000..7a318c9
--- /dev/null
@@ -0,0 +1,188 @@
+//===-- sanitizer_deadlock_detector1.cc -----------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Deadlock detector implementation based on NxN adjacency bit matrix.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_deadlock_detector_interface.h"
+#include "sanitizer_deadlock_detector.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
+
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+
+namespace __sanitizer {
+
+typedef TwoLevelBitVector<> DDBV;  // DeadlockDetector's bit vector.
+
+struct DDPhysicalThread {
+};
+
+struct DDLogicalThread {
+  u64 ctx;
+  DeadlockDetectorTLS<DDBV> dd;
+  DDReport rep;
+  bool report_pending;
+};
+
+struct DD : public DDetector {
+  SpinMutex mtx;
+  DeadlockDetector<DDBV> dd;
+  DDFlags flags;
+
+  explicit DD(const DDFlags *flags);
+
+  DDPhysicalThread *CreatePhysicalThread() override;
+  void DestroyPhysicalThread(DDPhysicalThread *pt) override;
+
+  DDLogicalThread *CreateLogicalThread(u64 ctx) override;
+  void DestroyLogicalThread(DDLogicalThread *lt) override;
+
+  void MutexInit(DDCallback *cb, DDMutex *m) override;
+  void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
+  void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+                      bool trylock) override;
+  void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
+  void MutexDestroy(DDCallback *cb, DDMutex *m) override;
+
+  DDReport *GetReport(DDCallback *cb) override;
+
+  void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
+  void ReportDeadlock(DDCallback *cb, DDMutex *m);
+};
+
+DDetector *DDetector::Create(const DDFlags *flags) {
+  (void)flags;
+  void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
+  return new(mem) DD(flags);
+}
+
+DD::DD(const DDFlags *flags)
+    : flags(*flags) {
+  dd.clear();
+}
+
+DDPhysicalThread* DD::CreatePhysicalThread() {
+  return nullptr;
+}
+
+void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
+}
+
+DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
+  DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt));
+  lt->ctx = ctx;
+  lt->dd.clear();
+  lt->report_pending = false;
+  return lt;
+}
+
+void DD::DestroyLogicalThread(DDLogicalThread *lt) {
+  lt->~DDLogicalThread();
+  InternalFree(lt);
+}
+
+void DD::MutexInit(DDCallback *cb, DDMutex *m) {
+  m->id = 0;
+  m->stk = cb->Unwind();
+}
+
+void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) {
+  if (!dd.nodeBelongsToCurrentEpoch(m->id))
+    m->id = dd.newNode(reinterpret_cast<uptr>(m));
+  dd.ensureCurrentEpoch(&lt->dd);
+}
+
+void DD::MutexBeforeLock(DDCallback *cb,
+    DDMutex *m, bool wlock) {
+  DDLogicalThread *lt = cb->lt;
+  if (lt->dd.empty()) return;  // This will be the first lock held by lt.
+  if (dd.hasAllEdges(&lt->dd, m->id)) return;  // We already have all edges.
+  SpinMutexLock lk(&mtx);
+  MutexEnsureID(lt, m);
+  if (dd.isHeld(&lt->dd, m->id))
+    return;  // FIXME: allow this only for recursive locks.
+  if (dd.onLockBefore(&lt->dd, m->id)) {
+    // Actually add this edge now so that we have all the stack traces.
+    dd.addEdges(&lt->dd, m->id, cb->Unwind(), cb->UniqueTid());
+    ReportDeadlock(cb, m);
+  }
+}
+
+void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) {
+  DDLogicalThread *lt = cb->lt;
+  uptr path[10];
+  uptr len = dd.findPathToLock(&lt->dd, m->id, path, ARRAY_SIZE(path));
+  CHECK_GT(len, 0U);  // Hm.. cycle of 10 locks? I'd like to see that.
+  CHECK_EQ(m->id, path[0]);
+  lt->report_pending = true;
+  DDReport *rep = &lt->rep;
+  rep->n = len;
+  for (uptr i = 0; i < len; i++) {
+    uptr from = path[i];
+    uptr to = path[(i + 1) % len];
+    DDMutex *m0 = (DDMutex*)dd.getData(from);
+    DDMutex *m1 = (DDMutex*)dd.getData(to);
+
+    u32 stk_from = -1U, stk_to = -1U;
+    int unique_tid = 0;
+    dd.findEdge(from, to, &stk_from, &stk_to, &unique_tid);
+    // Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to,
+    //    unique_tid);
+    rep->loop[i].thr_ctx = unique_tid;
+    rep->loop[i].mtx_ctx0 = m0->ctx;
+    rep->loop[i].mtx_ctx1 = m1->ctx;
+    rep->loop[i].stk[0] = stk_to;
+    rep->loop[i].stk[1] = stk_from;
+  }
+}
+
+void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
+  DDLogicalThread *lt = cb->lt;
+  u32 stk = 0;
+  if (flags.second_deadlock_stack)
+    stk = cb->Unwind();
+  // Printf("T%p MutexLock:   %zx stk %u\n", lt, m->id, stk);
+  if (dd.onFirstLock(&lt->dd, m->id, stk))
+    return;
+  if (dd.onLockFast(&lt->dd, m->id, stk))
+    return;
+
+  SpinMutexLock lk(&mtx);
+  MutexEnsureID(lt, m);
+  if (wlock)  // Only a recursive rlock may be held.
+    CHECK(!dd.isHeld(&lt->dd, m->id));
+  if (!trylock)
+    dd.addEdges(&lt->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
+  dd.onLockAfter(&lt->dd, m->id, stk);
+}
+
+void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
+  // Printf("T%p MutexUnLock: %zx\n", cb->lt, m->id);
+  dd.onUnlock(&cb->lt->dd, m->id);
+}
+
+void DD::MutexDestroy(DDCallback *cb,
+    DDMutex *m) {
+  if (!m->id) return;
+  SpinMutexLock lk(&mtx);
+  if (dd.nodeBelongsToCurrentEpoch(m->id))
+    dd.removeNode(m->id);
+  m->id = 0;
+}
+
+DDReport *DD::GetReport(DDCallback *cb) {
+  if (!cb->lt->report_pending)
+    return nullptr;
+  cb->lt->report_pending = false;
+  return &cb->lt->rep;
+}
+
+} // namespace __sanitizer
+#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
diff --git a/lsan/src/sanitizer_deadlock_detector2.cc b/lsan/src/sanitizer_deadlock_detector2.cc
new file mode 100644 (file)
index 0000000..bc96ad2
--- /dev/null
@@ -0,0 +1,426 @@
+//===-- sanitizer_deadlock_detector2.cc -----------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Deadlock detector implementation based on adjacency lists.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_deadlock_detector_interface.h"
+#include "sanitizer_common.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
+
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
+
+namespace __sanitizer {
+
+const int kMaxNesting = 64;
+const u32 kNoId = -1;
+const u32 kEndId = -2;
+const int kMaxLink = 8;
+const int kL1Size = 1024;
+const int kL2Size = 1024;
+const int kMaxMutex = kL1Size * kL2Size;
+
+struct Id {
+  u32 id;
+  u32 seq;
+
+  explicit Id(u32 id = 0, u32 seq = 0)
+      : id(id)
+      , seq(seq) {
+  }
+};
+
+struct Link {
+  u32 id;
+  u32 seq;
+  u32 tid;
+  u32 stk0;
+  u32 stk1;
+
+  explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 s0 = 0, u32 s1 = 0)
+      : id(id)
+      , seq(seq)
+      , tid(tid)
+      , stk0(s0)
+      , stk1(s1) {
+  }
+};
+
+struct DDPhysicalThread {
+  DDReport rep;
+  bool report_pending;
+  bool visited[kMaxMutex];
+  Link pending[kMaxMutex];
+  Link path[kMaxMutex];
+};
+
+struct ThreadMutex {
+  u32 id;
+  u32 stk;
+};
+
+struct DDLogicalThread {
+  u64         ctx;
+  ThreadMutex locked[kMaxNesting];
+  int         nlocked;
+};
+
+struct Mutex {
+  StaticSpinMutex mtx;
+  u32 seq;
+  int nlink;
+  Link link[kMaxLink];
+};
+
+struct DD : public DDetector {
+  explicit DD(const DDFlags *flags);
+
+  DDPhysicalThread* CreatePhysicalThread();
+  void DestroyPhysicalThread(DDPhysicalThread *pt);
+
+  DDLogicalThread* CreateLogicalThread(u64 ctx);
+  void DestroyLogicalThread(DDLogicalThread *lt);
+
+  void MutexInit(DDCallback *cb, DDMutex *m);
+  void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
+  void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+      bool trylock);
+  void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
+  void MutexDestroy(DDCallback *cb, DDMutex *m);
+
+  DDReport *GetReport(DDCallback *cb);
+
+  void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
+  void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
+  u32 allocateId(DDCallback *cb);
+  Mutex *getMutex(u32 id);
+  u32 getMutexId(Mutex *m);
+
+  DDFlags flags;
+
+  Mutex* mutex[kL1Size];
+
+  SpinMutex mtx;
+  InternalMmapVector<u32> free_id;
+  int id_gen;
+};
+
+DDetector *DDetector::Create(const DDFlags *flags) {
+  (void)flags;
+  void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
+  return new(mem) DD(flags);
+}
+
+DD::DD(const DDFlags *flags)
+    : flags(*flags)
+    , free_id(1024) {
+  id_gen = 0;
+}
+
+DDPhysicalThread* DD::CreatePhysicalThread() {
+  DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
+      "deadlock detector (physical thread)");
+  return pt;
+}
+
+void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
+  pt->~DDPhysicalThread();
+  UnmapOrDie(pt, sizeof(DDPhysicalThread));
+}
+
+DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
+  DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(
+      sizeof(DDLogicalThread));
+  lt->ctx = ctx;
+  lt->nlocked = 0;
+  return lt;
+}
+
+void DD::DestroyLogicalThread(DDLogicalThread *lt) {
+  lt->~DDLogicalThread();
+  InternalFree(lt);
+}
+
+void DD::MutexInit(DDCallback *cb, DDMutex *m) {
+  VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m);
+  m->id = kNoId;
+  m->recursion = 0;
+  atomic_store(&m->owner, 0, memory_order_relaxed);
+}
+
+Mutex *DD::getMutex(u32 id) {
+  return &mutex[id / kL2Size][id % kL2Size];
+}
+
+u32 DD::getMutexId(Mutex *m) {
+  for (int i = 0; i < kL1Size; i++) {
+    Mutex *tab = mutex[i];
+    if (tab == 0)
+      break;
+    if (m >= tab && m < tab + kL2Size)
+      return i * kL2Size + (m - tab);
+  }
+  return -1;
+}
+
+u32 DD::allocateId(DDCallback *cb) {
+  u32 id = -1;
+  SpinMutexLock l(&mtx);
+  if (free_id.size() > 0) {
+    id = free_id.back();
+    free_id.pop_back();
+  } else {
+    CHECK_LT(id_gen, kMaxMutex);
+    if ((id_gen % kL2Size) == 0) {
+      mutex[id_gen / kL2Size] = (Mutex*)MmapOrDie(kL2Size * sizeof(Mutex),
+          "deadlock detector (mutex table)");
+    }
+    id = id_gen++;
+  }
+  CHECK_LE(id, kMaxMutex);
+  VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
+  return id;
+}
+
+void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
+  VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n",
+      cb->lt->ctx, m, wlock, cb->lt->nlocked);
+  DDPhysicalThread *pt = cb->pt;
+  DDLogicalThread *lt = cb->lt;
+
+  uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+  if (owner == (uptr)cb->lt) {
+    VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n",
+        cb->lt->ctx);
+    return;
+  }
+
+  CHECK_LE(lt->nlocked, kMaxNesting);
+
+  // FIXME(dvyukov): don't allocate id if lt->nlocked == 0?
+  if (m->id == kNoId)
+    m->id = allocateId(cb);
+
+  ThreadMutex *tm = &lt->locked[lt->nlocked++];
+  tm->id = m->id;
+  if (flags.second_deadlock_stack)
+    tm->stk = cb->Unwind();
+  if (lt->nlocked == 1) {
+    VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n",
+        cb->lt->ctx);
+    return;
+  }
+
+  bool added = false;
+  Mutex *mtx = getMutex(m->id);
+  for (int i = 0; i < lt->nlocked - 1; i++) {
+    u32 id1 = lt->locked[i].id;
+    u32 stk1 = lt->locked[i].stk;
+    Mutex *mtx1 = getMutex(id1);
+    SpinMutexLock l(&mtx1->mtx);
+    if (mtx1->nlink == kMaxLink) {
+      // FIXME(dvyukov): check stale links
+      continue;
+    }
+    int li = 0;
+    for (; li < mtx1->nlink; li++) {
+      Link *link = &mtx1->link[li];
+      if (link->id == m->id) {
+        if (link->seq != mtx->seq) {
+          link->seq = mtx->seq;
+          link->tid = lt->ctx;
+          link->stk0 = stk1;
+          link->stk1 = cb->Unwind();
+          added = true;
+          VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
+              cb->lt->ctx, getMutexId(mtx1), m->id);
+        }
+        break;
+      }
+    }
+    if (li == mtx1->nlink) {
+      // FIXME(dvyukov): check stale links
+      Link *link = &mtx1->link[mtx1->nlink++];
+      link->id = m->id;
+      link->seq = mtx->seq;
+      link->tid = lt->ctx;
+      link->stk0 = stk1;
+      link->stk1 = cb->Unwind();
+      added = true;
+      VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
+          cb->lt->ctx, getMutexId(mtx1), m->id);
+    }
+  }
+
+  if (!added || mtx->nlink == 0) {
+    VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n",
+        cb->lt->ctx);
+    return;
+  }
+
+  CycleCheck(pt, lt, m);
+}
+
+void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+    bool trylock) {
+  VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n",
+      cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked);
+  DDLogicalThread *lt = cb->lt;
+
+  uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+  if (owner == (uptr)cb->lt) {
+    VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx);
+    CHECK(wlock);
+    m->recursion++;
+    return;
+  }
+  CHECK_EQ(owner, 0);
+  if (wlock) {
+    VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx);
+    CHECK_EQ(m->recursion, 0);
+    m->recursion = 1;
+    atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed);
+  }
+
+  if (!trylock)
+    return;
+
+  CHECK_LE(lt->nlocked, kMaxNesting);
+  if (m->id == kNoId)
+    m->id = allocateId(cb);
+  ThreadMutex *tm = &lt->locked[lt->nlocked++];
+  tm->id = m->id;
+  if (flags.second_deadlock_stack)
+    tm->stk = cb->Unwind();
+}
+
+void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
+  VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n",
+      cb->lt->ctx, m, wlock, cb->lt->nlocked);
+  DDLogicalThread *lt = cb->lt;
+
+  uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+  if (owner == (uptr)cb->lt) {
+    VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx);
+    if (--m->recursion > 0)
+      return;
+    VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx);
+    atomic_store(&m->owner, 0, memory_order_relaxed);
+  }
+  CHECK_NE(m->id, kNoId);
+  int last = lt->nlocked - 1;
+  for (int i = last; i >= 0; i--) {
+    if (cb->lt->locked[i].id == m->id) {
+      lt->locked[i] = lt->locked[last];
+      lt->nlocked--;
+      break;
+    }
+  }
+}
+
+void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
+  VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n",
+      cb->lt->ctx, m);
+  DDLogicalThread *lt = cb->lt;
+
+  if (m->id == kNoId)
+    return;
+
+  // Remove the mutex from lt->locked if there.
+  int last = lt->nlocked - 1;
+  for (int i = last; i >= 0; i--) {
+    if (lt->locked[i].id == m->id) {
+      lt->locked[i] = lt->locked[last];
+      lt->nlocked--;
+      break;
+    }
+  }
+
+  // Clear and invalidate the mutex descriptor.
+  {
+    Mutex *mtx = getMutex(m->id);
+    SpinMutexLock l(&mtx->mtx);
+    mtx->seq++;
+    mtx->nlink = 0;
+  }
+
+  // Return id to cache.
+  {
+    SpinMutexLock l(&mtx);
+    free_id.push_back(m->id);
+  }
+}
+
+void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
+    DDMutex *m) {
+  internal_memset(pt->visited, 0, sizeof(pt->visited));
+  int npath = 0;
+  int npending = 0;
+  {
+    Mutex *mtx = getMutex(m->id);
+    SpinMutexLock l(&mtx->mtx);
+    for (int li = 0; li < mtx->nlink; li++)
+      pt->pending[npending++] = mtx->link[li];
+  }
+  while (npending > 0) {
+    Link link = pt->pending[--npending];
+    if (link.id == kEndId) {
+      npath--;
+      continue;
+    }
+    if (pt->visited[link.id])
+      continue;
+    Mutex *mtx1 = getMutex(link.id);
+    SpinMutexLock l(&mtx1->mtx);
+    if (mtx1->seq != link.seq)
+      continue;
+    pt->visited[link.id] = true;
+    if (mtx1->nlink == 0)
+      continue;
+    pt->path[npath++] = link;
+    pt->pending[npending++] = Link(kEndId);
+    if (link.id == m->id)
+      return Report(pt, lt, npath);  // Bingo!
+    for (int li = 0; li < mtx1->nlink; li++) {
+      Link *link1 = &mtx1->link[li];
+      // Mutex *mtx2 = getMutex(link->id);
+      // FIXME(dvyukov): fast seq check
+      // FIXME(dvyukov): fast nlink != 0 check
+      // FIXME(dvyukov): fast pending check?
+      // FIXME(dvyukov): npending can be larger than kMaxMutex
+      pt->pending[npending++] = *link1;
+    }
+  }
+}
+
+void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) {
+  DDReport *rep = &pt->rep;
+  rep->n = npath;
+  for (int i = 0; i < npath; i++) {
+    Link *link = &pt->path[i];
+    Link *link0 = &pt->path[i ? i - 1 : npath - 1];
+    rep->loop[i].thr_ctx = link->tid;
+    rep->loop[i].mtx_ctx0 = link0->id;
+    rep->loop[i].mtx_ctx1 = link->id;
+    rep->loop[i].stk[0] = flags.second_deadlock_stack ? link->stk0 : 0;
+    rep->loop[i].stk[1] = link->stk1;
+  }
+  pt->report_pending = true;
+}
+
+DDReport *DD::GetReport(DDCallback *cb) {
+  if (!cb->pt->report_pending)
+    return 0;
+  cb->pt->report_pending = false;
+  return &cb->pt->rep;
+}
+
+}  // namespace __sanitizer
+#endif  // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
diff --git a/lsan/src/sanitizer_flag_parser.cc b/lsan/src/sanitizer_flag_parser.cc
new file mode 100644 (file)
index 0000000..1fc6b2e
--- /dev/null
@@ -0,0 +1,169 @@
+//===-- sanitizer_flag_parser.cc ------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_flag_parser.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_flag_parser.h"
+
+namespace __sanitizer {
+
+LowLevelAllocator FlagParser::Alloc;
+
+class UnknownFlags {
+  static const int kMaxUnknownFlags = 20;
+  const char *unknown_flags_[kMaxUnknownFlags];
+  int n_unknown_flags_;
+
+ public:
+  void Add(const char *name) {
+    CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
+    unknown_flags_[n_unknown_flags_++] = name;
+  }
+
+  void Report() {
+    if (!n_unknown_flags_) return;
+    Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
+    for (int i = 0; i < n_unknown_flags_; ++i)
+      Printf("    %s\n", unknown_flags_[i]);
+    n_unknown_flags_ = 0;
+  }
+};
+
+UnknownFlags unknown_flags;
+
+void ReportUnrecognizedFlags() {
+  unknown_flags.Report();
+}
+
+char *FlagParser::ll_strndup(const char *s, uptr n) {
+  uptr len = internal_strnlen(s, n);
+  char *s2 = (char*)Alloc.Allocate(len + 1);
+  internal_memcpy(s2, s, len);
+  s2[len] = 0;
+  return s2;
+}
+
+void FlagParser::PrintFlagDescriptions() {
+  Printf("Available flags for %s:\n", SanitizerToolName);
+  for (int i = 0; i < n_flags_; ++i)
+    Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc);
+}
+
+void FlagParser::fatal_error(const char *err) {
+  Printf("ERROR: %s\n", err);
+  Die();
+}
+
+bool FlagParser::is_space(char c) {
+  return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
+         c == '\r';
+}
+
+void FlagParser::skip_whitespace() {
+  while (is_space(buf_[pos_])) ++pos_;
+}
+
+void FlagParser::parse_flag() {
+  uptr name_start = pos_;
+  while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
+  if (buf_[pos_] != '=') fatal_error("expected '='");
+  char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
+
+  uptr value_start = ++pos_;
+  char *value;
+  if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
+    char quote = buf_[pos_++];
+    while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
+    if (buf_[pos_] == 0) fatal_error("unterminated string");
+    value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
+    ++pos_; // consume the closing quote
+  } else {
+    while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
+    if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
+      fatal_error("expected separator or eol");
+    value = ll_strndup(buf_ + value_start, pos_ - value_start);
+  }
+
+  bool res = run_handler(name, value);
+  if (!res) fatal_error("Flag parsing failed.");
+}
+
+void FlagParser::parse_flags() {
+  while (true) {
+    skip_whitespace();
+    if (buf_[pos_] == 0) break;
+    parse_flag();
+  }
+
+  // Do a sanity check for certain flags.
+  if (common_flags_dont_use.malloc_context_size < 1)
+    common_flags_dont_use.malloc_context_size = 1;
+}
+
+void FlagParser::ParseString(const char *s) {
+  if (!s) return;
+  // Backup current parser state to allow nested ParseString() calls.
+  const char *old_buf_ = buf_;
+  uptr old_pos_ = pos_;
+  buf_ = s;
+  pos_ = 0;
+
+  parse_flags();
+
+  buf_ = old_buf_;
+  pos_ = old_pos_;
+}
+
+bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
+  static const uptr kMaxIncludeSize = 1 << 15;
+  char *data;
+  uptr data_mapped_size;
+  error_t err;
+  uptr len;
+  if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
+                        Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
+    if (ignore_missing)
+      return true;
+    Printf("Failed to read options from '%s': error %d\n", path, err);
+    return false;
+  }
+  ParseString(data);
+  UnmapOrDie(data, data_mapped_size);
+  return true;
+}
+
+bool FlagParser::run_handler(const char *name, const char *value) {
+  for (int i = 0; i < n_flags_; ++i) {
+    if (internal_strcmp(name, flags_[i].name) == 0)
+      return flags_[i].handler->Parse(value);
+  }
+  // Unrecognized flag. This is not a fatal error, we may print a warning later.
+  unknown_flags.Add(name);
+  return true;
+}
+
+void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
+                                 const char *desc) {
+  CHECK_LT(n_flags_, kMaxFlags);
+  flags_[n_flags_].name = name;
+  flags_[n_flags_].desc = desc;
+  flags_[n_flags_].handler = handler;
+  ++n_flags_;
+}
+
+FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
+  flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_flags.cc b/lsan/src/sanitizer_flags.cc
new file mode 100644 (file)
index 0000000..a24ff90
--- /dev/null
@@ -0,0 +1,100 @@
+//===-- sanitizer_flags.cc ------------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_flags.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
+#include "sanitizer_flag_parser.h"
+
+namespace __sanitizer {
+
+CommonFlags common_flags_dont_use;
+
+struct FlagDescription {
+  const char *name;
+  const char *description;
+  FlagDescription *next;
+};
+
+IntrusiveList<FlagDescription> flag_descriptions;
+
+// If set, the tool will install its own SEGV signal handler by default.
+#ifndef SANITIZER_NEEDS_SEGV
+# define SANITIZER_NEEDS_SEGV 1
+#endif
+
+void CommonFlags::SetDefaults() {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+}
+
+void CommonFlags::CopyFrom(const CommonFlags &other) {
+  internal_memcpy(this, &other, sizeof(*this));
+}
+
+// Copy the string from "s" to "out", replacing "%b" with the binary basename.
+static void SubstituteBinaryName(const char *s, char *out, uptr out_size) {
+  char *out_end = out + out_size;
+  while (*s && out < out_end - 1) {
+    if (s[0] != '%' || s[1] != 'b') { *out++ = *s++; continue; }
+    const char *base = GetProcessName();
+    CHECK(base);
+    while (*base && out < out_end - 1)
+      *out++ = *base++;
+    s += 2; // skip "%b"
+  }
+  *out = '\0';
+}
+
+class FlagHandlerInclude : public FlagHandlerBase {
+  FlagParser *parser_;
+  bool ignore_missing_;
+
+ public:
+  explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing)
+      : parser_(parser), ignore_missing_(ignore_missing) {}
+  bool Parse(const char *value) final {
+    if (internal_strchr(value, '%')) {
+      char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude");
+      SubstituteBinaryName(value, buf, kMaxPathLength);
+      bool res = parser_->ParseFile(buf, ignore_missing_);
+      UnmapOrDie(buf, kMaxPathLength);
+      return res;
+    }
+    return parser_->ParseFile(value, ignore_missing_);
+  }
+};
+
+void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) {
+  FlagHandlerInclude *fh_include = new (FlagParser::Alloc) // NOLINT
+      FlagHandlerInclude(parser, /*ignore_missing*/ false);
+  parser->RegisterHandler("include", fh_include,
+                          "read more options from the given file");
+  FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc) // NOLINT
+      FlagHandlerInclude(parser, /*ignore_missing*/ true);
+  parser->RegisterHandler(
+      "include_if_exists", fh_include_if_exists,
+      "read more options from the given file (if it exists)");
+}
+
+void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) \
+  RegisterFlag(parser, #Name, Description, &cf->Name);
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+
+  RegisterIncludeFlags(parser, cf);
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_libc.cc b/lsan/src/sanitizer_libc.cc
new file mode 100644 (file)
index 0000000..05fdca6
--- /dev/null
@@ -0,0 +1,261 @@
+//===-- sanitizer_libc.cc -------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries. See sanitizer_libc.h for details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+
+namespace __sanitizer {
+
+s64 internal_atoll(const char *nptr) {
+  return internal_simple_strtoll(nptr, nullptr, 10);
+}
+
+void *internal_memchr(const void *s, int c, uptr n) {
+  const char *t = (const char *)s;
+  for (uptr i = 0; i < n; ++i, ++t)
+    if (*t == c)
+      return reinterpret_cast<void *>(const_cast<char *>(t));
+  return nullptr;
+}
+
+void *internal_memrchr(const void *s, int c, uptr n) {
+  const char *t = (const char *)s;
+  void *res = nullptr;
+  for (uptr i = 0; i < n; ++i, ++t) {
+    if (*t == c) res = reinterpret_cast<void *>(const_cast<char *>(t));
+  }
+  return res;
+}
+
+int internal_memcmp(const void* s1, const void* s2, uptr n) {
+  const char *t1 = (const char *)s1;
+  const char *t2 = (const char *)s2;
+  for (uptr i = 0; i < n; ++i, ++t1, ++t2)
+    if (*t1 != *t2)
+      return *t1 < *t2 ? -1 : 1;
+  return 0;
+}
+
+void *internal_memcpy(void *dest, const void *src, uptr n) {
+  char *d = (char*)dest;
+  const char *s = (const char *)src;
+  for (uptr i = 0; i < n; ++i)
+    d[i] = s[i];
+  return dest;
+}
+
+void *internal_memmove(void *dest, const void *src, uptr n) {
+  char *d = (char*)dest;
+  const char *s = (const char *)src;
+  sptr i, signed_n = (sptr)n;
+  CHECK_GE(signed_n, 0);
+  if (d < s) {
+    for (i = 0; i < signed_n; ++i)
+      d[i] = s[i];
+  } else {
+    if (d > s && signed_n > 0)
+      for (i = signed_n - 1; i >= 0 ; --i) {
+        d[i] = s[i];
+      }
+  }
+  return dest;
+}
+
+// Semi-fast bzero for 16-aligned data. Still far from peak performance.
+void internal_bzero_aligned16(void *s, uptr n) {
+  struct S16 { u64 a, b; } ALIGNED(16);
+  CHECK_EQ((reinterpret_cast<uptr>(s) | n) & 15, 0);
+  for (S16 *p = reinterpret_cast<S16*>(s), *end = p + n / 16; p < end; p++) {
+    p->a = p->b = 0;
+    // Make sure this does not become memset.
+    SanitizerBreakOptimization(nullptr);
+  }
+}
+
+void *internal_memset(void* s, int c, uptr n) {
+  // The next line prevents Clang from making a call to memset() instead of the
+  // loop below.
+  // FIXME: building the runtime with -ffreestanding is a better idea. However
+  // there currently are linktime problems due to PR12396.
+  char volatile *t = (char*)s;
+  for (uptr i = 0; i < n; ++i, ++t) {
+    *t = c;
+  }
+  return s;
+}
+
+uptr internal_strcspn(const char *s, const char *reject) {
+  uptr i;
+  for (i = 0; s[i]; i++) {
+    if (internal_strchr(reject, s[i]))
+      return i;
+  }
+  return i;
+}
+
+char* internal_strdup(const char *s) {
+  uptr len = internal_strlen(s);
+  char *s2 = (char*)InternalAlloc(len + 1);
+  internal_memcpy(s2, s, len);
+  s2[len] = 0;
+  return s2;
+}
+
+char* internal_strndup(const char *s, uptr n) {
+  uptr len = internal_strnlen(s, n);
+  char *s2 = (char*)InternalAlloc(len + 1);
+  internal_memcpy(s2, s, len);
+  s2[len] = 0;
+  return s2;
+}
+
+int internal_strcmp(const char *s1, const char *s2) {
+  while (true) {
+    unsigned c1 = *s1;
+    unsigned c2 = *s2;
+    if (c1 != c2) return (c1 < c2) ? -1 : 1;
+    if (c1 == 0) break;
+    s1++;
+    s2++;
+  }
+  return 0;
+}
+
+int internal_strncmp(const char *s1, const char *s2, uptr n) {
+  for (uptr i = 0; i < n; i++) {
+    unsigned c1 = *s1;
+    unsigned c2 = *s2;
+    if (c1 != c2) return (c1 < c2) ? -1 : 1;
+    if (c1 == 0) break;
+    s1++;
+    s2++;
+  }
+  return 0;
+}
+
+char* internal_strchr(const char *s, int c) {
+  while (true) {
+    if (*s == (char)c)
+      return const_cast<char *>(s);
+    if (*s == 0)
+      return nullptr;
+    s++;
+  }
+}
+
+char *internal_strchrnul(const char *s, int c) {
+  char *res = internal_strchr(s, c);
+  if (!res)
+    res = const_cast<char *>(s) + internal_strlen(s);
+  return res;
+}
+
+char *internal_strrchr(const char *s, int c) {
+  const char *res = nullptr;
+  for (uptr i = 0; s[i]; i++) {
+    if (s[i] == c) res = s + i;
+  }
+  return const_cast<char *>(res);
+}
+
+uptr internal_strlen(const char *s) {
+  uptr i = 0;
+  while (s[i]) i++;
+  return i;
+}
+
+char *internal_strncat(char *dst, const char *src, uptr n) {
+  uptr len = internal_strlen(dst);
+  uptr i;
+  for (i = 0; i < n && src[i]; i++)
+    dst[len + i] = src[i];
+  dst[len + i] = 0;
+  return dst;
+}
+
+char *internal_strncpy(char *dst, const char *src, uptr n) {
+  uptr i;
+  for (i = 0; i < n && src[i]; i++)
+    dst[i] = src[i];
+  internal_memset(dst + i, '\0', n - i);
+  return dst;
+}
+
+uptr internal_strnlen(const char *s, uptr maxlen) {
+  uptr i = 0;
+  while (i < maxlen && s[i]) i++;
+  return i;
+}
+
+char *internal_strstr(const char *haystack, const char *needle) {
+  // This is O(N^2), but we are not using it in hot places.
+  uptr len1 = internal_strlen(haystack);
+  uptr len2 = internal_strlen(needle);
+  if (len1 < len2) return nullptr;
+  for (uptr pos = 0; pos <= len1 - len2; pos++) {
+    if (internal_memcmp(haystack + pos, needle, len2) == 0)
+      return const_cast<char *>(haystack) + pos;
+  }
+  return nullptr;
+}
+
+s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {
+  CHECK_EQ(base, 10);
+  while (IsSpace(*nptr)) nptr++;
+  int sgn = 1;
+  u64 res = 0;
+  bool have_digits = false;
+  char *old_nptr = const_cast<char *>(nptr);
+  if (*nptr == '+') {
+    sgn = 1;
+    nptr++;
+  } else if (*nptr == '-') {
+    sgn = -1;
+    nptr++;
+  }
+  while (IsDigit(*nptr)) {
+    res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX;
+    int digit = ((*nptr) - '0');
+    res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX;
+    have_digits = true;
+    nptr++;
+  }
+  if (endptr) {
+    *endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
+  }
+  if (sgn > 0) {
+    return (s64)(Min((u64)INT64_MAX, res));
+  } else {
+    return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1);
+  }
+}
+
+bool mem_is_zero(const char *beg, uptr size) {
+  CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40));  // Sanity check.
+  const char *end = beg + size;
+  uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));
+  uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));
+  uptr all = 0;
+  // Prologue.
+  for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)
+    all |= *mem;
+  // Aligned loop.
+  for (; aligned_beg < aligned_end; aligned_beg++)
+    all |= *aligned_beg;
+  // Epilogue.
+  if ((char*)aligned_end >= beg)
+    for (const char *mem = (char*)aligned_end; mem < end; mem++)
+      all |= *mem;
+  return all == 0;
+}
+
+} // namespace __sanitizer
diff --git a/lsan/src/sanitizer_libignore.cc b/lsan/src/sanitizer_libignore.cc
new file mode 100644 (file)
index 0000000..4b8cbed
--- /dev/null
@@ -0,0 +1,100 @@
+//===-- sanitizer_libignore.cc --------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC
+
+#include "sanitizer_libignore.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+LibIgnore::LibIgnore(LinkerInitialized) {
+}
+
+void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
+  BlockingMutexLock lock(&mutex_);
+  if (count_ >= kMaxLibs) {
+    Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName,
+           kMaxLibs);
+    Die();
+  }
+  Lib *lib = &libs_[count_++];
+  lib->templ = internal_strdup(name_templ);
+  lib->name = nullptr;
+  lib->real_name = nullptr;
+  lib->loaded = false;
+}
+
+void LibIgnore::OnLibraryLoaded(const char *name) {
+  BlockingMutexLock lock(&mutex_);
+  // Try to match suppressions with symlink target.
+  InternalScopedString buf(kMaxPathLength);
+  if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
+      buf[0]) {
+    for (uptr i = 0; i < count_; i++) {
+      Lib *lib = &libs_[i];
+      if (!lib->loaded && (!lib->real_name) &&
+          TemplateMatch(lib->templ, name))
+        lib->real_name = internal_strdup(buf.data());
+    }
+  }
+
+  // Scan suppressions list and find newly loaded and unloaded libraries.
+  MemoryMappingLayout proc_maps(/*cache_enabled*/false);
+  InternalScopedString module(kMaxPathLength);
+  for (uptr i = 0; i < count_; i++) {
+    Lib *lib = &libs_[i];
+    bool loaded = false;
+    proc_maps.Reset();
+    uptr b, e, off, prot;
+    while (proc_maps.Next(&b, &e, &off, module.data(), module.size(), &prot)) {
+      if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
+        continue;
+      if (TemplateMatch(lib->templ, module.data()) ||
+          (lib->real_name &&
+          internal_strcmp(lib->real_name, module.data()) == 0)) {
+        if (loaded) {
+          Report("%s: called_from_lib suppression '%s' is matched against"
+                 " 2 libraries: '%s' and '%s'\n",
+                 SanitizerToolName, lib->templ, lib->name, module.data());
+          Die();
+        }
+        loaded = true;
+        if (lib->loaded)
+          continue;
+        VReport(1,
+                "Matched called_from_lib suppression '%s' against library"
+                " '%s'\n",
+                lib->templ, module.data());
+        lib->loaded = true;
+        lib->name = internal_strdup(module.data());
+        const uptr idx = atomic_load(&loaded_count_, memory_order_relaxed);
+        code_ranges_[idx].begin = b;
+        code_ranges_[idx].end = e;
+        atomic_store(&loaded_count_, idx + 1, memory_order_release);
+      }
+    }
+    if (lib->loaded && !loaded) {
+      Report("%s: library '%s' that was matched against called_from_lib"
+             " suppression '%s' is unloaded\n",
+             SanitizerToolName, lib->name, lib->templ);
+      Die();
+    }
+  }
+}
+
+void LibIgnore::OnLibraryUnloaded() {
+  OnLibraryLoaded(nullptr);
+}
+
+} // namespace __sanitizer
+
+#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lsan/src/sanitizer_linux.cc b/lsan/src/sanitizer_linux.cc
new file mode 100644 (file)
index 0000000..dc3efe2
--- /dev/null
@@ -0,0 +1,1257 @@
+//===-- sanitizer_linux.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements linux-specific functions from
+// sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+
+#if !SANITIZER_FREEBSD
+#include <asm/param.h>
+#endif
+
+// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
+// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
+// access stat from asm/stat.h, without conflicting with definition in
+// sys/stat.h, we use this trick.
+#if defined(__mips64)
+#include <asm/unistd.h>
+#include <sys/types.h>
+#define stat kernel_stat
+#include <asm/stat.h>
+#undef stat
+#endif
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#if SANITIZER_FREEBSD
+#include <sys/sysctl.h>
+#include <machine/atomic.h>
+extern "C" {
+// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
+// FreeBSD 9.2 and 10.0.
+#include <sys/umtx.h>
+}
+extern char **environ;  // provided by crt1
+#endif  // SANITIZER_FREEBSD
+
+#if !SANITIZER_ANDROID
+#include <sys/signal.h>
+#endif
+
+#if SANITIZER_LINUX
+// <linux/time.h>
+struct kernel_timeval {
+  long tv_sec;
+  long tv_usec;
+};
+
+// <linux/futex.h> is broken on some linux distributions.
+const int FUTEX_WAIT = 0;
+const int FUTEX_WAKE = 1;
+#endif  // SANITIZER_LINUX
+
+// Are we using 32-bit or 64-bit Linux syscalls?
+// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
+// but it still needs to use 64-bit syscalls.
+#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_WORDSIZE == 64)
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
+#else
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
+#endif
+
+namespace __sanitizer {
+
+#if SANITIZER_LINUX && defined(__x86_64__)
+#include "sanitizer_syscall_linux_x86_64.inc"
+#elif SANITIZER_LINUX && defined(__aarch64__)
+#include "sanitizer_syscall_linux_aarch64.inc"
+#else
+#include "sanitizer_syscall_generic.inc"
+#endif
+
+// --------------- sanitizer_libc.h
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+                   OFF_T offset) {
+#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+  return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
+                          offset);
+#else
+  // mmap2 specifies file offset in 4096-byte units.
+  CHECK(IsAligned(offset, 4096));
+  return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd,
+                          offset / 4096);
+#endif
+}
+
+uptr internal_munmap(void *addr, uptr length) {
+  return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+  return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
+}
+
+uptr internal_close(fd_t fd) {
+  return internal_syscall(SYSCALL(close), fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags);
+#else
+  return internal_syscall(SYSCALL(open), (uptr)filename, flags);
+#endif
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags,
+                          mode);
+#else
+  return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode);
+#endif
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+  sptr res;
+  HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf,
+               count));
+  return res;
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+  sptr res;
+  HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(write), fd, (uptr)buf,
+               count));
+  return res;
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+  sptr res;
+  HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
+               (OFF_T)size));
+  return res;
+}
+
+#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD
+static void stat64_to_stat(struct stat64 *in, struct stat *out) {
+  internal_memset(out, 0, sizeof(*out));
+  out->st_dev = in->st_dev;
+  out->st_ino = in->st_ino;
+  out->st_mode = in->st_mode;
+  out->st_nlink = in->st_nlink;
+  out->st_uid = in->st_uid;
+  out->st_gid = in->st_gid;
+  out->st_rdev = in->st_rdev;
+  out->st_size = in->st_size;
+  out->st_blksize = in->st_blksize;
+  out->st_blocks = in->st_blocks;
+  out->st_atime = in->st_atime;
+  out->st_mtime = in->st_mtime;
+  out->st_ctime = in->st_ctime;
+  out->st_ino = in->st_ino;
+}
+#endif
+
+#if defined(__mips64)
+static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
+  internal_memset(out, 0, sizeof(*out));
+  out->st_dev = in->st_dev;
+  out->st_ino = in->st_ino;
+  out->st_mode = in->st_mode;
+  out->st_nlink = in->st_nlink;
+  out->st_uid = in->st_uid;
+  out->st_gid = in->st_gid;
+  out->st_rdev = in->st_rdev;
+  out->st_size = in->st_size;
+  out->st_blksize = in->st_blksize;
+  out->st_blocks = in->st_blocks;
+  out->st_atime = in->st_atime_nsec;
+  out->st_mtime = in->st_mtime_nsec;
+  out->st_ctime = in->st_ctime_nsec;
+  out->st_ino = in->st_ino;
+}
+#endif
+
+uptr internal_stat(const char *path, void *buf) {
+#if SANITIZER_FREEBSD
+  return internal_syscall(SYSCALL(stat), path, buf);
+#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path,
+                          (uptr)buf, 0);
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
+# if defined(__mips64)
+  // For mips64, stat syscall fills buffer in the format of kernel_stat
+  struct kernel_stat kbuf;
+  int res = internal_syscall(SYSCALL(stat), path, &kbuf);
+  kernel_stat_to_stat(&kbuf, (struct stat *)buf);
+  return res;
+# else
+  return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf);
+# endif
+#else
+  struct stat64 buf64;
+  int res = internal_syscall(SYSCALL(stat64), path, &buf64);
+  stat64_to_stat(&buf64, (struct stat *)buf);
+  return res;
+#endif
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+#if SANITIZER_FREEBSD
+  return internal_syscall(SYSCALL(lstat), path, buf);
+#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path,
+                         (uptr)buf, AT_SYMLINK_NOFOLLOW);
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
+  return internal_syscall(SYSCALL(lstat), (uptr)path, (uptr)buf);
+#else
+  struct stat64 buf64;
+  int res = internal_syscall(SYSCALL(lstat64), path, &buf64);
+  stat64_to_stat(&buf64, (struct stat *)buf);
+  return res;
+#endif
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+  return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
+#else
+  struct stat64 buf64;
+  int res = internal_syscall(SYSCALL(fstat64), fd, &buf64);
+  stat64_to_stat(&buf64, (struct stat *)buf);
+  return res;
+#endif
+}
+
+uptr internal_filesize(fd_t fd) {
+  struct stat st;
+  if (internal_fstat(fd, &st))
+    return -1;
+  return (uptr)st.st_size;
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0);
+#else
+  return internal_syscall(SYSCALL(dup2), oldfd, newfd);
+#endif
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(readlinkat), AT_FDCWD,
+                          (uptr)path, (uptr)buf, bufsize);
+#else
+  return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize);
+#endif
+}
+
+uptr internal_unlink(const char *path) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0);
+#else
+  return internal_syscall(SYSCALL(unlink), (uptr)path);
+#endif
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
+                          (uptr)newpath);
+#else
+  return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
+#endif
+}
+
+uptr internal_sched_yield() {
+  return internal_syscall(SYSCALL(sched_yield));
+}
+
+void internal__exit(int exitcode) {
+#if SANITIZER_FREEBSD
+  internal_syscall(SYSCALL(exit), exitcode);
+#else
+  internal_syscall(SYSCALL(exit_group), exitcode);
+#endif
+  Die();  // Unreachable.
+}
+
+uptr internal_execve(const char *filename, char *const argv[],
+                     char *const envp[]) {
+  return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv,
+                          (uptr)envp);
+}
+
+// ----------------- sanitizer_common.h
+bool FileExists(const char *filename) {
+  struct stat st;
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0))
+#else
+  if (internal_stat(filename, &st))
+#endif
+    return false;
+  // Sanity check: filename is a regular file.
+  return S_ISREG(st.st_mode);
+}
+
+uptr GetTid() {
+#if SANITIZER_FREEBSD
+  return (uptr)pthread_self();
+#else
+  return internal_syscall(SYSCALL(gettid));
+#endif
+}
+
+u64 NanoTime() {
+#if SANITIZER_FREEBSD
+  timeval tv;
+#else
+  kernel_timeval tv;
+#endif
+  internal_memset(&tv, 0, sizeof(tv));
+  internal_syscall(SYSCALL(gettimeofday), (uptr)&tv, 0);
+  return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
+}
+
+// Like getenv, but reads env directly from /proc (on Linux) or parses the
+// 'environ' array (on FreeBSD) and does not use libc. This function should be
+// called first inside __asan_init.
+const char *GetEnv(const char *name) {
+#if SANITIZER_FREEBSD
+  if (::environ != 0) {
+    uptr NameLen = internal_strlen(name);
+    for (char **Env = ::environ; *Env != 0; Env++) {
+      if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
+        return (*Env) + NameLen + 1;
+    }
+  }
+  return 0;  // Not found.
+#elif SANITIZER_LINUX
+  static char *environ;
+  static uptr len;
+  static bool inited;
+  if (!inited) {
+    inited = true;
+    uptr environ_size;
+    if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len))
+      environ = nullptr;
+  }
+  if (!environ || len == 0) return nullptr;
+  uptr namelen = internal_strlen(name);
+  const char *p = environ;
+  while (*p != '\0') {  // will happen at the \0\0 that terminates the buffer
+    // proc file has the format NAME=value\0NAME=value\0NAME=value\0...
+    const char* endp =
+        (char*)internal_memchr(p, '\0', len - (p - environ));
+    if (!endp)  // this entry isn't NUL terminated
+      return nullptr;
+    else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=')  // Match.
+      return p + namelen + 1;  // point after =
+    p = endp + 1;
+  }
+  return nullptr;  // Not found.
+#else
+#error "Unsupported platform"
+#endif
+}
+
+extern "C" {
+  SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end;
+}
+
+#if !SANITIZER_GO
+static void ReadNullSepFileToArray(const char *path, char ***arr,
+                                   int arr_size) {
+  char *buff;
+  uptr buff_size;
+  uptr buff_len;
+  *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");
+  if (!ReadFileToBuffer(path, &buff, &buff_size, &buff_len, 1024 * 1024)) {
+    (*arr)[0] = nullptr;
+    return;
+  }
+  (*arr)[0] = buff;
+  int count, i;
+  for (count = 1, i = 1; ; i++) {
+    if (buff[i] == 0) {
+      if (buff[i+1] == 0) break;
+      (*arr)[count] = &buff[i+1];
+      CHECK_LE(count, arr_size - 1);  // FIXME: make this more flexible.
+      count++;
+    }
+  }
+  (*arr)[count] = nullptr;
+}
+#endif
+
+static void GetArgsAndEnv(char ***argv, char ***envp) {
+#if !SANITIZER_GO
+  if (&__libc_stack_end) {
+#endif
+    uptr* stack_end = (uptr*)__libc_stack_end;
+    int argc = *stack_end;
+    *argv = (char**)(stack_end + 1);
+    *envp = (char**)(stack_end + argc + 2);
+#if !SANITIZER_GO
+  } else {
+    static const int kMaxArgv = 2000, kMaxEnvp = 2000;
+    ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv);
+    ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp);
+  }
+#endif
+}
+
+char **GetArgv() {
+  char **argv, **envp;
+  GetArgsAndEnv(&argv, &envp);
+  return argv;
+}
+
+void ReExec() {
+  char **argv, **envp;
+  GetArgsAndEnv(&argv, &envp);
+  uptr rv = internal_execve("/proc/self/exe", argv, envp);
+  int rverrno;
+  CHECK_EQ(internal_iserror(rv, &rverrno), true);
+  Printf("execve failed, errno %d\n", rverrno);
+  Die();
+}
+
+enum MutexState {
+  MtxUnlocked = 0,
+  MtxLocked = 1,
+  MtxSleeping = 2
+};
+
+BlockingMutex::BlockingMutex() {
+  internal_memset(this, 0, sizeof(*this));
+}
+
+void BlockingMutex::Lock() {
+  CHECK_EQ(owner_, 0);
+  atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+  if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
+    return;
+  while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
+#if SANITIZER_FREEBSD
+    _umtx_op(m, UMTX_OP_WAIT_UINT, MtxSleeping, 0, 0);
+#else
+    internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0);
+#endif
+  }
+}
+
+void BlockingMutex::Unlock() {
+  atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+  u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed);
+  CHECK_NE(v, MtxUnlocked);
+  if (v == MtxSleeping) {
+#if SANITIZER_FREEBSD
+    _umtx_op(m, UMTX_OP_WAKE, 1, 0, 0);
+#else
+    internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE, 1, 0, 0, 0);
+#endif
+  }
+}
+
+void BlockingMutex::CheckLocked() {
+  atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+  CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
+}
+
+// ----------------- sanitizer_linux.h
+// The actual size of this structure is specified by d_reclen.
+// Note that getdents64 uses a different structure format. We only provide the
+// 32-bit syscall here.
+struct linux_dirent {
+#if SANITIZER_X32 || defined(__aarch64__)
+  u64 d_ino;
+  u64 d_off;
+#else
+  unsigned long      d_ino;
+  unsigned long      d_off;
+#endif
+  unsigned short     d_reclen;
+#ifdef __aarch64__
+  unsigned char      d_type;
+#endif
+  char               d_name[256];
+};
+
+// Syscall wrappers.
+uptr internal_ptrace(int request, int pid, void *addr, void *data) {
+  return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
+                          (uptr)data);
+}
+
+uptr internal_waitpid(int pid, int *status, int options) {
+  return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options,
+                          0 /* rusage */);
+}
+
+uptr internal_getpid() {
+  return internal_syscall(SYSCALL(getpid));
+}
+
+uptr internal_getppid() {
+  return internal_syscall(SYSCALL(getppid));
+}
+
+uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count);
+#else
+  return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count);
+#endif
+}
+
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
+  return internal_syscall(SYSCALL(lseek), fd, offset, whence);
+}
+
+#if SANITIZER_LINUX
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
+  return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5);
+}
+#endif
+
+uptr internal_sigaltstack(const struct sigaltstack *ss,
+                         struct sigaltstack *oss) {
+  return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
+}
+
+int internal_fork() {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+  return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
+#else
+  return internal_syscall(SYSCALL(fork));
+#endif
+}
+
+#if SANITIZER_LINUX
+#define SA_RESTORER 0x04000000
+// Doesn't set sa_restorer, use with caution (see below).
+int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
+  __sanitizer_kernel_sigaction_t k_act, k_oldact;
+  internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t));
+  internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t));
+  const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act;
+  __sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact;
+  if (u_act) {
+    k_act.handler = u_act->handler;
+    k_act.sigaction = u_act->sigaction;
+    internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
+                    sizeof(__sanitizer_kernel_sigset_t));
+    // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
+    k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
+    // FIXME: most often sa_restorer is unset, however the kernel requires it
+    // to point to a valid signal restorer that calls the rt_sigreturn syscall.
+    // If sa_restorer passed to the kernel is NULL, the program may crash upon
+    // signal delivery or fail to unwind the stack in the signal handler.
+    // libc implementation of sigaction() passes its own restorer to
+    // rt_sigaction, so we need to do the same (we'll need to reimplement the
+    // restorers; for x86_64 the restorer address can be obtained from
+    // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact).
+    k_act.sa_restorer = u_act->sa_restorer;
+  }
+
+  uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum,
+      (uptr)(u_act ? &k_act : nullptr),
+      (uptr)(u_oldact ? &k_oldact : nullptr),
+      (uptr)sizeof(__sanitizer_kernel_sigset_t));
+
+  if ((result == 0) && u_oldact) {
+    u_oldact->handler = k_oldact.handler;
+    u_oldact->sigaction = k_oldact.sigaction;
+    internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask,
+                    sizeof(__sanitizer_kernel_sigset_t));
+    u_oldact->sa_flags = k_oldact.sa_flags;
+    u_oldact->sa_restorer = k_oldact.sa_restorer;
+  }
+  return result;
+}
+#endif  // SANITIZER_LINUX
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+    __sanitizer_sigset_t *oldset) {
+#if SANITIZER_FREEBSD
+  return internal_syscall(SYSCALL(sigprocmask), how, set, oldset);
+#else
+  __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
+  __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset;
+  return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how,
+                          (uptr)&k_set->sig[0], (uptr)&k_oldset->sig[0],
+                          sizeof(__sanitizer_kernel_sigset_t));
+#endif
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) {
+  internal_memset(set, 0xff, sizeof(*set));
+}
+
+#if SANITIZER_LINUX
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
+  signum -= 1;
+  CHECK_GE(signum, 0);
+  CHECK_LT(signum, sizeof(*set) * 8);
+  __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
+  const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
+  const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
+  k_set->sig[idx] &= ~(1 << bit);
+}
+#endif  // SANITIZER_LINUX
+
+// ThreadLister implementation.
+ThreadLister::ThreadLister(int pid)
+  : pid_(pid),
+    descriptor_(-1),
+    buffer_(4096),
+    error_(true),
+    entry_((struct linux_dirent *)buffer_.data()),
+    bytes_read_(0) {
+  char task_directory_path[80];
+  internal_snprintf(task_directory_path, sizeof(task_directory_path),
+                    "/proc/%d/task/", pid);
+  uptr openrv = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY);
+  if (internal_iserror(openrv)) {
+    error_ = true;
+    Report("Can't open /proc/%d/task for reading.\n", pid);
+  } else {
+    error_ = false;
+    descriptor_ = openrv;
+  }
+}
+
+int ThreadLister::GetNextTID() {
+  int tid = -1;
+  do {
+    if (error_)
+      return -1;
+    if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries())
+      return -1;
+    if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' &&
+        entry_->d_name[0] <= '9') {
+      // Found a valid tid.
+      tid = (int)internal_atoll(entry_->d_name);
+    }
+    entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen);
+  } while (tid < 0);
+  return tid;
+}
+
+void ThreadLister::Reset() {
+  if (error_ || descriptor_ < 0)
+    return;
+  internal_lseek(descriptor_, 0, SEEK_SET);
+}
+
+ThreadLister::~ThreadLister() {
+  if (descriptor_ >= 0)
+    internal_close(descriptor_);
+}
+
+bool ThreadLister::error() { return error_; }
+
+bool ThreadLister::GetDirectoryEntries() {
+  CHECK_GE(descriptor_, 0);
+  CHECK_NE(error_, true);
+  bytes_read_ = internal_getdents(descriptor_,
+                                  (struct linux_dirent *)buffer_.data(),
+                                  buffer_.size());
+  if (internal_iserror(bytes_read_)) {
+    Report("Can't read directory entries from /proc/%d/task.\n", pid_);
+    error_ = true;
+    return false;
+  } else if (bytes_read_ == 0) {
+    return false;
+  }
+  entry_ = (struct linux_dirent *)buffer_.data();
+  return true;
+}
+
+uptr GetPageSize() {
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__))
+  return EXEC_PAGESIZE;
+#else
+  return sysconf(_SC_PAGESIZE);  // EXEC_PAGESIZE may not be trustworthy.
+#endif
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+#if SANITIZER_FREEBSD
+  const int Mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+  const char *default_module_name = "kern.proc.pathname";
+  size_t Size = buf_len;
+  bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
+  int readlink_error = IsErr ? errno : 0;
+  uptr module_name_len = Size;
+#else
+  const char *default_module_name = "/proc/self/exe";
+  uptr module_name_len = internal_readlink(
+      default_module_name, buf, buf_len);
+  int readlink_error;
+  bool IsErr = internal_iserror(module_name_len, &readlink_error);
+#endif
+  if (IsErr) {
+    // We can't read binary name for some reason, assume it's unknown.
+    Report("WARNING: reading executable name failed with errno %d, "
+           "some stack frames may not be symbolized\n", readlink_error);
+    module_name_len = internal_snprintf(buf, buf_len, "%s",
+                                        default_module_name);
+    CHECK_LT(module_name_len, buf_len);
+  }
+  return module_name_len;
+}
+
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
+#if SANITIZER_LINUX
+  char *tmpbuf;
+  uptr tmpsize;
+  uptr tmplen;
+  if (ReadFileToBuffer("/proc/self/cmdline", &tmpbuf, &tmpsize, &tmplen,
+                       1024 * 1024)) {
+    internal_strncpy(buf, tmpbuf, buf_len);
+    UnmapOrDie(tmpbuf, tmpsize);
+    return internal_strlen(buf);
+  }
+#endif
+  return ReadBinaryName(buf, buf_len);
+}
+
+// Match full names of the form /path/to/base_name{-,.}*
+bool LibraryNameIs(const char *full_name, const char *base_name) {
+  const char *name = full_name;
+  // Strip path.
+  while (*name != '\0') name++;
+  while (name > full_name && *name != '/') name--;
+  if (*name == '/') name++;
+  uptr base_name_length = internal_strlen(base_name);
+  if (internal_strncmp(name, base_name, base_name_length)) return false;
+  return (name[base_name_length] == '-' || name[base_name_length] == '.');
+}
+
+#if !SANITIZER_ANDROID
+// Call cb for each region mapped by map.
+void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
+  CHECK_NE(map, nullptr);
+#if !SANITIZER_FREEBSD
+  typedef ElfW(Phdr) Elf_Phdr;
+  typedef ElfW(Ehdr) Elf_Ehdr;
+#endif  // !SANITIZER_FREEBSD
+  char *base = (char *)map->l_addr;
+  Elf_Ehdr *ehdr = (Elf_Ehdr *)base;
+  char *phdrs = base + ehdr->e_phoff;
+  char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize;
+
+  // Find the segment with the minimum base so we can "relocate" the p_vaddr
+  // fields.  Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC
+  // objects have a non-zero base.
+  uptr preferred_base = (uptr)-1;
+  for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {
+    Elf_Phdr *phdr = (Elf_Phdr *)iter;
+    if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr)
+      preferred_base = (uptr)phdr->p_vaddr;
+  }
+
+  // Compute the delta from the real base to get a relocation delta.
+  sptr delta = (uptr)base - preferred_base;
+  // Now we can figure out what the loader really mapped.
+  for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {
+    Elf_Phdr *phdr = (Elf_Phdr *)iter;
+    if (phdr->p_type == PT_LOAD) {
+      uptr seg_start = phdr->p_vaddr + delta;
+      uptr seg_end = seg_start + phdr->p_memsz;
+      // None of these values are aligned.  We consider the ragged edges of the
+      // load command as defined, since they are mapped from the file.
+      seg_start = RoundDownTo(seg_start, GetPageSizeCached());
+      seg_end = RoundUpTo(seg_end, GetPageSizeCached());
+      cb((void *)seg_start, seg_end - seg_start);
+    }
+  }
+}
+#endif
+
+#if defined(__x86_64__) && SANITIZER_LINUX
+// We cannot use glibc's clone wrapper, because it messes with the child
+// task's TLS. It writes the PID and TID of the child task to its thread
+// descriptor, but in our case the child task shares the thread descriptor with
+// the parent (because we don't know how to allocate a new thread
+// descriptor to keep glibc happy). So the stock version of clone(), when
+// used with CLONE_VM, would end up corrupting the parent's thread descriptor.
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr) {
+  long long res;
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+  ((unsigned long long *)child_stack)[0] = (uptr)fn;
+  ((unsigned long long *)child_stack)[1] = (uptr)arg;
+  register void *r8 __asm__("r8") = newtls;
+  register int *r10 __asm__("r10") = child_tidptr;
+  __asm__ __volatile__(
+                       /* %rax = syscall(%rax = SYSCALL(clone),
+                        *                %rdi = flags,
+                        *                %rsi = child_stack,
+                        *                %rdx = parent_tidptr,
+                        *                %r8  = new_tls,
+                        *                %r10 = child_tidptr)
+                        */
+                       "syscall\n"
+
+                       /* if (%rax != 0)
+                        *   return;
+                        */
+                       "testq  %%rax,%%rax\n"
+                       "jnz    1f\n"
+
+                       /* In the child. Terminate unwind chain. */
+                       // XXX: We should also terminate the CFI unwind chain
+                       // here. Unfortunately clang 3.2 doesn't support the
+                       // necessary CFI directives, so we skip that part.
+                       "xorq   %%rbp,%%rbp\n"
+
+                       /* Call "fn(arg)". */
+                       "popq   %%rax\n"
+                       "popq   %%rdi\n"
+                       "call   *%%rax\n"
+
+                       /* Call _exit(%rax). */
+                       "movq   %%rax,%%rdi\n"
+                       "movq   %2,%%rax\n"
+                       "syscall\n"
+
+                       /* Return to parent. */
+                     "1:\n"
+                       : "=a" (res)
+                       : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
+                         "S"(child_stack),
+                         "D"(flags),
+                         "d"(parent_tidptr),
+                         "r"(r8),
+                         "r"(r10)
+                       : "rsp", "memory", "r11", "rcx");
+  return res;
+}
+#elif defined(__mips__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr) {
+  long long res;
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+  ((unsigned long long *)child_stack)[0] = (uptr)fn;
+  ((unsigned long long *)child_stack)[1] = (uptr)arg;
+  register void *a3 __asm__("$7") = newtls;
+  register int *a4 __asm__("$8") = child_tidptr;
+  // We don't have proper CFI directives here because it requires alot of code
+  // for very marginal benefits.
+  __asm__ __volatile__(
+                       /* $v0 = syscall($v0 = __NR_clone,
+                        * $a0 = flags,
+                        * $a1 = child_stack,
+                        * $a2 = parent_tidptr,
+                        * $a3 = new_tls,
+                        * $a4 = child_tidptr)
+                        */
+                       ".cprestore 16;\n"
+                       "move $4,%1;\n"
+                       "move $5,%2;\n"
+                       "move $6,%3;\n"
+                       "move $7,%4;\n"
+                       /* Store the fifth argument on stack
+                        * if we are using 32-bit abi.
+                        */
+#if SANITIZER_WORDSIZE == 32
+                       "lw %5,16($29);\n"
+#else
+                       "move $8,%5;\n"
+#endif
+                       "li $2,%6;\n"
+                       "syscall;\n"
+
+                       /* if ($v0 != 0)
+                        * return;
+                        */
+                       "bnez $2,1f;\n"
+
+                       /* Call "fn(arg)". */
+                       "ld $25,0($29);\n"
+                       "ld $4,8($29);\n"
+                       "jal $25;\n"
+
+                       /* Call _exit($v0). */
+                       "move $4,$2;\n"
+                       "li $2,%7;\n"
+                       "syscall;\n"
+
+                       /* Return to parent. */
+                     "1:\n"
+                       : "=r" (res)
+                       : "r"(flags),
+                         "r"(child_stack),
+                         "r"(parent_tidptr),
+                         "r"(a3),
+                         "r"(a4),
+                         "i"(__NR_clone),
+                         "i"(__NR_exit)
+                       : "memory", "$29" );
+  return res;
+}
+#elif defined(__aarch64__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr) {
+  long long res;
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+  ((unsigned long long *)child_stack)[0] = (uptr)fn;
+  ((unsigned long long *)child_stack)[1] = (uptr)arg;
+
+  register int (*__fn)(void *)  __asm__("x0") = fn;
+  register void *__stack __asm__("x1") = child_stack;
+  register int   __flags __asm__("x2") = flags;
+  register void *__arg   __asm__("x3") = arg;
+  register int  *__ptid  __asm__("x4") = parent_tidptr;
+  register void *__tls   __asm__("x5") = newtls;
+  register int  *__ctid  __asm__("x6") = child_tidptr;
+
+  __asm__ __volatile__(
+                       "mov x0,x2\n" /* flags  */
+                       "mov x2,x4\n" /* ptid  */
+                       "mov x3,x5\n" /* tls  */
+                       "mov x4,x6\n" /* ctid  */
+                       "mov x8,%9\n" /* clone  */
+
+                       "svc 0x0\n"
+
+                       /* if (%r0 != 0)
+                        *   return %r0;
+                        */
+                       "cmp x0, #0\n"
+                       "bne 1f\n"
+
+                       /* In the child, now. Call "fn(arg)". */
+                       "ldp x1, x0, [sp], #16\n"
+                       "blr x1\n"
+
+                       /* Call _exit(%r0).  */
+                       "mov x8, %10\n"
+                       "svc 0x0\n"
+                     "1:\n"
+
+                       : "=r" (res)
+                       : "i"(-EINVAL),
+                         "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
+                         "r"(__ptid), "r"(__tls), "r"(__ctid),
+                         "i"(__NR_clone), "i"(__NR_exit)
+                       : "x30", "memory");
+  return res;
+}
+#elif defined(__arm__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr) {
+  unsigned int res;
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 2 * sizeof(unsigned int);
+  ((unsigned int *)child_stack)[0] = (uptr)fn;
+  ((unsigned int *)child_stack)[1] = (uptr)arg;
+  register int r0 __asm__("r0") = flags;
+  register void *r1 __asm__("r1") = child_stack;
+  register int *r2 __asm__("r2") = parent_tidptr;
+  register void *r3 __asm__("r3") = newtls;
+  register int *r4 __asm__("r4") = child_tidptr;
+  register int r7 __asm__("r7") = __NR_clone;
+  __asm__ __volatile__("push    {r4, r7}\n"
+                       "swi 0x0\n"
+                       "cmp r0, #0\n"
+                       "bne 1f\n"
+                       "ldr r0, [sp, #4]\n"
+#if defined(__ARM_ARCH_4T__) && defined(__THUMB_INTERWORK__)
+                       "ldr     ip, [sp], #8\n"
+                       "mov lr, pc\n"
+                       "bx      ip\n"
+#else
+                       "ldr     lr, [sp], #8\n"
+                       "blx lr\n"
+#endif
+                       "mov r7, %7\n"
+                       "swi 0x0\n"
+                       "1:\n"
+                       "pop {r4, r7}\n"
+                       "mov %0, r0\n"
+                       : "=r"(res)
+                       : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
+                         "i"(__NR_exit)
+                       : "memory");
+  return res;
+}
+#elif defined(__i386__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                    int *parent_tidptr, void *newtls, int *child_tidptr) {
+  int res;
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 7 * sizeof(unsigned int);
+  ((unsigned int *)child_stack)[0] = (uptr)flags;
+  ((unsigned int *)child_stack)[1] = (uptr)0;
+  ((unsigned int *)child_stack)[2] = (uptr)fn;
+  ((unsigned int *)child_stack)[3] = (uptr)arg;
+  __asm__ __volatile__(
+                       /* %eax = syscall(%eax = SYSCALL(clone),
+                        *                %ebx = flags,
+                        *                %ecx = child_stack,
+                        *                %edx = parent_tidptr,
+                        *                %esi  = new_tls,
+                        *                %edi = child_tidptr)
+                        */
+
+                        /* Obtain flags */
+                        "movl    (%%ecx), %%ebx\n"
+                        /* Do the system call */
+                        "pushl   %%ebx\n"
+                        "pushl   %%esi\n"
+                        "pushl   %%edi\n"
+                        /* Remember the flag value.  */
+                        "movl    %%ebx, (%%ecx)\n"
+                        "int     $0x80\n"
+                        "popl    %%edi\n"
+                        "popl    %%esi\n"
+                        "popl    %%ebx\n"
+
+                        /* if (%eax != 0)
+                         *   return;
+                         */
+
+                        "test    %%eax,%%eax\n"
+                        "jnz    1f\n"
+
+                        /* terminate the stack frame */
+                        "xorl   %%ebp,%%ebp\n"
+                        /* Call FN. */
+                        "call    *%%ebx\n"
+#ifdef PIC
+                        "call    here\n"
+                        "here:\n"
+                        "popl    %%ebx\n"
+                        "addl    $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
+#endif
+                        /* Call exit */
+                        "movl    %%eax, %%ebx\n"
+                        "movl    %2, %%eax\n"
+                        "int     $0x80\n"
+                        "1:\n"
+                       : "=a" (res)
+                       : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
+                         "c"(child_stack),
+                         "d"(parent_tidptr),
+                         "S"(newtls),
+                         "D"(child_tidptr)
+                       : "memory", "esp");
+  return res;
+}
+#endif  // defined(__x86_64__) && SANITIZER_LINUX
+
+#if SANITIZER_ANDROID
+#define PROP_VALUE_MAX 92
+extern "C" SANITIZER_WEAK_ATTRIBUTE int __system_property_get(const char *name,
+                                                              char *value);
+void GetExtraActivationFlags(char *buf, uptr size) {
+  CHECK(size > PROP_VALUE_MAX);
+  CHECK(&__system_property_get);
+  __system_property_get("asan.options", buf);
+}
+
+#if __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+    int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
+static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
+                                   void *data) {
+  // Any name starting with "lib" indicates a bug in L where library base names
+  // are returned instead of paths.
+  if (info->dlpi_name && info->dlpi_name[0] == 'l' &&
+      info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') {
+    *(bool *)data = true;
+    return 1;
+  }
+  return 0;
+}
+
+static atomic_uint32_t android_api_level;
+
+static AndroidApiLevel AndroidDetectApiLevel() {
+  if (!&dl_iterate_phdr)
+    return ANDROID_KITKAT; // K or lower
+  bool base_name_seen = false;
+  dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
+  if (base_name_seen)
+    return ANDROID_LOLLIPOP_MR1; // L MR1
+  return ANDROID_POST_LOLLIPOP;   // post-L
+  // Plain L (API level 21) is completely broken wrt ASan and not very
+  // interesting to detect.
+}
+
+AndroidApiLevel AndroidGetApiLevel() {
+  AndroidApiLevel level =
+      (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
+  if (level) return level;
+  level = AndroidDetectApiLevel();
+  atomic_store(&android_api_level, level, memory_order_relaxed);
+  return level;
+}
+
+#endif
+
+bool IsDeadlySignal(int signum) {
+  if (common_flags()->handle_abort && signum == SIGABRT)
+    return true;
+  if (common_flags()->handle_sigfpe && signum == SIGFPE)
+    return true;
+  return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
+}
+
+#ifndef SANITIZER_GO
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+  // Start the thread with signals blocked, otherwise it can steal user signals.
+  __sanitizer_sigset_t set, old;
+  internal_sigfillset(&set);
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
+  // on any thread, setuid call hangs (see test/tsan/setuid.c).
+  internal_sigdelset(&set, 33);
+#endif
+  internal_sigprocmask(SIG_SETMASK, &set, &old);
+  void *th;
+  real_pthread_create(&th, nullptr, (void*(*)(void *arg))func, arg);
+  internal_sigprocmask(SIG_SETMASK, &old, nullptr);
+  return th;
+}
+
+void internal_join_thread(void *th) {
+  real_pthread_join(th, nullptr);
+}
+#else
+void *internal_start_thread(void (*func)(void *), void *arg) { return 0; }
+
+void internal_join_thread(void *th) {}
+#endif
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+#if defined(__arm__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.arm_pc;
+  *bp = ucontext->uc_mcontext.arm_fp;
+  *sp = ucontext->uc_mcontext.arm_sp;
+#elif defined(__aarch64__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.pc;
+  *bp = ucontext->uc_mcontext.regs[29];
+  *sp = ucontext->uc_mcontext.sp;
+#elif defined(__hppa__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.sc_iaoq[0];
+  /* GCC uses %r3 whenever a frame pointer is needed.  */
+  *bp = ucontext->uc_mcontext.sc_gr[3];
+  *sp = ucontext->uc_mcontext.sc_gr[30];
+#elif defined(__x86_64__)
+# if SANITIZER_FREEBSD
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.mc_rip;
+  *bp = ucontext->uc_mcontext.mc_rbp;
+  *sp = ucontext->uc_mcontext.mc_rsp;
+# else
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.gregs[REG_RIP];
+  *bp = ucontext->uc_mcontext.gregs[REG_RBP];
+  *sp = ucontext->uc_mcontext.gregs[REG_RSP];
+# endif
+#elif defined(__i386__)
+# if SANITIZER_FREEBSD
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.mc_eip;
+  *bp = ucontext->uc_mcontext.mc_ebp;
+  *sp = ucontext->uc_mcontext.mc_esp;
+# else
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.gregs[REG_EIP];
+  *bp = ucontext->uc_mcontext.gregs[REG_EBP];
+  *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# endif
+#elif defined(__powerpc__) || defined(__powerpc64__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.regs->nip;
+  *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+  // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+  // pointer, but GCC always uses r31 when we need a frame pointer.
+  *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+#elif defined(__sparc__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  uptr *stk_ptr;
+# if defined (__arch64__)
+  *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
+  *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
+  stk_ptr = (uptr *) (*sp + 2047);
+  *bp = stk_ptr[15];
+# else
+  *pc = ucontext->uc_mcontext.gregs[REG_PC];
+  *sp = ucontext->uc_mcontext.gregs[REG_O6];
+  stk_ptr = (uptr *) *sp;
+  *bp = stk_ptr[15];
+# endif
+#elif defined(__mips__)
+  ucontext_t *ucontext = (ucontext_t*)context;
+  *pc = ucontext->uc_mcontext.pc;
+  *bp = ucontext->uc_mcontext.gregs[30];
+  *sp = ucontext->uc_mcontext.gregs[29];
+#else
+# error "Unsupported arch"
+#endif
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lsan/src/sanitizer_linux_libcdep.cc b/lsan/src/sanitizer_linux_libcdep.cc
new file mode 100644 (file)
index 0000000..ff69664
--- /dev/null
@@ -0,0 +1,564 @@
+//===-- sanitizer_linux_libcdep.cc ----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements linux-specific functions from
+// sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_freebsd.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
+
+#if SANITIZER_ANDROID || SANITIZER_FREEBSD
+#include <dlfcn.h>  // for dlsym()
+#endif
+
+#include <link.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/resource.h>
+
+#if SANITIZER_FREEBSD
+#include <pthread_np.h>
+#include <osreldate.h>
+#define pthread_getattr_np pthread_attr_get_np
+#endif
+
+#if SANITIZER_LINUX
+#include <sys/prctl.h>
+#endif
+
+#if SANITIZER_ANDROID
+#include <android/api-level.h>
+#endif
+
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+#include <android/log.h>
+#else
+#include <syslog.h>
+#endif
+
+#if !SANITIZER_ANDROID
+#include <elf.h>
+#include <unistd.h>
+#endif
+
+namespace __sanitizer {
+
+SANITIZER_WEAK_ATTRIBUTE int
+real_sigaction(int signum, const void *act, void *oldact);
+
+int internal_sigaction(int signum, const void *act, void *oldact) {
+#if !SANITIZER_GO
+  if (&real_sigaction)
+    return real_sigaction(signum, act, oldact);
+#endif
+  return sigaction(signum, (const struct sigaction *)act,
+                   (struct sigaction *)oldact);
+}
+
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+                                uptr *stack_bottom) {
+  CHECK(stack_top);
+  CHECK(stack_bottom);
+  if (at_initialization) {
+    // This is the main thread. Libpthread may not be initialized yet.
+    struct rlimit rl;
+    CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
+
+    // Find the mapping that contains a stack variable.
+    MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+    uptr start, end, offset;
+    uptr prev_end = 0;
+    while (proc_maps.Next(&start, &end, &offset, nullptr, 0,
+          /* protection */nullptr)) {
+      if ((uptr)&rl < end)
+        break;
+      prev_end = end;
+    }
+    CHECK((uptr)&rl >= start && (uptr)&rl < end);
+
+    // Get stacksize from rlimit, but clip it so that it does not overlap
+    // with other mappings.
+    uptr stacksize = rl.rlim_cur;
+    if (stacksize > end - prev_end)
+      stacksize = end - prev_end;
+    // When running with unlimited stack size, we still want to set some limit.
+    // The unlimited stack size is caused by 'ulimit -s unlimited'.
+    // Also, for some reason, GNU make spawns subprocesses with unlimited stack.
+    if (stacksize > kMaxThreadStackSize)
+      stacksize = kMaxThreadStackSize;
+    *stack_top = end;
+    *stack_bottom = end - stacksize;
+    return;
+  }
+  pthread_attr_t attr;
+  pthread_attr_init(&attr);
+  CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
+  uptr stacksize = 0;
+  void *stackaddr = nullptr;
+  my_pthread_attr_getstack(&attr, &stackaddr, &stacksize);
+  pthread_attr_destroy(&attr);
+
+  CHECK_LE(stacksize, kMaxThreadStackSize);  // Sanity check.
+  *stack_top = (uptr)stackaddr + stacksize;
+  *stack_bottom = (uptr)stackaddr;
+}
+
+#if !SANITIZER_GO
+bool SetEnv(const char *name, const char *value) {
+  void *f = dlsym(RTLD_NEXT, "setenv");
+  if (!f)
+    return false;
+  typedef int(*setenv_ft)(const char *name, const char *value, int overwrite);
+  setenv_ft setenv_f;
+  CHECK_EQ(sizeof(setenv_f), sizeof(f));
+  internal_memcpy(&setenv_f, &f, sizeof(f));
+  return setenv_f(name, value, 1) == 0;
+}
+#endif
+
+bool SanitizerSetThreadName(const char *name) {
+#ifdef PR_SET_NAME
+  return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);  // NOLINT
+#else
+  return false;
+#endif
+}
+
+bool SanitizerGetThreadName(char *name, int max_len) {
+#ifdef PR_GET_NAME
+  char buff[17];
+  if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0))  // NOLINT
+    return false;
+  internal_strncpy(name, buff, max_len);
+  name[max_len] = 0;
+  return true;
+#else
+  return false;
+#endif
+}
+
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
+static uptr g_tls_size;
+#endif
+
+#ifdef __i386__
+# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
+#else
+# define DL_INTERNAL_FUNCTION
+#endif
+
+#if defined(__mips__) || defined(__powerpc64__)
+// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
+// head structure. It lies before the static tls blocks.
+static uptr TlsPreTcbSize() {
+# if defined(__mips__)
+  const uptr kTcbHead = 16; // sizeof (tcbhead_t)
+# elif defined(__powerpc64__)
+  const uptr kTcbHead = 88; // sizeof (tcbhead_t)
+# endif
+  const uptr kTlsAlign = 16;
+  const uptr kTlsPreTcbSize =
+    (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1);
+  InitTlsSize();
+  g_tls_size = (g_tls_size + kTlsPreTcbSize + kTlsAlign -1) & ~(kTlsAlign - 1);
+  return kTlsPreTcbSize;
+}
+#endif
+
+void InitTlsSize() {
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
+// all current supported platforms have 16 bytes stack alignment
+  const size_t kStackAlign = 16;
+  typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
+  get_tls_func get_tls;
+  void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
+  CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr));
+  internal_memcpy(&get_tls, &get_tls_static_info_ptr,
+                  sizeof(get_tls_static_info_ptr));
+  CHECK_NE(get_tls, 0);
+  size_t tls_size = 0;
+  size_t tls_align = 0;
+  get_tls(&tls_size, &tls_align);
+  if (tls_align < kStackAlign)
+    tls_align = kStackAlign;
+  g_tls_size = RoundUpTo(tls_size, tls_align);
+#endif  // !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
+}
+
+#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \
+    || defined(__aarch64__) || defined(__powerpc64__)) \
+    && SANITIZER_LINUX && !SANITIZER_ANDROID
+// sizeof(struct pthread) from glibc.
+static atomic_uintptr_t kThreadDescriptorSize;
+
+uptr ThreadDescriptorSize() {
+  uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
+  if (val)
+    return val;
+#if defined(__x86_64__) || defined(__i386__)
+#ifdef _CS_GNU_LIBC_VERSION
+  char buf[64];
+  uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
+  if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {
+    char *end;
+    int minor = internal_simple_strtoll(buf + 8, &end, 10);
+    if (end != buf + 8 && (*end == '\0' || *end == '.')) {
+      /* sizeof(struct pthread) values from various glibc versions.  */
+      if (SANITIZER_X32)
+        val = 1728;  // Assume only one particular version for x32.
+      else if (minor <= 3)
+        val = FIRST_32_SECOND_64(1104, 1696);
+      else if (minor == 4)
+        val = FIRST_32_SECOND_64(1120, 1728);
+      else if (minor == 5)
+        val = FIRST_32_SECOND_64(1136, 1728);
+      else if (minor <= 9)
+        val = FIRST_32_SECOND_64(1136, 1712);
+      else if (minor == 10)
+        val = FIRST_32_SECOND_64(1168, 1776);
+      else if (minor <= 12)
+        val = FIRST_32_SECOND_64(1168, 2288);
+      else if (minor == 13)
+        val = FIRST_32_SECOND_64(1168, 2304);
+      else
+        val = FIRST_32_SECOND_64(1216, 2304);
+    }
+    if (val)
+      atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+    return val;
+  }
+#endif
+#elif defined(__mips__)
+  // TODO(sagarthakur): add more values as per different glibc versions.
+  val = FIRST_32_SECOND_64(1152, 1776);
+  if (val)
+    atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+  return val;
+#elif defined(__aarch64__)
+  // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
+  val = 1776;
+  atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+  return val;
+#elif defined(__powerpc64__)
+  val = 1776; // from glibc.ppc64le 2.20-8.fc21
+  atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+  return val;
+#endif
+  return 0;
+}
+
+// The offset at which pointer to self is located in the thread descriptor.
+const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16);
+
+uptr ThreadSelfOffset() {
+  return kThreadSelfOffset;
+}
+
+uptr ThreadSelf() {
+  uptr descr_addr;
+# if defined(__i386__)
+  asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
+# elif defined(__x86_64__)
+  asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
+# elif defined(__mips__)
+  // MIPS uses TLS variant I. The thread pointer (in hardware register $29)
+  // points to the end of the TCB + 0x7000. The pthread_descr structure is
+  // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+  // TCB and the size of pthread_descr.
+  const uptr kTlsTcbOffset = 0x7000;
+  uptr thread_pointer;
+  asm volatile(".set push;\
+                .set mips64r2;\
+                rdhwr %0,$29;\
+                .set pop" : "=r" (thread_pointer));
+  descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
+# elif defined(__aarch64__)
+  descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
+# elif defined(__powerpc64__)
+  // PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
+  // points to the end of the TCB + 0x7000. The pthread_descr structure is
+  // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+  // TCB and the size of pthread_descr.
+  const uptr kTlsTcbOffset = 0x7000;
+  uptr thread_pointer;
+  asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
+  descr_addr = thread_pointer - TlsPreTcbSize();
+# else
+#  error "unsupported CPU arch"
+# endif
+  return descr_addr;
+}
+#endif  // (x86_64 || i386 || MIPS) && SANITIZER_LINUX
+
+#if SANITIZER_FREEBSD
+static void **ThreadSelfSegbase() {
+  void **segbase = 0;
+# if defined(__i386__)
+  // sysarch(I386_GET_GSBASE, segbase);
+  __asm __volatile("mov %%gs:0, %0" : "=r" (segbase));
+# elif defined(__x86_64__)
+  // sysarch(AMD64_GET_FSBASE, segbase);
+  __asm __volatile("movq %%fs:0, %0" : "=r" (segbase));
+# else
+#  error "unsupported CPU arch for FreeBSD platform"
+# endif
+  return segbase;
+}
+
+uptr ThreadSelf() {
+  return (uptr)ThreadSelfSegbase()[2];
+}
+#endif  // SANITIZER_FREEBSD
+
+#if !SANITIZER_GO
+static void GetTls(uptr *addr, uptr *size) {
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+# if defined(__x86_64__) || defined(__i386__)
+  *addr = ThreadSelf();
+  *size = GetTlsSize();
+  *addr -= *size;
+  *addr += ThreadDescriptorSize();
+# elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__)
+  *addr = ThreadSelf();
+  *size = GetTlsSize();
+# else
+  *addr = 0;
+  *size = 0;
+# endif
+#elif SANITIZER_FREEBSD
+  void** segbase = ThreadSelfSegbase();
+  *addr = 0;
+  *size = 0;
+  if (segbase != 0) {
+    // tcbalign = 16
+    // tls_size = round(tls_static_space, tcbalign);
+    // dtv = segbase[1];
+    // dtv[2] = segbase - tls_static_space;
+    void **dtv = (void**) segbase[1];
+    *addr = (uptr) dtv[2];
+    *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]);
+  }
+#elif SANITIZER_ANDROID
+  *addr = 0;
+  *size = 0;
+#else
+# error "Unknown OS"
+#endif
+}
+#endif
+
+#if !SANITIZER_GO
+uptr GetTlsSize() {
+#if SANITIZER_FREEBSD || SANITIZER_ANDROID
+  uptr addr, size;
+  GetTls(&addr, &size);
+  return size;
+#else
+  return g_tls_size;
+#endif
+}
+#endif
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+                          uptr *tls_addr, uptr *tls_size) {
+#if SANITIZER_GO
+  // Stub implementation for Go.
+  *stk_addr = *stk_size = *tls_addr = *tls_size = 0;
+#else
+  GetTls(tls_addr, tls_size);
+
+  uptr stack_top, stack_bottom;
+  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+  *stk_addr = stack_bottom;
+  *stk_size = stack_top - stack_bottom;
+
+  if (!main) {
+    // If stack and tls intersect, make them non-intersecting.
+    if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
+      CHECK_GT(*tls_addr + *tls_size, *stk_addr);
+      CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
+      *stk_size -= *tls_size;
+      *tls_addr = *stk_addr + *stk_size;
+    }
+  }
+#endif
+}
+
+# if !SANITIZER_FREEBSD
+typedef ElfW(Phdr) Elf_Phdr;
+# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001  // v9.2
+#  define Elf_Phdr XElf32_Phdr
+#  define dl_phdr_info xdl_phdr_info
+#  define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
+# endif
+
+struct DlIteratePhdrData {
+  LoadedModule *modules;
+  uptr current_n;
+  bool first;
+  uptr max_n;
+  string_predicate_t filter;
+};
+
+static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
+  DlIteratePhdrData *data = (DlIteratePhdrData*)arg;
+  if (data->current_n == data->max_n)
+    return 0;
+  InternalScopedString module_name(kMaxPathLength);
+  if (data->first) {
+    data->first = false;
+    // First module is the binary itself.
+    ReadBinaryNameCached(module_name.data(), module_name.size());
+  } else if (info->dlpi_name) {
+    module_name.append("%s", info->dlpi_name);
+  }
+  if (module_name[0] == '\0')
+    return 0;
+  if (data->filter && !data->filter(module_name.data()))
+    return 0;
+  LoadedModule *cur_module = &data->modules[data->current_n];
+  cur_module->set(module_name.data(), info->dlpi_addr);
+  data->current_n++;
+  for (int i = 0; i < info->dlpi_phnum; i++) {
+    const Elf_Phdr *phdr = &info->dlpi_phdr[i];
+    if (phdr->p_type == PT_LOAD) {
+      uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
+      uptr cur_end = cur_beg + phdr->p_memsz;
+      bool executable = phdr->p_flags & PF_X;
+      cur_module->addAddressRange(cur_beg, cur_end, executable);
+    }
+  }
+  return 0;
+}
+
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+    int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
+                      string_predicate_t filter) {
+#if SANITIZER_ANDROID && __ANDROID_API__ <= 22
+  u32 api_level = AndroidGetApiLevel();
+  // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
+  // The runtime check allows the same library to work with
+  // both K and L (and future) Android releases.
+  if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier
+    MemoryMappingLayout memory_mapping(false);
+    return memory_mapping.DumpListOfModules(modules, max_modules, filter);
+  }
+#endif
+  CHECK(modules);
+  DlIteratePhdrData data = {modules, 0, true, max_modules, filter};
+  dl_iterate_phdr(dl_iterate_phdr_cb, &data);
+  return data.current_n;
+}
+
+// getrusage does not give us the current RSS, only the max RSS.
+// Still, this is better than nothing if /proc/self/statm is not available
+// for some reason, e.g. due to a sandbox.
+static uptr GetRSSFromGetrusage() {
+  struct rusage usage;
+  if (getrusage(RUSAGE_SELF, &usage))  // Failed, probably due to a sandbox.
+    return 0;
+  return usage.ru_maxrss << 10;  // ru_maxrss is in Kb.
+}
+
+uptr GetRSS() {
+  if (!common_flags()->can_use_proc_maps_statm)
+    return GetRSSFromGetrusage();
+  fd_t fd = OpenFile("/proc/self/statm", RdOnly);
+  if (fd == kInvalidFd)
+    return GetRSSFromGetrusage();
+  char buf[64];
+  uptr len = internal_read(fd, buf, sizeof(buf) - 1);
+  internal_close(fd);
+  if ((sptr)len <= 0)
+    return 0;
+  buf[len] = 0;
+  // The format of the file is:
+  // 1084 89 69 11 0 79 0
+  // We need the second number which is RSS in pages.
+  char *pos = buf;
+  // Skip the first number.
+  while (*pos >= '0' && *pos <= '9')
+    pos++;
+  // Skip whitespaces.
+  while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
+    pos++;
+  // Read the number.
+  uptr rss = 0;
+  while (*pos >= '0' && *pos <= '9')
+    rss = rss * 10 + *pos++ - '0';
+  return rss * GetPageSizeCached();
+}
+
+// 64-bit Android targets don't provide the deprecated __android_log_write.
+// Starting with the L release, syslog() works and is preferable to
+// __android_log_write.
+#if SANITIZER_LINUX
+
+#if SANITIZER_ANDROID
+static atomic_uint8_t android_log_initialized;
+
+void AndroidLogInit() {
+  atomic_store(&android_log_initialized, 1, memory_order_release);
+}
+
+static bool IsSyslogAvailable() {
+  return atomic_load(&android_log_initialized, memory_order_acquire);
+}
+#else
+void AndroidLogInit() {}
+
+static bool IsSyslogAvailable() { return true; }
+#endif  // SANITIZER_ANDROID
+
+static void WriteOneLineToSyslog(const char *s) {
+#if SANITIZER_ANDROID &&__ANDROID_API__ < 21
+  __android_log_write(ANDROID_LOG_INFO, NULL, s);
+#else
+  syslog(LOG_INFO, "%s", s);
+#endif
+}
+
+void WriteToSyslog(const char *buffer) {
+  if (!IsSyslogAvailable())
+    return;
+  char *copy = internal_strdup(buffer);
+  char *p = copy;
+  char *q;
+  // syslog, at least on Android, has an implicit message length limit.
+  // Print one line at a time.
+  do {
+    q = internal_strchr(p, '\n');
+    if (q)
+      *q = '\0';
+    WriteOneLineToSyslog(p);
+    if (q)
+      p = q + 1;
+  } while (q);
+  InternalFree(copy);
+}
+#endif // SANITIZER_LINUX
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lsan/src/sanitizer_mac.cc b/lsan/src/sanitizer_mac.cc
new file mode 100644 (file)
index 0000000..3d6f487
--- /dev/null
@@ -0,0 +1,418 @@
+//===-- sanitizer_mac.cc --------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// implements OSX-specific functions.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so
+// the clients will most certainly use 64-bit ones as well.
+#ifndef _DARWIN_USE_64_BIT_INODE
+#define _DARWIN_USE_64_BIT_INODE 1
+#endif
+#include <stdio.h>
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_mac.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_procmaps.h"
+
+#if !SANITIZER_IOS
+#include <crt_externs.h>  // for _NSGetEnviron
+#else
+extern char **environ;
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libkern/OSAtomic.h>
+#include <mach-o/dyld.h>
+#include <mach/mach.h>
+#include <mach/vm_statistics.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace __sanitizer {
+
+#include "sanitizer_syscall_generic.inc"
+
+// ---------------------- sanitizer_libc.h
+uptr internal_mmap(void *addr, size_t length, int prot, int flags,
+                   int fd, u64 offset) {
+  if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_ANALYSIS_TOOL);
+  return (uptr)mmap(addr, length, prot, flags, fd, offset);
+}
+
+uptr internal_munmap(void *addr, uptr length) {
+  return munmap(addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+  return mprotect(addr, length, prot);
+}
+
+uptr internal_close(fd_t fd) {
+  return close(fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+  return open(filename, flags);
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+  return open(filename, flags, mode);
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+  return read(fd, buf, count);
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+  return write(fd, buf, count);
+}
+
+uptr internal_stat(const char *path, void *buf) {
+  return stat(path, (struct stat *)buf);
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+  return lstat(path, (struct stat *)buf);
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+  return fstat(fd, (struct stat *)buf);
+}
+
+uptr internal_filesize(fd_t fd) {
+  struct stat st;
+  if (internal_fstat(fd, &st))
+    return -1;
+  return (uptr)st.st_size;
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+  return dup2(oldfd, newfd);
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+  return readlink(path, buf, bufsize);
+}
+
+uptr internal_unlink(const char *path) {
+  return unlink(path);
+}
+
+uptr internal_sched_yield() {
+  return sched_yield();
+}
+
+void internal__exit(int exitcode) {
+  _exit(exitcode);
+}
+
+uptr internal_getpid() {
+  return getpid();
+}
+
+int internal_sigaction(int signum, const void *act, void *oldact) {
+  return sigaction(signum,
+                   (struct sigaction *)act, (struct sigaction *)oldact);
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+                          __sanitizer_sigset_t *oldset) {
+  return sigprocmask(how, set, oldset);
+}
+
+int internal_fork() {
+  // TODO(glider): this may call user's pthread_atfork() handlers which is bad.
+  return fork();
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+  return rename(oldpath, newpath);
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+  return ftruncate(fd, size);
+}
+
+// ----------------- sanitizer_common.h
+bool FileExists(const char *filename) {
+  struct stat st;
+  if (stat(filename, &st))
+    return false;
+  // Sanity check: filename is a regular file.
+  return S_ISREG(st.st_mode);
+}
+
+uptr GetTid() {
+  return reinterpret_cast<uptr>(pthread_self());
+}
+
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+                                uptr *stack_bottom) {
+  CHECK(stack_top);
+  CHECK(stack_bottom);
+  uptr stacksize = pthread_get_stacksize_np(pthread_self());
+  // pthread_get_stacksize_np() returns an incorrect stack size for the main
+  // thread on Mavericks. See
+  // https://code.google.com/p/address-sanitizer/issues/detail?id=261
+  if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
+      stacksize == (1 << 19))  {
+    struct rlimit rl;
+    CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
+    // Most often rl.rlim_cur will be the desired 8M.
+    if (rl.rlim_cur < kMaxThreadStackSize) {
+      stacksize = rl.rlim_cur;
+    } else {
+      stacksize = kMaxThreadStackSize;
+    }
+  }
+  void *stackaddr = pthread_get_stackaddr_np(pthread_self());
+  *stack_top = (uptr)stackaddr;
+  *stack_bottom = *stack_top - stacksize;
+}
+
+char **GetEnviron() {
+#if !SANITIZER_IOS
+  char ***env_ptr = _NSGetEnviron();
+  if (!env_ptr) {
+    Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
+           "called after libSystem_initializer().\n");
+    CHECK(env_ptr);
+  }
+  char **environ = *env_ptr;
+#endif
+  CHECK(environ);
+  return environ;
+}
+
+const char *GetEnv(const char *name) {
+  char **env = GetEnviron();
+  uptr name_len = internal_strlen(name);
+  while (*env != 0) {
+    uptr len = internal_strlen(*env);
+    if (len > name_len) {
+      const char *p = *env;
+      if (!internal_memcmp(p, name, name_len) &&
+          p[name_len] == '=') {  // Match.
+        return *env + name_len + 1;  // String starting after =.
+      }
+    }
+    env++;
+  }
+  return 0;
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+  CHECK_LE(kMaxPathLength, buf_len);
+
+  // On OS X the executable path is saved to the stack by dyld. Reading it
+  // from there is much faster than calling dladdr, especially for large
+  // binaries with symbols.
+  InternalScopedString exe_path(kMaxPathLength);
+  uint32_t size = exe_path.size();
+  if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
+      realpath(exe_path.data(), buf) != 0) {
+    return internal_strlen(buf);
+  }
+  return 0;
+}
+
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+  return ReadBinaryName(buf, buf_len);
+}
+
+void ReExec() {
+  UNIMPLEMENTED();
+}
+
+uptr GetPageSize() {
+  return sysconf(_SC_PAGESIZE);
+}
+
+BlockingMutex::BlockingMutex() {
+  internal_memset(this, 0, sizeof(*this));
+}
+
+void BlockingMutex::Lock() {
+  CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
+  CHECK_EQ(OS_SPINLOCK_INIT, 0);
+  CHECK_NE(owner_, (uptr)pthread_self());
+  OSSpinLockLock((OSSpinLock*)&opaque_storage_);
+  CHECK(!owner_);
+  owner_ = (uptr)pthread_self();
+}
+
+void BlockingMutex::Unlock() {
+  CHECK(owner_ == (uptr)pthread_self());
+  owner_ = 0;
+  OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
+}
+
+void BlockingMutex::CheckLocked() {
+  CHECK_EQ((uptr)pthread_self(), owner_);
+}
+
+u64 NanoTime() {
+  return 0;
+}
+
+uptr GetTlsSize() {
+  return 0;
+}
+
+void InitTlsSize() {
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+                          uptr *tls_addr, uptr *tls_size) {
+#ifndef SANITIZER_GO
+  uptr stack_top, stack_bottom;
+  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+  *stk_addr = stack_bottom;
+  *stk_size = stack_top - stack_bottom;
+  *tls_addr = 0;
+  *tls_size = 0;
+#else
+  *stk_addr = 0;
+  *stk_size = 0;
+  *tls_addr = 0;
+  *tls_size = 0;
+#endif
+}
+
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
+                      string_predicate_t filter) {
+  MemoryMappingLayout memory_mapping(false);
+  return memory_mapping.DumpListOfModules(modules, max_modules, filter);
+}
+
+bool IsDeadlySignal(int signum) {
+  return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
+}
+
+MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
+
+MacosVersion GetMacosVersionInternal() {
+  int mib[2] = { CTL_KERN, KERN_OSRELEASE };
+  char version[100];
+  uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
+  for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
+  // Get the version length.
+  CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1);
+  CHECK_LT(len, maxlen);
+  CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1);
+  switch (version[0]) {
+    case '9': return MACOS_VERSION_LEOPARD;
+    case '1': {
+      switch (version[1]) {
+        case '0': return MACOS_VERSION_SNOW_LEOPARD;
+        case '1': return MACOS_VERSION_LION;
+        case '2': return MACOS_VERSION_MOUNTAIN_LION;
+        case '3': return MACOS_VERSION_MAVERICKS;
+        case '4': return MACOS_VERSION_YOSEMITE;
+        default:
+          if (IsDigit(version[1]))
+            return MACOS_VERSION_UNKNOWN_NEWER;
+          else
+            return MACOS_VERSION_UNKNOWN;
+      }
+    }
+    default: return MACOS_VERSION_UNKNOWN;
+  }
+}
+
+MacosVersion GetMacosVersion() {
+  atomic_uint32_t *cache =
+      reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
+  MacosVersion result =
+      static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
+  if (result == MACOS_VERSION_UNINITIALIZED) {
+    result = GetMacosVersionInternal();
+    atomic_store(cache, result, memory_order_release);
+  }
+  return result;
+}
+
+uptr GetRSS() {
+  struct task_basic_info info;
+  unsigned count = TASK_BASIC_INFO_COUNT;
+  kern_return_t result =
+      task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
+  if (UNLIKELY(result != KERN_SUCCESS)) {
+    Report("Cannot get task info. Error: %d\n", result);
+    Die();
+  }
+  return info.resident_size;
+}
+
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+  // Start the thread with signals blocked, otherwise it can steal user signals.
+  __sanitizer_sigset_t set, old;
+  internal_sigfillset(&set);
+  internal_sigprocmask(SIG_SETMASK, &set, &old);
+  pthread_t th;
+  pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
+  internal_sigprocmask(SIG_SETMASK, &old, 0);
+  return th;
+}
+
+void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+  ucontext_t *ucontext = (ucontext_t*)context;
+# if defined(__aarch64__)
+  *pc = ucontext->uc_mcontext->__ss.__pc;
+#   if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
+  *bp = ucontext->uc_mcontext->__ss.__fp;
+#   else
+  *bp = ucontext->uc_mcontext->__ss.__lr;
+#   endif
+  *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__x86_64__)
+  *pc = ucontext->uc_mcontext->__ss.__rip;
+  *bp = ucontext->uc_mcontext->__ss.__rbp;
+  *sp = ucontext->uc_mcontext->__ss.__rsp;
+# elif defined(__arm__)
+  *pc = ucontext->uc_mcontext->__ss.__pc;
+  *bp = ucontext->uc_mcontext->__ss.__r[7];
+  *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__i386__)
+  *pc = ucontext->uc_mcontext->__ss.__eip;
+  *bp = ucontext->uc_mcontext->__ss.__ebp;
+  *sp = ucontext->uc_mcontext->__ss.__esp;
+# else
+# error "Unknown architecture"
+# endif
+}
+
+char **GetArgv() {
+  return *_NSGetArgv();
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_MAC
diff --git a/lsan/src/sanitizer_persistent_allocator.cc b/lsan/src/sanitizer_persistent_allocator.cc
new file mode 100644 (file)
index 0000000..b989ed0
--- /dev/null
@@ -0,0 +1,17 @@
+//===-- sanitizer_persistent_allocator.cc -----------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+#include "sanitizer_persistent_allocator.h"
+
+namespace __sanitizer {
+
+PersistentAllocator thePersistentAllocator;
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_platform_limits_linux.cc b/lsan/src/sanitizer_platform_limits_linux.cc
new file mode 100644 (file)
index 0000000..a1f0432
--- /dev/null
@@ -0,0 +1,104 @@
+//===-- sanitizer_platform_limits_linux.cc --------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of linux kernel data structures.
+//===----------------------------------------------------------------------===//
+
+// This is a separate compilation unit for linux headers that conflict with
+// userspace headers.
+// Most "normal" includes go in sanitizer_platform_limits_posix.cc
+
+#include "sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_posix.h"
+
+// For offsetof -> __builtin_offsetof definition.
+#include <stddef.h>
+
+// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that
+// are not defined anywhere in userspace headers. Fake them. This seems to work
+// fine with newer headers, too.
+#include <asm/posix_types.h>
+#if defined(__x86_64__) ||  defined(__mips__)
+#include <sys/stat.h>
+#else
+#define ino_t __kernel_ino_t
+#define mode_t __kernel_mode_t
+#define nlink_t __kernel_nlink_t
+#define uid_t __kernel_uid_t
+#define gid_t __kernel_gid_t
+#define off_t __kernel_off_t
+#define time_t __kernel_time_t
+// This header seems to contain the definitions of _kernel_ stat* structs.
+#include <asm/stat.h>
+#undef ino_t
+#undef mode_t
+#undef nlink_t
+#undef uid_t
+#undef gid_t
+#undef off_t
+#endif
+
+#include <linux/aio_abi.h>
+
+#if !SANITIZER_ANDROID
+#include <sys/statfs.h>
+#include <linux/perf_event.h>
+#endif
+
+namespace __sanitizer {
+#if !SANITIZER_ANDROID
+  unsigned struct_statfs64_sz = sizeof(struct statfs64);
+#endif
+}  // namespace __sanitizer
+
+#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
+                            && !defined(__mips__) && !defined(__sparc__)
+COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
+#endif
+
+COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat));
+
+#if defined(__i386__)
+COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64));
+#endif
+
+CHECK_TYPE_SIZE(io_event);
+CHECK_SIZE_AND_OFFSET(io_event, data);
+CHECK_SIZE_AND_OFFSET(io_event, obj);
+CHECK_SIZE_AND_OFFSET(io_event, res);
+CHECK_SIZE_AND_OFFSET(io_event, res2);
+
+#if !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <=
+               sizeof(struct perf_event_attr));
+CHECK_SIZE_AND_OFFSET(perf_event_attr, type);
+CHECK_SIZE_AND_OFFSET(perf_event_attr, size);
+#endif
+
+COMPILER_CHECK(iocb_cmd_pread == IOCB_CMD_PREAD);
+COMPILER_CHECK(iocb_cmd_pwrite == IOCB_CMD_PWRITE);
+#if !SANITIZER_ANDROID
+COMPILER_CHECK(iocb_cmd_preadv == IOCB_CMD_PREADV);
+COMPILER_CHECK(iocb_cmd_pwritev == IOCB_CMD_PWRITEV);
+#endif
+
+CHECK_TYPE_SIZE(iocb);
+CHECK_SIZE_AND_OFFSET(iocb, aio_data);
+// Skip aio_key, it's weird.
+CHECK_SIZE_AND_OFFSET(iocb, aio_lio_opcode);
+CHECK_SIZE_AND_OFFSET(iocb, aio_reqprio);
+CHECK_SIZE_AND_OFFSET(iocb, aio_fildes);
+CHECK_SIZE_AND_OFFSET(iocb, aio_buf);
+CHECK_SIZE_AND_OFFSET(iocb, aio_nbytes);
+CHECK_SIZE_AND_OFFSET(iocb, aio_offset);
+
+#endif  // SANITIZER_LINUX
diff --git a/lsan/src/sanitizer_platform_limits_posix.cc b/lsan/src/sanitizer_platform_limits_posix.cc
new file mode 100644 (file)
index 0000000..0933685
--- /dev/null
@@ -0,0 +1,1263 @@
+//===-- sanitizer_platform_limits_posix.cc --------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific POSIX data structures.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
+// Tests in this file assume that off_t-dependent data structures match the
+// libc ABI. For example, struct dirent here is what readdir() function (as
+// exported from libc) returns, and not the user-facing "dirent", which
+// depends on _FILE_OFFSET_BITS setting.
+// To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below.
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS
+#endif
+#if SANITIZER_FREEBSD
+#define _WANT_RTENTRY
+#include <sys/param.h>
+#include <sys/socketvar.h>
+#endif
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <grp.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <termios.h>
+#include <time.h>
+#include <wchar.h>
+
+#if !SANITIZER_IOS
+#include <net/route.h>
+#endif
+
+#if !SANITIZER_ANDROID
+#include <sys/mount.h>
+#include <sys/timeb.h>
+#endif
+
+#if SANITIZER_LINUX
+#include <malloc.h>
+#include <mntent.h>
+#include <netinet/ether.h>
+#include <sys/sysinfo.h>
+#include <sys/vt.h>
+#include <linux/cdrom.h>
+#include <linux/fd.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#include <linux/input.h>
+#include <linux/ioctl.h>
+#include <linux/soundcard.h>
+#include <linux/sysctl.h>
+#include <linux/utsname.h>
+#include <linux/posix_types.h>
+#include <net/if_arp.h>
+#endif
+
+#if SANITIZER_FREEBSD
+# include <sys/mount.h>
+# include <sys/sockio.h>
+# include <sys/socket.h>
+# include <sys/filio.h>
+# include <sys/signal.h>
+# include <sys/timespec.h>
+# include <sys/timex.h>
+# include <sys/mqueue.h>
+# include <sys/msg.h>
+# include <sys/ipc.h>
+# include <sys/msg.h>
+# include <sys/statvfs.h>
+# include <sys/soundcard.h>
+# include <sys/mtio.h>
+# include <sys/consio.h>
+# include <sys/kbio.h>
+# include <sys/link_elf.h>
+# include <netinet/ip_mroute.h>
+# include <netinet/in.h>
+# include <net/ethernet.h>
+# include <net/ppp_defs.h>
+# include <glob.h>
+# include <term.h>
+
+#define _KERNEL  // to declare 'shminfo' structure
+# include <sys/shm.h>
+#undef _KERNEL
+
+#undef INLINE  // to avoid clashes with sanitizers' definitions
+#endif
+
+#if SANITIZER_FREEBSD || SANITIZER_IOS
+#undef IOC_DIRMASK
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+# include <utime.h>
+# include <sys/ptrace.h>
+# if defined(__mips64) || defined(__aarch64__) || defined(__arm__)
+#  include <asm/ptrace.h>
+#  ifdef __arm__
+typedef struct user_fpregs elf_fpregset_t;
+#  endif
+# endif
+# include <semaphore.h>
+#endif
+
+#if !SANITIZER_ANDROID
+#include <ifaddrs.h>
+#include <sys/ucontext.h>
+#include <wordexp.h>
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#include <glob.h>
+#include <obstack.h>
+#include <mqueue.h>
+#include <net/if_ppp.h>
+#include <netax25/ax25.h>
+#include <netipx/ipx.h>
+#include <netrom/netrom.h>
+#if HAVE_RPC_XDR_H
+# include <rpc/xdr.h>
+#elif HAVE_TIRPC_RPC_XDR_H
+# include <tirpc/rpc/xdr.h>
+#endif
+#include <scsi/scsi.h>
+#include <sys/mtio.h>
+#include <sys/kd.h>
+#include <sys/shm.h>
+#include <sys/statvfs.h>
+#include <sys/timex.h>
+#if defined(__mips64)
+# include <sys/procfs.h>
+#endif
+#include <sys/user.h>
+#include <sys/ustat.h>
+#include <linux/cyclades.h>
+#include <linux/if_eql.h>
+#include <linux/if_plip.h>
+#include <linux/lp.h>
+#include <linux/mroute.h>
+#include <linux/mroute6.h>
+#include <linux/scc.h>
+#include <linux/serial.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if SANITIZER_ANDROID
+#include <linux/kd.h>
+#include <linux/mtio.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif
+
+#if SANITIZER_LINUX
+#include <link.h>
+#include <sys/vfs.h>
+#include <sys/epoll.h>
+#include <linux/capability.h>
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_MAC
+#include <net/ethernet.h>
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#endif
+
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_posix.h"
+
+namespace __sanitizer {
+  unsigned struct_utsname_sz = sizeof(struct utsname);
+  unsigned struct_stat_sz = sizeof(struct stat);
+#if !SANITIZER_IOS && !SANITIZER_FREEBSD
+  unsigned struct_stat64_sz = sizeof(struct stat64);
+#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD
+  unsigned struct_rusage_sz = sizeof(struct rusage);
+  unsigned struct_tm_sz = sizeof(struct tm);
+  unsigned struct_passwd_sz = sizeof(struct passwd);
+  unsigned struct_group_sz = sizeof(struct group);
+  unsigned siginfo_t_sz = sizeof(siginfo_t);
+  unsigned struct_sigaction_sz = sizeof(struct sigaction);
+  unsigned struct_itimerval_sz = sizeof(struct itimerval);
+  unsigned pthread_t_sz = sizeof(pthread_t);
+  unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
+  unsigned pid_t_sz = sizeof(pid_t);
+  unsigned timeval_sz = sizeof(timeval);
+  unsigned uid_t_sz = sizeof(uid_t);
+  unsigned gid_t_sz = sizeof(gid_t);
+  unsigned mbstate_t_sz = sizeof(mbstate_t);
+  unsigned sigset_t_sz = sizeof(sigset_t);
+  unsigned struct_timezone_sz = sizeof(struct timezone);
+  unsigned struct_tms_sz = sizeof(struct tms);
+  unsigned struct_sigevent_sz = sizeof(struct sigevent);
+  unsigned struct_sched_param_sz = sizeof(struct sched_param);
+
+
+#if SANITIZER_MAC && !SANITIZER_IOS
+  unsigned struct_statfs64_sz = sizeof(struct statfs64);
+#endif // SANITIZER_MAC && !SANITIZER_IOS
+
+#if !SANITIZER_ANDROID
+  unsigned struct_statfs_sz = sizeof(struct statfs);
+  unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
+  unsigned ucontext_t_sz = sizeof(ucontext_t);
+#endif // !SANITIZER_ANDROID
+
+#if SANITIZER_LINUX
+  unsigned struct_epoll_event_sz = sizeof(struct epoll_event);
+  unsigned struct_sysinfo_sz = sizeof(struct sysinfo);
+  unsigned __user_cap_header_struct_sz =
+      sizeof(struct __user_cap_header_struct);
+  unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct);
+  unsigned struct_new_utsname_sz = sizeof(struct new_utsname);
+  unsigned struct_old_utsname_sz = sizeof(struct old_utsname);
+  unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+  unsigned struct_rlimit_sz = sizeof(struct rlimit);
+  unsigned struct_timespec_sz = sizeof(struct timespec);
+  unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
+  unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  unsigned struct_ustat_sz = sizeof(struct ustat);
+  unsigned struct_rlimit64_sz = sizeof(struct rlimit64);
+  unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  unsigned struct_timex_sz = sizeof(struct timex);
+  unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
+  unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
+  unsigned struct_statvfs_sz = sizeof(struct statvfs);
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+  uptr sig_ign = (uptr)SIG_IGN;
+  uptr sig_dfl = (uptr)SIG_DFL;
+  uptr sa_siginfo = (uptr)SA_SIGINFO;
+
+#if SANITIZER_LINUX
+  int e_tabsz = (int)E_TABSZ;
+#endif
+
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  unsigned struct_shminfo_sz = sizeof(struct shminfo);
+  unsigned struct_shm_info_sz = sizeof(struct shm_info);
+  int shmctl_ipc_stat = (int)IPC_STAT;
+  int shmctl_ipc_info = (int)IPC_INFO;
+  int shmctl_shm_info = (int)SHM_INFO;
+  int shmctl_shm_stat = (int)SHM_STAT;
+#endif
+
+  int map_fixed = MAP_FIXED;
+
+  int af_inet = (int)AF_INET;
+  int af_inet6 = (int)AF_INET6;
+
+  uptr __sanitizer_in_addr_sz(int af) {
+    if (af == AF_INET)
+      return sizeof(struct in_addr);
+    else if (af == AF_INET6)
+      return sizeof(struct in6_addr);
+    else
+      return 0;
+  }
+
+#if SANITIZER_LINUX
+unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
+#elif SANITIZER_FREEBSD
+unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+#endif
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  int glob_nomatch = GLOB_NOMATCH;
+  int glob_altdirfunc = GLOB_ALTDIRFUNC;
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
+    (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+      defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
+#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
+  unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
+  unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
+#elif defined(__aarch64__)
+  unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs);
+  unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpsimd_state);
+#else
+  unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
+  unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
+#endif // __mips64 || __powerpc64__ || __aarch64__
+#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
+    defined(__aarch64__) || defined(__arm__)
+  unsigned struct_user_fpxregs_struct_sz = 0;
+#else
+  unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
+#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__
+#ifdef __arm__
+  unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE;
+#else
+  unsigned struct_user_vfpregs_struct_sz = 0;
+#endif
+
+  int ptrace_peektext = PTRACE_PEEKTEXT;
+  int ptrace_peekdata = PTRACE_PEEKDATA;
+  int ptrace_peekuser = PTRACE_PEEKUSER;
+#if (defined(PTRACE_GETREGS) && defined(PTRACE_SETREGS)) || \
+    (defined(PT_GETREGS) && defined(PT_SETREGS))
+  int ptrace_getregs = PTRACE_GETREGS;
+  int ptrace_setregs = PTRACE_SETREGS;
+#else
+  int ptrace_getregs = -1;
+  int ptrace_setregs = -1;
+#endif
+#if (defined(PTRACE_GETFPREGS) && defined(PTRACE_SETFPREGS)) || \
+    (defined(PT_GETFPREGS) && defined(PT_SETFPREGS))
+  int ptrace_getfpregs = PTRACE_GETFPREGS;
+  int ptrace_setfpregs = PTRACE_SETFPREGS;
+#else
+  int ptrace_getfpregs = -1;
+  int ptrace_setfpregs = -1;
+#endif
+#if (defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)) || \
+    (defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS))
+  int ptrace_getfpxregs = PTRACE_GETFPXREGS;
+  int ptrace_setfpxregs = PTRACE_SETFPXREGS;
+#else
+  int ptrace_getfpxregs = -1;
+  int ptrace_setfpxregs = -1;
+#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS
+#if defined(PTRACE_GETVFPREGS) && defined(PTRACE_SETVFPREGS)
+  int ptrace_getvfpregs = PTRACE_GETVFPREGS;
+  int ptrace_setvfpregs = PTRACE_SETVFPREGS;
+#else
+  int ptrace_getvfpregs = -1;
+  int ptrace_setvfpregs = -1;
+#endif
+  int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
+#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) ||              \
+    (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
+  int ptrace_getsiginfo = PTRACE_GETSIGINFO;
+  int ptrace_setsiginfo = PTRACE_SETSIGINFO;
+#else
+  int ptrace_getsiginfo = -1;
+  int ptrace_setsiginfo = -1;
+#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO
+#if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET)
+  int ptrace_getregset = PTRACE_GETREGSET;
+  int ptrace_setregset = PTRACE_SETREGSET;
+#else
+  int ptrace_getregset = -1;
+  int ptrace_setregset = -1;
+#endif // PTRACE_GETREGSET/PTRACE_SETREGSET
+#endif
+
+  unsigned path_max = PATH_MAX;
+
+  // ioctl arguments
+  unsigned struct_ifreq_sz = sizeof(struct ifreq);
+  unsigned struct_termios_sz = sizeof(struct termios);
+  unsigned struct_winsize_sz = sizeof(struct winsize);
+
+#if SANITIZER_LINUX
+  unsigned struct_arpreq_sz = sizeof(struct arpreq);
+  unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf);
+  unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession);
+  unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio);
+  unsigned struct_cdrom_subchnl_sz = sizeof(struct cdrom_subchnl);
+  unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti);
+  unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry);
+  unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr);
+  unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl);
+  unsigned struct_ff_effect_sz = sizeof(struct ff_effect);
+  unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params);
+  unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct);
+  unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state);
+  unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors);
+  unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd);
+  unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct);
+  unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors);
+  unsigned struct_format_descr_sz = sizeof(struct format_descr);
+  unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid);
+  unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry);
+  unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo);
+  unsigned struct_input_id_sz = sizeof(struct input_id);
+  unsigned struct_mtpos_sz = sizeof(struct mtpos);
+  unsigned struct_termio_sz = sizeof(struct termio);
+  unsigned struct_vt_consize_sz = sizeof(struct vt_consize);
+  unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes);
+  unsigned struct_vt_stat_sz = sizeof(struct vt_stat);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SOUND_VERSION >= 0x040000
+  unsigned struct_copr_buffer_sz = 0;
+  unsigned struct_copr_debug_buf_sz = 0;
+  unsigned struct_copr_msg_sz = 0;
+#else
+  unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
+  unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
+  unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
+#endif
+  unsigned struct_midi_info_sz = sizeof(struct midi_info);
+  unsigned struct_mtget_sz = sizeof(struct mtget);
+  unsigned struct_mtop_sz = sizeof(struct mtop);
+  unsigned struct_rtentry_sz = sizeof(struct rtentry);
+  unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument);
+  unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
+  unsigned struct_synth_info_sz = sizeof(struct synth_info);
+  unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct);
+  unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor);
+#if EV_VERSION > (0x010000)
+  unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry);
+#else
+  unsigned struct_input_keymap_entry_sz = 0;
+#endif
+  unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data);
+  unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs);
+  unsigned struct_kbentry_sz = sizeof(struct kbentry);
+  unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode);
+  unsigned struct_kbsentry_sz = sizeof(struct kbsentry);
+  unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo);
+  unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct);
+  unsigned struct_scc_modem_sz = sizeof(struct scc_modem);
+  unsigned struct_scc_stat_sz = sizeof(struct scc_stat);
+  unsigned struct_serial_multiport_struct_sz
+      = sizeof(struct serial_multiport_struct);
+  unsigned struct_serial_struct_sz = sizeof(struct serial_struct);
+  unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25);
+  unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc);
+  unsigned struct_unimapinit_sz = sizeof(struct unimapinit);
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
+  unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+#if !SANITIZER_ANDROID && !SANITIZER_MAC
+  unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
+  unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
+#endif
+
+  const unsigned IOCTL_NOT_PRESENT = 0;
+
+  unsigned IOCTL_FIOASYNC = FIOASYNC;
+  unsigned IOCTL_FIOCLEX = FIOCLEX;
+  unsigned IOCTL_FIOGETOWN = FIOGETOWN;
+  unsigned IOCTL_FIONBIO = FIONBIO;
+  unsigned IOCTL_FIONCLEX = FIONCLEX;
+  unsigned IOCTL_FIOSETOWN = FIOSETOWN;
+  unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
+  unsigned IOCTL_SIOCATMARK = SIOCATMARK;
+  unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
+  unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
+  unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
+  unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
+  unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
+  unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
+  unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
+  unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
+  unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
+  unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
+  unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
+  unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
+  unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
+  unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
+  unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
+  unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
+  unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
+  unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
+  unsigned IOCTL_TIOCCONS = TIOCCONS;
+  unsigned IOCTL_TIOCEXCL = TIOCEXCL;
+  unsigned IOCTL_TIOCGETD = TIOCGETD;
+  unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
+  unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
+  unsigned IOCTL_TIOCMBIC = TIOCMBIC;
+  unsigned IOCTL_TIOCMBIS = TIOCMBIS;
+  unsigned IOCTL_TIOCMGET = TIOCMGET;
+  unsigned IOCTL_TIOCMSET = TIOCMSET;
+  unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
+  unsigned IOCTL_TIOCNXCL = TIOCNXCL;
+  unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
+  unsigned IOCTL_TIOCPKT = TIOCPKT;
+  unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
+  unsigned IOCTL_TIOCSETD = TIOCSETD;
+  unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
+  unsigned IOCTL_TIOCSTI = TIOCSTI;
+  unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
+#if ((SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID)
+  unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
+  unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
+#endif
+
+#if SANITIZER_LINUX
+  unsigned IOCTL_EVIOCGABS = EVIOCGABS(0);
+  unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0);
+  unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS;
+  unsigned IOCTL_EVIOCGID = EVIOCGID;
+  unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0);
+  unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE;
+  unsigned IOCTL_EVIOCGLED = EVIOCGLED(0);
+  unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0);
+  unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0);
+  unsigned IOCTL_EVIOCGRAB = EVIOCGRAB;
+  unsigned IOCTL_EVIOCGREP = EVIOCGREP;
+  unsigned IOCTL_EVIOCGSND = EVIOCGSND(0);
+  unsigned IOCTL_EVIOCGSW = EVIOCGSW(0);
+  unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0);
+  unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION;
+  unsigned IOCTL_EVIOCRMFF = EVIOCRMFF;
+  unsigned IOCTL_EVIOCSABS = EVIOCSABS(0);
+  unsigned IOCTL_EVIOCSFF = EVIOCSFF;
+  unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE;
+  unsigned IOCTL_EVIOCSREP = EVIOCSREP;
+  unsigned IOCTL_BLKFLSBUF = BLKFLSBUF;
+  unsigned IOCTL_BLKGETSIZE = BLKGETSIZE;
+  unsigned IOCTL_BLKRAGET = BLKRAGET;
+  unsigned IOCTL_BLKRASET = BLKRASET;
+  unsigned IOCTL_BLKROGET = BLKROGET;
+  unsigned IOCTL_BLKROSET = BLKROSET;
+  unsigned IOCTL_BLKRRPART = BLKRRPART;
+  unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ;
+  unsigned IOCTL_CDROMEJECT = CDROMEJECT;
+  unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW;
+  unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION;
+  unsigned IOCTL_CDROMPAUSE = CDROMPAUSE;
+  unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF;
+  unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND;
+  unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO;
+  unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED;
+  unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1;
+  unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2;
+  unsigned IOCTL_CDROMREADRAW = CDROMREADRAW;
+  unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY;
+  unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR;
+  unsigned IOCTL_CDROMRESET = CDROMRESET;
+  unsigned IOCTL_CDROMRESUME = CDROMRESUME;
+  unsigned IOCTL_CDROMSEEK = CDROMSEEK;
+  unsigned IOCTL_CDROMSTART = CDROMSTART;
+  unsigned IOCTL_CDROMSTOP = CDROMSTOP;
+  unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL;
+  unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL;
+  unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD;
+  unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC;
+  unsigned IOCTL_FDCLRPRM = FDCLRPRM;
+  unsigned IOCTL_FDDEFPRM = FDDEFPRM;
+  unsigned IOCTL_FDFLUSH = FDFLUSH;
+  unsigned IOCTL_FDFMTBEG = FDFMTBEG;
+  unsigned IOCTL_FDFMTEND = FDFMTEND;
+  unsigned IOCTL_FDFMTTRK = FDFMTTRK;
+  unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM;
+  unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT;
+  unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP;
+  unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT;
+  unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS;
+  unsigned IOCTL_FDGETPRM = FDGETPRM;
+  unsigned IOCTL_FDMSGOFF = FDMSGOFF;
+  unsigned IOCTL_FDMSGON = FDMSGON;
+  unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT;
+  unsigned IOCTL_FDRAWCMD = FDRAWCMD;
+  unsigned IOCTL_FDRESET = FDRESET;
+  unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM;
+  unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH;
+  unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS;
+  unsigned IOCTL_FDSETPRM = FDSETPRM;
+  unsigned IOCTL_FDTWADDLE = FDTWADDLE;
+  unsigned IOCTL_FDWERRORCLR = FDWERRORCLR;
+  unsigned IOCTL_FDWERRORGET = FDWERRORGET;
+  unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD;
+  unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO;
+  unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT;
+  unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA;
+  unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY;
+  unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS;
+  unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT;
+  unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR;
+  unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR;
+  unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT;
+  unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA;
+  unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS;
+  unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT;
+  unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR;
+  unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR;
+  unsigned IOCTL_MTIOCPOS = MTIOCPOS;
+  unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP;
+  unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG;
+  unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS;
+  unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT;
+  unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP;
+  unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP;
+  unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG;
+  unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS;
+  unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID;
+  unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU;
+  unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP;
+  unsigned IOCTL_SIOCADDRT = SIOCADDRT;
+  unsigned IOCTL_SIOCDARP = SIOCDARP;
+  unsigned IOCTL_SIOCDELRT = SIOCDELRT;
+  unsigned IOCTL_SIOCDRARP = SIOCDRARP;
+  unsigned IOCTL_SIOCGARP = SIOCGARP;
+  unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP;
+  unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR;
+  unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP;
+  unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM;
+  unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME;
+  unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE;
+  unsigned IOCTL_SIOCGRARP = SIOCGRARP;
+  unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP;
+  unsigned IOCTL_SIOCSARP = SIOCSARP;
+  unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP;
+  unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR;
+  unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK;
+  unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP;
+  unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM;
+  unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE;
+  unsigned IOCTL_SIOCSRARP = SIOCSRARP;
+# if SOUND_VERSION >= 0x040000
+  unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SOUND_PCM_READ_FILTER = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT;
+# else  // SOUND_VERSION
+  unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT;
+  unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD;
+  unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE;
+  unsigned IOCTL_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG;
+  unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA;
+  unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET;
+  unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN;
+  unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG;
+  unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE;
+  unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA;
+  unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS;
+  unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS;
+  unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER;
+  unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE;
+  unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS;
+  unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER;
+#endif // SOUND_VERSION
+  unsigned IOCTL_TCFLSH = TCFLSH;
+  unsigned IOCTL_TCGETA = TCGETA;
+  unsigned IOCTL_TCGETS = TCGETS;
+  unsigned IOCTL_TCSBRK = TCSBRK;
+  unsigned IOCTL_TCSBRKP = TCSBRKP;
+  unsigned IOCTL_TCSETA = TCSETA;
+  unsigned IOCTL_TCSETAF = TCSETAF;
+  unsigned IOCTL_TCSETAW = TCSETAW;
+  unsigned IOCTL_TCSETS = TCSETS;
+  unsigned IOCTL_TCSETSF = TCSETSF;
+  unsigned IOCTL_TCSETSW = TCSETSW;
+  unsigned IOCTL_TCXONC = TCXONC;
+  unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS;
+  unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR;
+  unsigned IOCTL_TIOCINQ = TIOCINQ;
+  unsigned IOCTL_TIOCLINUX = TIOCLINUX;
+  unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG;
+  unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR;
+  unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD;
+  unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD;
+  unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS;
+  unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR;
+  unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE;
+  unsigned IOCTL_VT_GETSTATE = VT_GETSTATE;
+  unsigned IOCTL_VT_RESIZE = VT_RESIZE;
+  unsigned IOCTL_VT_RESIZEX = VT_RESIZEX;
+  unsigned IOCTL_VT_SENDSIG = VT_SENDSIG;
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+  unsigned IOCTL_MTIOCGET = MTIOCGET;
+  unsigned IOCTL_MTIOCTOP = MTIOCTOP;
+  unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
+  unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
+  unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
+  unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
+  unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
+  unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
+  unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
+  unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
+  unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
+  unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
+  unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
+  unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE;
+  unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR;
+  unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO;
+  unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME;
+  unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE;
+  unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT;
+  unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT;
+  unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS;
+  unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS;
+  unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND;
+  unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC;
+  unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE;
+  unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET;
+  unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES;
+  unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC;
+  unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI;
+  unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD;
+  unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO;
+  unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL;
+  unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE;
+  unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME;
+  unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT;
+  unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE;
+  unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START;
+  unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP;
+  unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO;
+  unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE;
+  unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM;
+  unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS;
+  unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS;
+  unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD;
+  unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK;
+  unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE;
+  unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN;
+  unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX;
+  unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE;
+  unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1;
+  unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2;
+  unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3;
+  unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD;
+  unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC;
+  unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE;
+  unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN;
+  unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM;
+  unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV;
+  unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK;
+  unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC;
+  unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER;
+  unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS;
+  unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH;
+  unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE;
+  unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME;
+  unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM;
+  unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS;
+  unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD;
+  unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE;
+  unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN;
+  unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX;
+  unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE;
+  unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1;
+  unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2;
+  unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3;
+  unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD;
+  unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC;
+  unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE;
+  unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN;
+  unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM;
+  unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV;
+  unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC;
+  unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER;
+  unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH;
+  unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE;
+  unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME;
+  unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
+  unsigned IOCTL_VT_GETMODE = VT_GETMODE;
+  unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
+  unsigned IOCTL_VT_RELDISP = VT_RELDISP;
+  unsigned IOCTL_VT_SETMODE = VT_SETMODE;
+  unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+  unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH;
+  unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT;
+  unsigned IOCTL_CYGETMON = CYGETMON;
+  unsigned IOCTL_CYGETTHRESH = CYGETTHRESH;
+  unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT;
+  unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH;
+  unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT;
+  unsigned IOCTL_CYSETTHRESH = CYSETTHRESH;
+  unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT;
+  unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE;
+  unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE;
+  unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG;
+  unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG;
+  unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG;
+  unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG;
+#if EV_VERSION > (0x010000)
+  unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2;
+  unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0);
+  unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2;
+#else
+  unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT;
+  unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT;
+#endif
+  unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS;
+  unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION;
+  unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS;
+  unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION;
+  unsigned IOCTL_GIO_CMAP = GIO_CMAP;
+  unsigned IOCTL_GIO_FONT = GIO_FONT;
+  unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP;
+  unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP;
+  unsigned IOCTL_KDADDIO = KDADDIO;
+  unsigned IOCTL_KDDELIO = KDDELIO;
+  unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE;
+  unsigned IOCTL_KDGKBDIACR = KDGKBDIACR;
+  unsigned IOCTL_KDGKBENT = KDGKBENT;
+  unsigned IOCTL_KDGKBLED = KDGKBLED;
+  unsigned IOCTL_KDGKBMETA = KDGKBMETA;
+  unsigned IOCTL_KDGKBSENT = KDGKBSENT;
+  unsigned IOCTL_KDMAPDISP = KDMAPDISP;
+  unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE;
+  unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT;
+  unsigned IOCTL_KDSKBDIACR = KDSKBDIACR;
+  unsigned IOCTL_KDSKBENT = KDSKBENT;
+  unsigned IOCTL_KDSKBLED = KDSKBLED;
+  unsigned IOCTL_KDSKBMETA = KDSKBMETA;
+  unsigned IOCTL_KDSKBSENT = KDSKBSENT;
+  unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP;
+  unsigned IOCTL_LPABORT = LPABORT;
+  unsigned IOCTL_LPABORTOPEN = LPABORTOPEN;
+  unsigned IOCTL_LPCAREFUL = LPCAREFUL;
+  unsigned IOCTL_LPCHAR = LPCHAR;
+  unsigned IOCTL_LPGETIRQ = LPGETIRQ;
+  unsigned IOCTL_LPGETSTATUS = LPGETSTATUS;
+  unsigned IOCTL_LPRESET = LPRESET;
+  unsigned IOCTL_LPSETIRQ = LPSETIRQ;
+  unsigned IOCTL_LPTIME = LPTIME;
+  unsigned IOCTL_LPWAIT = LPWAIT;
+  unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG;
+  unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG;
+  unsigned IOCTL_PIO_CMAP = PIO_CMAP;
+  unsigned IOCTL_PIO_FONT = PIO_FONT;
+  unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP;
+  unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR;
+  unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP;
+  unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN;
+  unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST;
+  unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE;
+  unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE;
+  unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT;
+  unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT;
+  unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID;
+  unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID;
+  unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS;
+  unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID;
+  unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID;
+  unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS;
+  unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP;
+  unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA;
+  unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS;
+  unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS;
+  unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL;
+  unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS;
+  unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL;
+  unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI;
+  unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI;
+  unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL;
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+  unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
+  unsigned IOCTL_KDDISABIO = KDDISABIO;
+  unsigned IOCTL_KDENABIO = KDENABIO;
+  unsigned IOCTL_KDGETLED = KDGETLED;
+  unsigned IOCTL_KDGETMODE = KDGETMODE;
+  unsigned IOCTL_KDGKBMODE = KDGKBMODE;
+  unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
+  unsigned IOCTL_KDMKTONE = KDMKTONE;
+  unsigned IOCTL_KDSETLED = KDSETLED;
+  unsigned IOCTL_KDSETMODE = KDSETMODE;
+  unsigned IOCTL_KDSKBMODE = KDSKBMODE;
+  unsigned IOCTL_KIOCSOUND = KIOCSOUND;
+  unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
+  unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
+  unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+  const int errno_EINVAL = EINVAL;
+// EOWNERDEAD is not present in some older platforms.
+#if defined(EOWNERDEAD)
+  const int errno_EOWNERDEAD = EOWNERDEAD;
+#else
+  const int errno_EOWNERDEAD = -1;
+#endif
+
+  const int si_SEGV_MAPERR = SEGV_MAPERR;
+  const int si_SEGV_ACCERR = SEGV_ACCERR;
+} // namespace __sanitizer
+
+COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
+
+COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
+CHECK_TYPE_SIZE(pthread_key_t);
+
+#if SANITIZER_LINUX
+// FIXME: We define those on Linux and Mac, but only check on Linux.
+COMPILER_CHECK(IOC_NRBITS == _IOC_NRBITS);
+COMPILER_CHECK(IOC_TYPEBITS == _IOC_TYPEBITS);
+COMPILER_CHECK(IOC_SIZEBITS == _IOC_SIZEBITS);
+COMPILER_CHECK(IOC_DIRBITS == _IOC_DIRBITS);
+COMPILER_CHECK(IOC_NRMASK == _IOC_NRMASK);
+COMPILER_CHECK(IOC_TYPEMASK == _IOC_TYPEMASK);
+COMPILER_CHECK(IOC_SIZEMASK == _IOC_SIZEMASK);
+COMPILER_CHECK(IOC_DIRMASK == _IOC_DIRMASK);
+COMPILER_CHECK(IOC_NRSHIFT == _IOC_NRSHIFT);
+COMPILER_CHECK(IOC_TYPESHIFT == _IOC_TYPESHIFT);
+COMPILER_CHECK(IOC_SIZESHIFT == _IOC_SIZESHIFT);
+COMPILER_CHECK(IOC_DIRSHIFT == _IOC_DIRSHIFT);
+COMPILER_CHECK(IOC_NONE == _IOC_NONE);
+COMPILER_CHECK(IOC_WRITE == _IOC_WRITE);
+COMPILER_CHECK(IOC_READ == _IOC_READ);
+COMPILER_CHECK(EVIOC_ABS_MAX == ABS_MAX);
+COMPILER_CHECK(EVIOC_EV_MAX == EV_MAX);
+COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
+COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678));
+COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678));
+COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678));
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+// There are more undocumented fields in dl_phdr_info that we are not interested
+// in.
+COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(glob_t);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
+#endif
+
+CHECK_TYPE_SIZE(addrinfo);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
+
+CHECK_TYPE_SIZE(hostent);
+CHECK_SIZE_AND_OFFSET(hostent, h_name);
+CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
+CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
+CHECK_SIZE_AND_OFFSET(hostent, h_length);
+CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
+
+CHECK_TYPE_SIZE(iovec);
+CHECK_SIZE_AND_OFFSET(iovec, iov_base);
+CHECK_SIZE_AND_OFFSET(iovec, iov_len);
+
+CHECK_TYPE_SIZE(msghdr);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
+
+CHECK_TYPE_SIZE(cmsghdr);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
+
+COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
+CHECK_SIZE_AND_OFFSET(dirent, d_ino);
+#if SANITIZER_MAC
+CHECK_SIZE_AND_OFFSET(dirent, d_seekoff);
+#elif SANITIZER_FREEBSD
+// There is no 'd_off' field on FreeBSD.
+#else
+CHECK_SIZE_AND_OFFSET(dirent, d_off);
+#endif
+CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64));
+CHECK_SIZE_AND_OFFSET(dirent64, d_ino);
+CHECK_SIZE_AND_OFFSET(dirent64, d_off);
+CHECK_SIZE_AND_OFFSET(dirent64, d_reclen);
+#endif
+
+CHECK_TYPE_SIZE(ifconf);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
+
+CHECK_TYPE_SIZE(pollfd);
+CHECK_SIZE_AND_OFFSET(pollfd, fd);
+CHECK_SIZE_AND_OFFSET(pollfd, events);
+CHECK_SIZE_AND_OFFSET(pollfd, revents);
+
+CHECK_TYPE_SIZE(nfds_t);
+
+CHECK_TYPE_SIZE(sigset_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
+// Can't write checks for sa_handler and sa_sigaction due to them being
+// preprocessor macros.
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags);
+#if SANITIZER_LINUX
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer);
+#endif
+
+#if SANITIZER_LINUX
+CHECK_TYPE_SIZE(__sysctl_args);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, name);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, newval);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen);
+
+CHECK_TYPE_SIZE(__kernel_uid_t);
+CHECK_TYPE_SIZE(__kernel_gid_t);
+#if !defined(__aarch64__)
+CHECK_TYPE_SIZE(__kernel_old_uid_t);
+CHECK_TYPE_SIZE(__kernel_old_gid_t);
+#endif
+CHECK_TYPE_SIZE(__kernel_off_t);
+CHECK_TYPE_SIZE(__kernel_loff_t);
+CHECK_TYPE_SIZE(__kernel_fd_set);
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(wordexp_t);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
+#endif
+
+CHECK_TYPE_SIZE(tm);
+CHECK_SIZE_AND_OFFSET(tm, tm_sec);
+CHECK_SIZE_AND_OFFSET(tm, tm_min);
+CHECK_SIZE_AND_OFFSET(tm, tm_hour);
+CHECK_SIZE_AND_OFFSET(tm, tm_mday);
+CHECK_SIZE_AND_OFFSET(tm, tm_mon);
+CHECK_SIZE_AND_OFFSET(tm, tm_year);
+CHECK_SIZE_AND_OFFSET(tm, tm_wday);
+CHECK_SIZE_AND_OFFSET(tm, tm_yday);
+CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
+CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
+CHECK_SIZE_AND_OFFSET(tm, tm_zone);
+
+#if SANITIZER_LINUX
+CHECK_TYPE_SIZE(mntent);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_dir);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_type);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_opts);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_freq);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_passno);
+#endif
+
+CHECK_TYPE_SIZE(ether_addr);
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(ipc_perm);
+# if SANITIZER_FREEBSD
+CHECK_SIZE_AND_OFFSET(ipc_perm, key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
+# else
+CHECK_SIZE_AND_OFFSET(ipc_perm, __key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, __seq);
+# endif
+CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+#if !defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21)
+/* On aarch64 glibc 2.20 and earlier provided incorrect mode field.  */
+CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
+#endif
+
+CHECK_TYPE_SIZE(shmid_ds);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
+#endif
+
+CHECK_TYPE_SIZE(clock_t);
+
+#if SANITIZER_LINUX
+CHECK_TYPE_SIZE(clockid_t);
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+// Compare against the union, because we can't reach into the union in a
+// compliant way.
+#ifdef ifa_dstaddr
+#undef ifa_dstaddr
+#endif
+# if SANITIZER_FREEBSD
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+# else
+COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
+               sizeof(((ifaddrs *)nullptr)->ifa_ifu));
+COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
+               offsetof(ifaddrs, ifa_ifu));
+# endif // SANITIZER_FREEBSD
+#else
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+#endif // SANITIZER_LINUX
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+#endif
+
+#if SANITIZER_LINUX
+COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo));
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(timeb);
+CHECK_SIZE_AND_OFFSET(timeb, time);
+CHECK_SIZE_AND_OFFSET(timeb, millitm);
+CHECK_SIZE_AND_OFFSET(timeb, timezone);
+CHECK_SIZE_AND_OFFSET(timeb, dstflag);
+#endif
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+#if !SANITIZER_ANDROID
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+#endif
+
+#if SANITIZER_MAC
+CHECK_SIZE_AND_OFFSET(passwd, pw_change);
+CHECK_SIZE_AND_OFFSET(passwd, pw_expire);
+CHECK_SIZE_AND_OFFSET(passwd, pw_class);
+#endif
+
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+#if HAVE_RPC_XDR_H || HAVE_TIRPC_RPC_XDR_H
+CHECK_TYPE_SIZE(XDR);
+CHECK_SIZE_AND_OFFSET(XDR, x_op);
+CHECK_SIZE_AND_OFFSET(XDR, x_ops);
+CHECK_SIZE_AND_OFFSET(XDR, x_public);
+CHECK_SIZE_AND_OFFSET(XDR, x_private);
+CHECK_SIZE_AND_OFFSET(XDR, x_base);
+CHECK_SIZE_AND_OFFSET(XDR, x_handy);
+COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
+COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
+COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE));
+CHECK_SIZE_AND_OFFSET(FILE, _flags);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_ptr);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_ptr);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_save_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_backup_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_save_end);
+CHECK_SIZE_AND_OFFSET(FILE, _markers);
+CHECK_SIZE_AND_OFFSET(FILE, _chain);
+CHECK_SIZE_AND_OFFSET(FILE, _fileno);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer__obstack_chunk) <= sizeof(_obstack_chunk));
+CHECK_SIZE_AND_OFFSET(_obstack_chunk, limit);
+CHECK_SIZE_AND_OFFSET(_obstack_chunk, prev);
+CHECK_TYPE_SIZE(obstack);
+CHECK_SIZE_AND_OFFSET(obstack, chunk_size);
+CHECK_SIZE_AND_OFFSET(obstack, chunk);
+CHECK_SIZE_AND_OFFSET(obstack, object_base);
+CHECK_SIZE_AND_OFFSET(obstack, next_free);
+
+CHECK_TYPE_SIZE(cookie_io_functions_t);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close);
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+CHECK_TYPE_SIZE(sem_t);
+#endif
+
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
diff --git a/lsan/src/sanitizer_posix.cc b/lsan/src/sanitizer_posix.cc
new file mode 100644 (file)
index 0000000..ed44633
--- /dev/null
@@ -0,0 +1,340 @@
+//===-- sanitizer_posix.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements POSIX-specific functions from
+// sanitizer_posix.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_POSIX
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/mman.h>
+
+#if SANITIZER_LINUX
+#include <sys/utsname.h>
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#include <sys/personality.h>
+#endif
+
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented.  So just define it to zero.
+#undef  MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
+namespace __sanitizer {
+
+// ------------- sanitizer_common.h
+uptr GetMmapGranularity() {
+  return GetPageSize();
+}
+
+#if SANITIZER_WORDSIZE == 32
+// Take care of unusable kernel area in top gigabyte.
+static uptr GetKernelAreaSize() {
+#if SANITIZER_LINUX && !SANITIZER_X32
+  const uptr gbyte = 1UL << 30;
+
+  // Firstly check if there are writable segments
+  // mapped to top gigabyte (e.g. stack).
+  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+  uptr end, prot;
+  while (proc_maps.Next(/*start*/nullptr, &end,
+                        /*offset*/nullptr, /*filename*/nullptr,
+                        /*filename_size*/0, &prot)) {
+    if ((end >= 3 * gbyte)
+        && (prot & MemoryMappingLayout::kProtectionWrite) != 0)
+      return 0;
+  }
+
+#if !SANITIZER_ANDROID
+  // Even if nothing is mapped, top Gb may still be accessible
+  // if we are running on 64-bit kernel.
+  // Uname may report misleading results if personality type
+  // is modified (e.g. under schroot) so check this as well.
+  struct utsname uname_info;
+  int pers = personality(0xffffffffUL);
+  if (!(pers & PER_MASK)
+      && uname(&uname_info) == 0
+      && internal_strstr(uname_info.machine, "64"))
+    return 0;
+#endif  // SANITIZER_ANDROID
+
+  // Top gigabyte is reserved for kernel.
+  return gbyte;
+#else
+  return 0;
+#endif  // SANITIZER_LINUX && !SANITIZER_X32
+}
+#endif  // SANITIZER_WORDSIZE == 32
+
+uptr GetMaxVirtualAddress() {
+#if SANITIZER_WORDSIZE == 64
+# if defined(__powerpc64__) || defined(__aarch64__)
+  // On PowerPC64 we have two different address space layouts: 44- and 46-bit.
+  // We somehow need to figure out which one we are using now and choose
+  // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
+  // Note that with 'ulimit -s unlimited' the stack is moved away from the top
+  // of the address space, so simply checking the stack address is not enough.
+  // This should (does) work for both PowerPC64 Endian modes.
+  // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit.
+  return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
+# elif defined(__mips64)
+  return (1ULL << 40) - 1;  // 0x000000ffffffffffUL;
+# else
+  return (1ULL << 47) - 1;  // 0x00007fffffffffffUL;
+# endif
+#else  // SANITIZER_WORDSIZE == 32
+  uptr res = (1ULL << 32) - 1;  // 0xffffffff;
+  if (!common_flags()->full_address_space)
+    res -= GetKernelAreaSize();
+  CHECK_LT(reinterpret_cast<uptr>(&res), res);
+  return res;
+#endif  // SANITIZER_WORDSIZE
+}
+
+void *MmapOrDie(uptr size, const char *mem_type) {
+  size = RoundUpTo(size, GetPageSizeCached());
+  uptr res = internal_mmap(nullptr, size,
+                           PROT_READ | PROT_WRITE,
+                           MAP_PRIVATE | MAP_ANON, -1, 0);
+  int reserrno;
+  if (internal_iserror(res, &reserrno))
+    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
+  IncreaseTotalMmap(size);
+  return (void *)res;
+}
+
+void UnmapOrDie(void *addr, uptr size) {
+  if (!addr || !size) return;
+  uptr res = internal_munmap(addr, size);
+  if (internal_iserror(res)) {
+    Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
+           SanitizerToolName, size, size, addr);
+    CHECK("unable to unmap" && 0);
+  }
+  DecreaseTotalMmap(size);
+}
+
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
+  uptr PageSize = GetPageSizeCached();
+  uptr p = internal_mmap(nullptr,
+                         RoundUpTo(size, PageSize),
+                         PROT_READ | PROT_WRITE,
+                         MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+                         -1, 0);
+  int reserrno;
+  if (internal_iserror(p, &reserrno))
+    ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno);
+  IncreaseTotalMmap(size);
+  return (void *)p;
+}
+
+void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
+  uptr PageSize = GetPageSizeCached();
+  uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
+      RoundUpTo(size, PageSize),
+      PROT_READ | PROT_WRITE,
+      MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+      -1, 0);
+  int reserrno;
+  if (internal_iserror(p, &reserrno)) {
+    char mem_type[30];
+    internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
+                      fixed_addr);
+    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
+  }
+  IncreaseTotalMmap(size);
+  return (void *)p;
+}
+
+bool MprotectNoAccess(uptr addr, uptr size) {
+  return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
+}
+
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+  int flags;
+  switch (mode) {
+    case RdOnly: flags = O_RDONLY; break;
+    case WrOnly: flags = O_WRONLY | O_CREAT; break;
+    case RdWr: flags = O_RDWR | O_CREAT; break;
+  }
+  fd_t res = internal_open(filename, flags, 0660);
+  if (internal_iserror(res, errno_p))
+    return kInvalidFd;
+  return res;
+}
+
+void CloseFile(fd_t fd) {
+  internal_close(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+                  error_t *error_p) {
+  uptr res = internal_read(fd, buff, buff_size);
+  if (internal_iserror(res, error_p))
+    return false;
+  if (bytes_read)
+    *bytes_read = res;
+  return true;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+                 error_t *error_p) {
+  uptr res = internal_write(fd, buff, buff_size);
+  if (internal_iserror(res, error_p))
+    return false;
+  if (bytes_written)
+    *bytes_written = res;
+  return true;
+}
+
+bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
+  uptr res = internal_rename(oldpath, newpath);
+  return !internal_iserror(res, error_p);
+}
+
+void *MapFileToMemory(const char *file_name, uptr *buff_size) {
+  fd_t fd = OpenFile(file_name, RdOnly);
+  CHECK(fd != kInvalidFd);
+  uptr fsize = internal_filesize(fd);
+  CHECK_NE(fsize, (uptr)-1);
+  CHECK_GT(fsize, 0);
+  *buff_size = RoundUpTo(fsize, GetPageSizeCached());
+  uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
+  return internal_iserror(map) ? nullptr : (void *)map;
+}
+
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
+  uptr flags = MAP_SHARED;
+  if (addr) flags |= MAP_FIXED;
+  uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
+  int mmap_errno = 0;
+  if (internal_iserror(p, &mmap_errno)) {
+    Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
+           fd, (long long)offset, size, p, mmap_errno);
+    return nullptr;
+  }
+  return (void *)p;
+}
+
+static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
+                                        uptr start2, uptr end2) {
+  CHECK(start1 <= end1);
+  CHECK(start2 <= end2);
+  return (end1 < start2) || (end2 < start1);
+}
+
+// FIXME: this is thread-unsafe, but should not cause problems most of the time.
+// When the shadow is mapped only a single thread usually exists (plus maybe
+// several worker threads on Mac, which aren't expected to map big chunks of
+// memory).
+bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
+  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+  uptr start, end;
+  while (proc_maps.Next(&start, &end,
+                        /*offset*/nullptr, /*filename*/nullptr,
+                        /*filename_size*/0, /*protection*/nullptr)) {
+    if (start == end) continue;  // Empty range.
+    CHECK_NE(0, end);
+    if (!IntervalsAreSeparate(start, end - 1, range_start, range_end))
+      return false;
+  }
+  return true;
+}
+
+void DumpProcessMap() {
+  MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+  uptr start, end;
+  const sptr kBufSize = 4095;
+  char *filename = (char*)MmapOrDie(kBufSize, __func__);
+  Report("Process memory map follows:\n");
+  while (proc_maps.Next(&start, &end, /* file_offset */nullptr,
+                        filename, kBufSize, /* protection */nullptr)) {
+    Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename);
+  }
+  Report("End of process memory map.\n");
+  UnmapOrDie(filename, kBufSize);
+}
+
+const char *GetPwd() {
+  return GetEnv("PWD");
+}
+
+bool IsPathSeparator(const char c) {
+  return c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+  return path != nullptr && IsPathSeparator(path[0]);
+}
+
+void ReportFile::Write(const char *buffer, uptr length) {
+  SpinMutexLock l(mu);
+  static const char *kWriteError =
+      "ReportFile::Write() can't output requested buffer!\n";
+  ReopenIfNecessary();
+  if (length != internal_write(fd, buffer, length)) {
+    internal_write(fd, kWriteError, internal_strlen(kWriteError));
+    Die();
+  }
+}
+
+bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
+  uptr s, e, off, prot;
+  InternalScopedString buff(kMaxPathLength);
+  MemoryMappingLayout proc_maps(/*cache_enabled*/false);
+  while (proc_maps.Next(&s, &e, &off, buff.data(), buff.size(), &prot)) {
+    if ((prot & MemoryMappingLayout::kProtectionExecute) != 0
+        && internal_strcmp(module, buff.data()) == 0) {
+      *start = s;
+      *end = e;
+      return true;
+    }
+  }
+  return false;
+}
+
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+  uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
+  uptr pc, sp, bp;
+  GetPcSpBp(context, &pc, &sp, &bp);
+  return SignalContext(context, addr, pc, sp, bp);
+}
+
+// This function check is the built VMA matches the runtime one for
+// architectures with multiple VMA size.
+void CheckVMASize() {
+#ifdef __aarch64__
+  static const unsigned kBuiltVMA = SANITIZER_AARCH64_VMA;
+  unsigned maxRuntimeVMA =
+    (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+  if (kBuiltVMA != maxRuntimeVMA) {
+    Printf("WARNING: %s runtime VMA is not the one built for.\n",
+      SanitizerToolName);
+    Printf("\tBuilt VMA:   %u bits\n", kBuiltVMA);
+    Printf("\tRuntime VMA: %u bits\n", maxRuntimeVMA);
+  }
+#endif
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
diff --git a/lsan/src/sanitizer_posix_libcdep.cc b/lsan/src/sanitizer_posix_libcdep.cc
new file mode 100644 (file)
index 0000000..4b7273b
--- /dev/null
@@ -0,0 +1,322 @@
+//===-- sanitizer_posix_libcdep.cc ----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements libc-dependent POSIX-specific functions
+// from sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_POSIX
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented.  So just define it to zero.
+#undef  MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
+namespace __sanitizer {
+
+u32 GetUid() {
+  return getuid();
+}
+
+uptr GetThreadSelf() {
+  return (uptr)pthread_self();
+}
+
+void FlushUnneededShadowMemory(uptr addr, uptr size) {
+  madvise((void*)addr, size, MADV_DONTNEED);
+}
+
+void NoHugePagesInRegion(uptr addr, uptr size) {
+#ifdef MADV_NOHUGEPAGE  // May not be defined on old systems.
+  madvise((void *)addr, size, MADV_NOHUGEPAGE);
+#endif  // MADV_NOHUGEPAGE
+}
+
+void DontDumpShadowMemory(uptr addr, uptr length) {
+#ifdef MADV_DONTDUMP
+  madvise((void *)addr, length, MADV_DONTDUMP);
+#endif
+}
+
+static rlim_t getlim(int res) {
+  rlimit rlim;
+  CHECK_EQ(0, getrlimit(res, &rlim));
+  return rlim.rlim_cur;
+}
+
+static void setlim(int res, rlim_t lim) {
+  // The following magic is to prevent clang from replacing it with memset.
+  volatile struct rlimit rlim;
+  rlim.rlim_cur = lim;
+  rlim.rlim_max = lim;
+  if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
+    Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
+    Die();
+  }
+}
+
+void DisableCoreDumperIfNecessary() {
+  if (common_flags()->disable_coredump) {
+    setlim(RLIMIT_CORE, 0);
+  }
+}
+
+bool StackSizeIsUnlimited() {
+  rlim_t stack_size = getlim(RLIMIT_STACK);
+  return (stack_size == RLIM_INFINITY);
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+  setlim(RLIMIT_STACK, (rlim_t)limit);
+  CHECK(!StackSizeIsUnlimited());
+}
+
+bool AddressSpaceIsUnlimited() {
+  rlim_t as_size = getlim(RLIMIT_AS);
+  return (as_size == RLIM_INFINITY);
+}
+
+void SetAddressSpaceUnlimited() {
+  setlim(RLIMIT_AS, RLIM_INFINITY);
+  CHECK(AddressSpaceIsUnlimited());
+}
+
+void SleepForSeconds(int seconds) {
+  sleep(seconds);
+}
+
+void SleepForMillis(int millis) {
+  usleep(millis * 1000);
+}
+
+void Abort() {
+  abort();
+}
+
+int Atexit(void (*function)(void)) {
+#ifndef SANITIZER_GO
+  return atexit(function);
+#else
+  return 0;
+#endif
+}
+
+bool SupportsColoredOutput(fd_t fd) {
+  return isatty(fd) != 0;
+}
+
+#ifndef SANITIZER_GO
+// TODO(glider): different tools may require different altstack size.
+static const uptr kAltStackSize = SIGSTKSZ * 4;  // SIGSTKSZ is not enough.
+
+void SetAlternateSignalStack() {
+  stack_t altstack, oldstack;
+  CHECK_EQ(0, sigaltstack(nullptr, &oldstack));
+  // If the alternate stack is already in place, do nothing.
+  // Android always sets an alternate stack, but it's too small for us.
+  if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return;
+  // TODO(glider): the mapped stack should have the MAP_STACK flag in the
+  // future. It is not required by man 2 sigaltstack now (they're using
+  // malloc()).
+  void* base = MmapOrDie(kAltStackSize, __func__);
+  altstack.ss_sp = (char*) base;
+  altstack.ss_flags = 0;
+  altstack.ss_size = kAltStackSize;
+  CHECK_EQ(0, sigaltstack(&altstack, nullptr));
+}
+
+void UnsetAlternateSignalStack() {
+  stack_t altstack, oldstack;
+  altstack.ss_sp = nullptr;
+  altstack.ss_flags = SS_DISABLE;
+  altstack.ss_size = kAltStackSize;  // Some sane value required on Darwin.
+  CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
+  UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
+}
+
+typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
+static void MaybeInstallSigaction(int signum,
+                                  SignalHandlerType handler) {
+  if (!IsDeadlySignal(signum))
+    return;
+  struct sigaction sigact;
+  internal_memset(&sigact, 0, sizeof(sigact));
+  sigact.sa_sigaction = (sa_sigaction_t)handler;
+  // Do not block the signal from being received in that signal's handler.
+  // Clients are responsible for handling this correctly.
+  sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
+  if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
+  CHECK_EQ(0, internal_sigaction(signum, &sigact, nullptr));
+  VReport(1, "Installed the sigaction for signal %d\n", signum);
+}
+
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {
+  // Set the alternate signal stack for the main thread.
+  // This will cause SetAlternateSignalStack to be called twice, but the stack
+  // will be actually set only once.
+  if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
+  MaybeInstallSigaction(SIGSEGV, handler);
+  MaybeInstallSigaction(SIGBUS, handler);
+  MaybeInstallSigaction(SIGABRT, handler);
+  MaybeInstallSigaction(SIGFPE, handler);
+}
+#endif  // SANITIZER_GO
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+  uptr page_size = GetPageSizeCached();
+  // Checking too large memory ranges is slow.
+  CHECK_LT(size, page_size * 10);
+  int sock_pair[2];
+  if (pipe(sock_pair))
+    return false;
+  uptr bytes_written =
+      internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
+  int write_errno;
+  bool result;
+  if (internal_iserror(bytes_written, &write_errno)) {
+    CHECK_EQ(EFAULT, write_errno);
+    result = false;
+  } else {
+    result = (bytes_written == size);
+  }
+  internal_close(sock_pair[0]);
+  internal_close(sock_pair[1]);
+  return result;
+}
+
+void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+  // Some kinds of sandboxes may forbid filesystem access, so we won't be able
+  // to read the file mappings from /proc/self/maps. Luckily, neither the
+  // process will be able to load additional libraries, so it's fine to use the
+  // cached mappings.
+  MemoryMappingLayout::CacheMemoryMappings();
+  // Same for /proc/self/exe in the symbolizer.
+#if !SANITIZER_GO
+  Symbolizer::GetOrInit()->PrepareForSandboxing();
+  CovPrepareForSandboxing(args);
+#endif
+}
+
+#if SANITIZER_ANDROID || SANITIZER_GO
+int GetNamedMappingFd(const char *name, uptr size) {
+  return -1;
+}
+#else
+int GetNamedMappingFd(const char *name, uptr size) {
+  if (!common_flags()->decorate_proc_maps)
+    return -1;
+  char shmname[200];
+  CHECK(internal_strlen(name) < sizeof(shmname) - 10);
+  internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(),
+                    name);
+  int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
+  CHECK_GE(fd, 0);
+  int res = internal_ftruncate(fd, size);
+  CHECK_EQ(0, res);
+  res = shm_unlink(shmname);
+  CHECK_EQ(0, res);
+  return fd;
+}
+#endif
+
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
+  int fd = name ? GetNamedMappingFd(name, size) : -1;
+  unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+  if (fd == -1) flags |= MAP_ANON;
+
+  uptr PageSize = GetPageSizeCached();
+  uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)),
+                         RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE,
+                         flags, fd, 0);
+  int reserrno;
+  if (internal_iserror(p, &reserrno))
+    Report("ERROR: %s failed to "
+           "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
+           SanitizerToolName, size, size, fixed_addr, reserrno);
+  IncreaseTotalMmap(size);
+  return (void *)p;
+}
+
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+  int fd = name ? GetNamedMappingFd(name, size) : -1;
+  unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+  if (fd == -1) flags |= MAP_ANON;
+
+  return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd,
+                               0);
+}
+
+// This function is defined elsewhere if we intercepted pthread_attr_getstack.
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE int
+real_pthread_attr_getstack(void *attr, void **addr, size_t *size);
+} // extern "C"
+
+int my_pthread_attr_getstack(void *attr, void **addr, uptr *size) {
+#if !SANITIZER_GO && !SANITIZER_MAC
+  if (&real_pthread_attr_getstack)
+    return real_pthread_attr_getstack((pthread_attr_t *)attr, addr,
+                                      (size_t *)size);
+#endif
+  return pthread_attr_getstack((pthread_attr_t *)attr, addr, (size_t *)size);
+}
+
+#if !SANITIZER_GO
+void AdjustStackSize(void *attr_) {
+  pthread_attr_t *attr = (pthread_attr_t *)attr_;
+  uptr stackaddr = 0;
+  uptr stacksize = 0;
+  my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);
+  // GLibC will return (0 - stacksize) as the stack address in the case when
+  // stacksize is set, but stackaddr is not.
+  bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);
+  // We place a lot of tool data into TLS, account for that.
+  const uptr minstacksize = GetTlsSize() + 128*1024;
+  if (stacksize < minstacksize) {
+    if (!stack_set) {
+      if (stacksize != 0) {
+        VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize,
+                minstacksize);
+        pthread_attr_setstacksize(attr, minstacksize);
+      }
+    } else {
+      Printf("Sanitizer: pre-allocated stack size is insufficient: "
+             "%zu < %zu\n", stacksize, minstacksize);
+      Printf("Sanitizer: pthread_create is likely to fail.\n");
+    }
+  }
+}
+#endif // !SANITIZER_GO
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
diff --git a/lsan/src/sanitizer_printf.cc b/lsan/src/sanitizer_printf.cc
new file mode 100644 (file)
index 0000000..6688610
--- /dev/null
@@ -0,0 +1,329 @@
+//===-- sanitizer_printf.cc -----------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer.
+//
+// Internal printf function, used inside run-time libraries.
+// We can't use libc printf because we intercept some of the functions used
+// inside it.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 &&               \
+      !defined(va_copy)
+# define va_copy(dst, src) ((dst) = (src))
+#endif
+
+namespace __sanitizer {
+
+StaticSpinMutex CommonSanitizerReportMutex;
+
+static int AppendChar(char **buff, const char *buff_end, char c) {
+  if (*buff < buff_end) {
+    **buff = c;
+    (*buff)++;
+  }
+  return 1;
+}
+
+// Appends number in a given base to buffer. If its length is less than
+// |minimal_num_length|, it is padded with leading zeroes or spaces, depending
+// on the value of |pad_with_zero|.
+static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
+                        u8 base, u8 minimal_num_length, bool pad_with_zero,
+                        bool negative) {
+  uptr const kMaxLen = 30;
+  RAW_CHECK(base == 10 || base == 16);
+  RAW_CHECK(base == 10 || !negative);
+  RAW_CHECK(absolute_value || !negative);
+  RAW_CHECK(minimal_num_length < kMaxLen);
+  int result = 0;
+  if (negative && minimal_num_length)
+    --minimal_num_length;
+  if (negative && pad_with_zero)
+    result += AppendChar(buff, buff_end, '-');
+  uptr num_buffer[kMaxLen];
+  int pos = 0;
+  do {
+    RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");
+    num_buffer[pos++] = absolute_value % base;
+    absolute_value /= base;
+  } while (absolute_value > 0);
+  if (pos < minimal_num_length) {
+    // Make sure compiler doesn't insert call to memset here.
+    internal_memset(&num_buffer[pos], 0,
+                    sizeof(num_buffer[0]) * (minimal_num_length - pos));
+    pos = minimal_num_length;
+  }
+  RAW_CHECK(pos > 0);
+  pos--;
+  for (; pos >= 0 && num_buffer[pos] == 0; pos--) {
+    char c = (pad_with_zero || pos == 0) ? '0' : ' ';
+    result += AppendChar(buff, buff_end, c);
+  }
+  if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');
+  for (; pos >= 0; pos--) {
+    char digit = static_cast<char>(num_buffer[pos]);
+    result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
+                                                      : 'a' + digit - 10);
+  }
+  return result;
+}
+
+static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,
+                          u8 minimal_num_length, bool pad_with_zero) {
+  return AppendNumber(buff, buff_end, num, base, minimal_num_length,
+                      pad_with_zero, false /* negative */);
+}
+
+static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
+                               u8 minimal_num_length, bool pad_with_zero) {
+  bool negative = (num < 0);
+  return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,
+                      minimal_num_length, pad_with_zero, negative);
+}
+
+static int AppendString(char **buff, const char *buff_end, int precision,
+                        const char *s) {
+  if (!s)
+    s = "<null>";
+  int result = 0;
+  for (; *s; s++) {
+    if (precision >= 0 && result >= precision)
+      break;
+    result += AppendChar(buff, buff_end, *s);
+  }
+  return result;
+}
+
+static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
+  int result = 0;
+  result += AppendString(buff, buff_end, -1, "0x");
+  result += AppendUnsigned(buff, buff_end, ptr_value, 16,
+                           SANITIZER_POINTER_FORMAT_LENGTH, true);
+  return result;
+}
+
+int VSNPrintf(char *buff, int buff_length,
+              const char *format, va_list args) {
+  static const char *kPrintfFormatsHelp =
+    "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n";
+  RAW_CHECK(format);
+  RAW_CHECK(buff_length > 0);
+  const char *buff_end = &buff[buff_length - 1];
+  const char *cur = format;
+  int result = 0;
+  for (; *cur; cur++) {
+    if (*cur != '%') {
+      result += AppendChar(&buff, buff_end, *cur);
+      continue;
+    }
+    cur++;
+    bool have_width = (*cur >= '0' && *cur <= '9');
+    bool pad_with_zero = (*cur == '0');
+    int width = 0;
+    if (have_width) {
+      while (*cur >= '0' && *cur <= '9') {
+        width = width * 10 + *cur++ - '0';
+      }
+    }
+    bool have_precision = (cur[0] == '.' && cur[1] == '*');
+    int precision = -1;
+    if (have_precision) {
+      cur += 2;
+      precision = va_arg(args, int);
+    }
+    bool have_z = (*cur == 'z');
+    cur += have_z;
+    bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
+    cur += have_ll * 2;
+    s64 dval;
+    u64 uval;
+    bool have_flags = have_width | have_z | have_ll;
+    // Only %s supports precision for now
+    CHECK(!(precision >= 0 && *cur != 's'));
+    switch (*cur) {
+      case 'd': {
+        dval = have_ll ? va_arg(args, s64)
+             : have_z ? va_arg(args, sptr)
+             : va_arg(args, int);
+        result += AppendSignedDecimal(&buff, buff_end, dval, width,
+                                      pad_with_zero);
+        break;
+      }
+      case 'u':
+      case 'x': {
+        uval = have_ll ? va_arg(args, u64)
+             : have_z ? va_arg(args, uptr)
+             : va_arg(args, unsigned);
+        result += AppendUnsigned(&buff, buff_end, uval,
+                                 (*cur == 'u') ? 10 : 16, width, pad_with_zero);
+        break;
+      }
+      case 'p': {
+        RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+        result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
+        break;
+      }
+      case 's': {
+        RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+        result += AppendString(&buff, buff_end, precision, va_arg(args, char*));
+        break;
+      }
+      case 'c': {
+        RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+        result += AppendChar(&buff, buff_end, va_arg(args, int));
+        break;
+      }
+      case '%' : {
+        RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+        result += AppendChar(&buff, buff_end, '%');
+        break;
+      }
+      default: {
+        RAW_CHECK_MSG(false, kPrintfFormatsHelp);
+      }
+    }
+  }
+  RAW_CHECK(buff <= buff_end);
+  AppendChar(&buff, buff_end + 1, '\0');
+  return result;
+}
+
+static void (*PrintfAndReportCallback)(const char *);
+void SetPrintfAndReportCallback(void (*callback)(const char *)) {
+  PrintfAndReportCallback = callback;
+}
+
+// Can be overriden in frontend.
+#if SANITIZER_SUPPORTS_WEAK_HOOKS
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void OnPrint(const char *str) {
+  (void)str;
+}
+#elif defined(SANITIZER_GO) && defined(TSAN_EXTERNAL_HOOKS)
+void OnPrint(const char *str);
+#else
+void OnPrint(const char *str) {
+  (void)str;
+}
+#endif
+
+static void CallPrintfAndReportCallback(const char *str) {
+  OnPrint(str);
+  if (PrintfAndReportCallback)
+    PrintfAndReportCallback(str);
+}
+
+static void SharedPrintfCode(bool append_pid, const char *format,
+                             va_list args) {
+  va_list args2;
+  va_copy(args2, args);
+  const int kLen = 16 * 1024;
+  // |local_buffer| is small enough not to overflow the stack and/or violate
+  // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other
+  // hand, the bigger the buffer is, the more the chance the error report will
+  // fit into it.
+  char local_buffer[400];
+  int needed_length;
+  char *buffer = local_buffer;
+  int buffer_size = ARRAY_SIZE(local_buffer);
+  // First try to print a message using a local buffer, and then fall back to
+  // mmaped buffer.
+  for (int use_mmap = 0; use_mmap < 2; use_mmap++) {
+    if (use_mmap) {
+      va_end(args);
+      va_copy(args, args2);
+      buffer = (char*)MmapOrDie(kLen, "Report");
+      buffer_size = kLen;
+    }
+    needed_length = 0;
+    // Check that data fits into the current buffer.
+#   define CHECK_NEEDED_LENGTH \
+      if (needed_length >= buffer_size) { \
+        if (!use_mmap) continue; \
+        RAW_CHECK_MSG(needed_length < kLen, \
+                      "Buffer in Report is too short!\n"); \
+      }
+    if (append_pid) {
+      int pid = internal_getpid();
+      const char *exe_name = GetProcessName();
+      if (common_flags()->log_exe_name && exe_name) {
+        needed_length += internal_snprintf(buffer, buffer_size,
+                                           "==%s", exe_name);
+        CHECK_NEEDED_LENGTH
+      }
+      needed_length += internal_snprintf(buffer + needed_length,
+                                         buffer_size - needed_length,
+                                         "==%d==", pid);
+      CHECK_NEEDED_LENGTH
+    }
+    needed_length += VSNPrintf(buffer + needed_length,
+                               buffer_size - needed_length, format, args);
+    CHECK_NEEDED_LENGTH
+    // If the message fit into the buffer, print it and exit.
+    break;
+#   undef CHECK_NEEDED_LENGTH
+  }
+  RawWrite(buffer);
+  if (common_flags()->log_to_syslog)
+    WriteToSyslog(buffer);
+  CallPrintfAndReportCallback(buffer);
+  // If we had mapped any memory, clean up.
+  if (buffer != local_buffer)
+    UnmapOrDie((void *)buffer, buffer_size);
+  va_end(args2);
+}
+
+FORMAT(1, 2)
+void Printf(const char *format, ...) {
+  va_list args;
+  va_start(args, format);
+  SharedPrintfCode(false, format, args);
+  va_end(args);
+}
+
+// Like Printf, but prints the current PID before the output string.
+FORMAT(1, 2)
+void Report(const char *format, ...) {
+  va_list args;
+  va_start(args, format);
+  SharedPrintfCode(true, format, args);
+  va_end(args);
+}
+
+// Writes at most "length" symbols to "buffer" (including trailing '\0').
+// Returns the number of symbols that should have been written to buffer
+// (not including trailing '\0'). Thus, the string is truncated
+// iff return value is not less than "length".
+FORMAT(3, 4)
+int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
+  va_list args;
+  va_start(args, format);
+  int needed_length = VSNPrintf(buffer, length, format, args);
+  va_end(args);
+  return needed_length;
+}
+
+FORMAT(2, 3)
+void InternalScopedString::append(const char *format, ...) {
+  CHECK_LT(length_, size());
+  va_list args;
+  va_start(args, format);
+  VSNPrintf(data() + length_, size() - length_, format, args);
+  va_end(args);
+  length_ += internal_strlen(data() + length_);
+  CHECK_LT(length_, size());
+}
+
+} // namespace __sanitizer
diff --git a/lsan/src/sanitizer_procmaps_common.cc b/lsan/src/sanitizer_procmaps_common.cc
new file mode 100644 (file)
index 0000000..1cf3f85
--- /dev/null
@@ -0,0 +1,179 @@
+//===-- sanitizer_procmaps_common.cc --------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (common parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+// Linker initialized.
+ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
+StaticSpinMutex MemoryMappingLayout::cache_lock_;  // Linker initialized.
+
+static int TranslateDigit(char c) {
+  if (c >= '0' && c <= '9')
+    return c - '0';
+  if (c >= 'a' && c <= 'f')
+    return c - 'a' + 10;
+  if (c >= 'A' && c <= 'F')
+    return c - 'A' + 10;
+  return -1;
+}
+
+// Parse a number and promote 'p' up to the first non-digit character.
+static uptr ParseNumber(const char **p, int base) {
+  uptr n = 0;
+  int d;
+  CHECK(base >= 2 && base <= 16);
+  while ((d = TranslateDigit(**p)) >= 0 && d < base) {
+    n = n * base + d;
+    (*p)++;
+  }
+  return n;
+}
+
+bool IsDecimal(char c) {
+  int d = TranslateDigit(c);
+  return d >= 0 && d < 10;
+}
+
+uptr ParseDecimal(const char **p) {
+  return ParseNumber(p, 10);
+}
+
+bool IsHex(char c) {
+  int d = TranslateDigit(c);
+  return d >= 0 && d < 16;
+}
+
+uptr ParseHex(const char **p) {
+  return ParseNumber(p, 16);
+}
+
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
+  ReadProcMaps(&proc_self_maps_);
+  if (cache_enabled) {
+    if (proc_self_maps_.mmaped_size == 0) {
+      LoadFromCache();
+      CHECK_GT(proc_self_maps_.len, 0);
+    }
+  } else {
+    CHECK_GT(proc_self_maps_.mmaped_size, 0);
+  }
+  Reset();
+  // FIXME: in the future we may want to cache the mappings on demand only.
+  if (cache_enabled)
+    CacheMemoryMappings();
+}
+
+MemoryMappingLayout::~MemoryMappingLayout() {
+  // Only unmap the buffer if it is different from the cached one. Otherwise
+  // it will be unmapped when the cache is refreshed.
+  if (proc_self_maps_.data != cached_proc_self_maps_.data) {
+    UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
+  }
+}
+
+void MemoryMappingLayout::Reset() {
+  current_ = proc_self_maps_.data;
+}
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+  SpinMutexLock l(&cache_lock_);
+  // Don't invalidate the cache if the mappings are unavailable.
+  ProcSelfMapsBuff old_proc_self_maps;
+  old_proc_self_maps = cached_proc_self_maps_;
+  ReadProcMaps(&cached_proc_self_maps_);
+  if (cached_proc_self_maps_.mmaped_size == 0) {
+    cached_proc_self_maps_ = old_proc_self_maps;
+  } else {
+    if (old_proc_self_maps.mmaped_size) {
+      UnmapOrDie(old_proc_self_maps.data,
+                 old_proc_self_maps.mmaped_size);
+    }
+  }
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+  SpinMutexLock l(&cache_lock_);
+  if (cached_proc_self_maps_.data) {
+    proc_self_maps_ = cached_proc_self_maps_;
+  }
+}
+
+uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
+                                            uptr max_modules,
+                                            string_predicate_t filter) {
+  Reset();
+  uptr cur_beg, cur_end, cur_offset, prot;
+  InternalScopedString module_name(kMaxPathLength);
+  uptr n_modules = 0;
+  for (uptr i = 0; n_modules < max_modules &&
+                       Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
+                            module_name.size(), &prot);
+       i++) {
+    const char *cur_name = module_name.data();
+    if (cur_name[0] == '\0')
+      continue;
+    if (filter && !filter(cur_name))
+      continue;
+    // Don't subtract 'cur_beg' from the first entry:
+    // * If a binary is compiled w/o -pie, then the first entry in
+    //   process maps is likely the binary itself (all dynamic libs
+    //   are mapped higher in address space). For such a binary,
+    //   instruction offset in binary coincides with the actual
+    //   instruction address in virtual memory (as code section
+    //   is mapped to a fixed memory range).
+    // * If a binary is compiled with -pie, all the modules are
+    //   mapped high at address space (in particular, higher than
+    //   shadow memory of the tool), so the module can't be the
+    //   first entry.
+    uptr base_address = (i ? cur_beg : 0) - cur_offset;
+    LoadedModule *cur_module = &modules[n_modules];
+    cur_module->set(cur_name, base_address);
+    cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
+    n_modules++;
+  }
+  return n_modules;
+}
+
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
+  char *smaps = nullptr;
+  uptr smaps_cap = 0;
+  uptr smaps_len = 0;
+  if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len))
+    return;
+  uptr start = 0;
+  bool file = false;
+  const char *pos = smaps;
+  while (pos < smaps + smaps_len) {
+    if (IsHex(pos[0])) {
+      start = ParseHex(&pos);
+      for (; *pos != '/' && *pos > '\n'; pos++) {}
+      file = *pos == '/';
+    } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
+      while (!IsDecimal(*pos)) pos++;
+      uptr rss = ParseDecimal(&pos) * 1024;
+      cb(start, rss, file, stats, stats_size);
+    }
+    while (*pos++ != '\n') {}
+  }
+  UnmapOrDie(smaps, smaps_cap);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lsan/src/sanitizer_procmaps_freebsd.cc b/lsan/src/sanitizer_procmaps_freebsd.cc
new file mode 100644 (file)
index 0000000..fbc5520
--- /dev/null
@@ -0,0 +1,86 @@
+//===-- sanitizer_procmaps_freebsd.cc -------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (FreeBSD-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD
+#include "sanitizer_common.h"
+#include "sanitizer_freebsd.h"
+#include "sanitizer_procmaps.h"
+
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+# include <osreldate.h>
+# if __FreeBSD_version <= 902001  // v9.2
+#  define kinfo_vmentry xkinfo_vmentry
+# endif
+#endif
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+  const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
+  size_t Size = 0;
+  int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
+  CHECK_EQ(Err, 0);
+  CHECK_GT(Size, 0);
+
+  size_t MmapedSize = Size * 4 / 3;
+  void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
+  Size = MmapedSize;
+  Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
+  CHECK_EQ(Err, 0);
+
+  proc_maps->data = (char*)VmMap;
+  proc_maps->mmaped_size = MmapedSize;
+  proc_maps->len = Size;
+}
+
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+                               char filename[], uptr filename_size,
+                               uptr *protection) {
+  char *last = proc_self_maps_.data + proc_self_maps_.len;
+  if (current_ >= last) return false;
+  uptr dummy;
+  if (!start) start = &dummy;
+  if (!end) end = &dummy;
+  if (!offset) offset = &dummy;
+  if (!protection) protection = &dummy;
+  struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
+
+  *start = (uptr)VmEntry->kve_start;
+  *end = (uptr)VmEntry->kve_end;
+  *offset = (uptr)VmEntry->kve_offset;
+
+  *protection = 0;
+  if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
+    *protection |= kProtectionRead;
+  if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
+    *protection |= kProtectionWrite;
+  if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
+    *protection |= kProtectionExecute;
+
+  if (filename != NULL && filename_size > 0) {
+    internal_snprintf(filename,
+                      Min(filename_size, (uptr)PATH_MAX),
+                      "%s", VmEntry->kve_path);
+  }
+
+  current_ += VmEntry->kve_structsize;
+
+  return true;
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_FREEBSD
diff --git a/lsan/src/sanitizer_procmaps_linux.cc b/lsan/src/sanitizer_procmaps_linux.cc
new file mode 100644 (file)
index 0000000..e77e33f
--- /dev/null
@@ -0,0 +1,88 @@
+//===-- sanitizer_procmaps_linux.cc ---------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (Linux-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_LINUX
+#include "sanitizer_common.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+  CHECK(ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
+                         &proc_maps->mmaped_size, &proc_maps->len));
+}
+
+static bool IsOneOf(char c, char c1, char c2) {
+  return c == c1 || c == c2;
+}
+
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+                               char filename[], uptr filename_size,
+                               uptr *protection) {
+  char *last = proc_self_maps_.data + proc_self_maps_.len;
+  if (current_ >= last) return false;
+  uptr dummy;
+  if (!start) start = &dummy;
+  if (!end) end = &dummy;
+  if (!offset) offset = &dummy;
+  if (!protection) protection = &dummy;
+  char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
+  if (next_line == 0)
+    next_line = last;
+  // Example: 08048000-08056000 r-xp 00000000 03:0c 64593   /foo/bar
+  *start = ParseHex(&current_);
+  CHECK_EQ(*current_++, '-');
+  *end = ParseHex(&current_);
+  CHECK_EQ(*current_++, ' ');
+  CHECK(IsOneOf(*current_, '-', 'r'));
+  *protection = 0;
+  if (*current_++ == 'r')
+    *protection |= kProtectionRead;
+  CHECK(IsOneOf(*current_, '-', 'w'));
+  if (*current_++ == 'w')
+    *protection |= kProtectionWrite;
+  CHECK(IsOneOf(*current_, '-', 'x'));
+  if (*current_++ == 'x')
+    *protection |= kProtectionExecute;
+  CHECK(IsOneOf(*current_, 's', 'p'));
+  if (*current_++ == 's')
+    *protection |= kProtectionShared;
+  CHECK_EQ(*current_++, ' ');
+  *offset = ParseHex(&current_);
+  CHECK_EQ(*current_++, ' ');
+  ParseHex(&current_);
+  CHECK_EQ(*current_++, ':');
+  ParseHex(&current_);
+  CHECK_EQ(*current_++, ' ');
+  while (IsDecimal(*current_))
+    current_++;
+  // Qemu may lack the trailing space.
+  // http://code.google.com/p/address-sanitizer/issues/detail?id=160
+  // CHECK_EQ(*current_++, ' ');
+  // Skip spaces.
+  while (current_ < next_line && *current_ == ' ')
+    current_++;
+  // Fill in the filename.
+  uptr i = 0;
+  while (current_ < next_line) {
+    if (filename && i < filename_size - 1)
+      filename[i++] = *current_;
+    current_++;
+  }
+  if (filename && i < filename_size)
+    filename[i] = 0;
+  current_ = next_line + 1;
+  return true;
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_LINUX
diff --git a/lsan/src/sanitizer_procmaps_mac.cc b/lsan/src/sanitizer_procmaps_mac.cc
new file mode 100644 (file)
index 0000000..393243b
--- /dev/null
@@ -0,0 +1,188 @@
+//===-- sanitizer_procmaps_mac.cc -----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (Mac-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+
+namespace __sanitizer {
+
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
+  Reset();
+}
+
+MemoryMappingLayout::~MemoryMappingLayout() {
+}
+
+// More information about Mach-O headers can be found in mach-o/loader.h
+// Each Mach-O image has a header (mach_header or mach_header_64) starting with
+// a magic number, and a list of linker load commands directly following the
+// header.
+// A load command is at least two 32-bit words: the command type and the
+// command size in bytes. We're interested only in segment load commands
+// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped
+// into the task's address space.
+// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or
+// segment_command_64 correspond to the memory address, memory size and the
+// file offset of the current memory segment.
+// Because these fields are taken from the images as is, one needs to add
+// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
+
+void MemoryMappingLayout::Reset() {
+  // Count down from the top.
+  // TODO(glider): as per man 3 dyld, iterating over the headers with
+  // _dyld_image_count is thread-unsafe. We need to register callbacks for
+  // adding and removing images which will invalidate the MemoryMappingLayout
+  // state.
+  current_image_ = _dyld_image_count();
+  current_load_cmd_count_ = -1;
+  current_load_cmd_addr_ = 0;
+  current_magic_ = 0;
+  current_filetype_ = 0;
+}
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+  // No-op on Mac for now.
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+  // No-op on Mac for now.
+}
+
+// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
+// Google Perftools, http://code.google.com/p/google-perftools.
+
+// NextSegmentLoad scans the current image for the next segment load command
+// and returns the start and end addresses and file offset of the corresponding
+// segment.
+// Note that the segment addresses are not necessarily sorted.
+template<u32 kLCSegment, typename SegmentCommand>
+bool MemoryMappingLayout::NextSegmentLoad(
+    uptr *start, uptr *end, uptr *offset,
+    char filename[], uptr filename_size, uptr *protection) {
+  const char* lc = current_load_cmd_addr_;
+  current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
+  if (((const load_command *)lc)->cmd == kLCSegment) {
+    const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
+    const SegmentCommand* sc = (const SegmentCommand *)lc;
+    if (start) *start = sc->vmaddr + dlloff;
+    if (protection) {
+      // Return the initial protection.
+      *protection = sc->initprot;
+    }
+    if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
+    if (offset) {
+      if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
+        *offset = sc->vmaddr;
+      } else {
+        *offset = sc->fileoff;
+      }
+    }
+    if (filename) {
+      internal_strncpy(filename, _dyld_get_image_name(current_image_),
+                       filename_size);
+    }
+    return true;
+  }
+  return false;
+}
+
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+                               char filename[], uptr filename_size,
+                               uptr *protection) {
+  for (; current_image_ >= 0; current_image_--) {
+    const mach_header* hdr = _dyld_get_image_header(current_image_);
+    if (!hdr) continue;
+    if (current_load_cmd_count_ < 0) {
+      // Set up for this image;
+      current_load_cmd_count_ = hdr->ncmds;
+      current_magic_ = hdr->magic;
+      current_filetype_ = hdr->filetype;
+      switch (current_magic_) {
+#ifdef MH_MAGIC_64
+        case MH_MAGIC_64: {
+          current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64);
+          break;
+        }
+#endif
+        case MH_MAGIC: {
+          current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header);
+          break;
+        }
+        default: {
+          continue;
+        }
+      }
+    }
+
+    for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) {
+      switch (current_magic_) {
+        // current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64.
+#ifdef MH_MAGIC_64
+        case MH_MAGIC_64: {
+          if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(
+                  start, end, offset, filename, filename_size, protection))
+            return true;
+          break;
+        }
+#endif
+        case MH_MAGIC: {
+          if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(
+                  start, end, offset, filename, filename_size, protection))
+            return true;
+          break;
+        }
+      }
+    }
+    // If we get here, no more load_cmd's in this image talk about
+    // segments.  Go on to the next image.
+  }
+  return false;
+}
+
+uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
+                                            uptr max_modules,
+                                            string_predicate_t filter) {
+  Reset();
+  uptr cur_beg, cur_end, prot;
+  InternalScopedString module_name(kMaxPathLength);
+  uptr n_modules = 0;
+  for (uptr i = 0; n_modules < max_modules &&
+                       Next(&cur_beg, &cur_end, 0, module_name.data(),
+                            module_name.size(), &prot);
+       i++) {
+    const char *cur_name = module_name.data();
+    if (cur_name[0] == '\0')
+      continue;
+    if (filter && !filter(cur_name))
+      continue;
+    LoadedModule *cur_module = nullptr;
+    if (n_modules > 0 &&
+        0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) {
+      cur_module = &modules[n_modules - 1];
+    } else {
+      cur_module = &modules[n_modules];
+      cur_module->set(cur_name, cur_beg);
+      n_modules++;
+    }
+    cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
+  }
+  return n_modules;
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_MAC
diff --git a/lsan/src/sanitizer_stackdepot.cc b/lsan/src/sanitizer_stackdepot.cc
new file mode 100644 (file)
index 0000000..3c5313c
--- /dev/null
@@ -0,0 +1,161 @@
+//===-- sanitizer_stackdepot.cc -------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_stackdepot.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_stackdepotbase.h"
+
+namespace __sanitizer {
+
+struct StackDepotNode {
+  StackDepotNode *link;
+  u32 id;
+  atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
+  u32 size;
+  u32 tag;
+  uptr stack[1];  // [size]
+
+  static const u32 kTabSizeLog = 20;
+  // Lower kTabSizeLog bits are equal for all items in one bucket.
+  // We use these bits to store the per-stack use counter.
+  static const u32 kUseCountBits = kTabSizeLog;
+  static const u32 kMaxUseCount = 1 << kUseCountBits;
+  static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
+  static const u32 kHashMask = ~kUseCountMask;
+
+  typedef StackTrace args_type;
+  bool eq(u32 hash, const args_type &args) const {
+    u32 hash_bits =
+        atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
+    if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag)
+      return false;
+    uptr i = 0;
+    for (; i < size; i++) {
+      if (stack[i] != args.trace[i]) return false;
+    }
+    return true;
+  }
+  static uptr storage_size(const args_type &args) {
+    return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
+  }
+  static u32 hash(const args_type &args) {
+    // murmur2
+    const u32 m = 0x5bd1e995;
+    const u32 seed = 0x9747b28c;
+    const u32 r = 24;
+    u32 h = seed ^ (args.size * sizeof(uptr));
+    for (uptr i = 0; i < args.size; i++) {
+      u32 k = args.trace[i];
+      k *= m;
+      k ^= k >> r;
+      k *= m;
+      h *= m;
+      h ^= k;
+    }
+    h ^= h >> 13;
+    h *= m;
+    h ^= h >> 15;
+    return h;
+  }
+  static bool is_valid(const args_type &args) {
+    return args.size > 0 && args.trace;
+  }
+  void store(const args_type &args, u32 hash) {
+    atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
+    size = args.size;
+    tag = args.tag;
+    internal_memcpy(stack, args.trace, size * sizeof(uptr));
+  }
+  args_type load() const {
+    return args_type(&stack[0], size, tag);
+  }
+  StackDepotHandle get_handle() { return StackDepotHandle(this); }
+
+  typedef StackDepotHandle handle_type;
+};
+
+COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount);
+
+u32 StackDepotHandle::id() { return node_->id; }
+int StackDepotHandle::use_count() {
+  return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) &
+         StackDepotNode::kUseCountMask;
+}
+void StackDepotHandle::inc_use_count_unsafe() {
+  u32 prev =
+      atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) &
+      StackDepotNode::kUseCountMask;
+  CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
+}
+
+// FIXME(dvyukov): this single reserved bit is used in TSan.
+typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
+    StackDepot;
+static StackDepot theDepot;
+
+StackDepotStats *StackDepotGetStats() {
+  return theDepot.GetStats();
+}
+
+u32 StackDepotPut(StackTrace stack) {
+  StackDepotHandle h = theDepot.Put(stack);
+  return h.valid() ? h.id() : 0;
+}
+
+StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
+  return theDepot.Put(stack);
+}
+
+StackTrace StackDepotGet(u32 id) {
+  return theDepot.Get(id);
+}
+
+void StackDepotLockAll() {
+  theDepot.LockAll();
+}
+
+void StackDepotUnlockAll() {
+  theDepot.UnlockAll();
+}
+
+bool StackDepotReverseMap::IdDescPair::IdComparator(
+    const StackDepotReverseMap::IdDescPair &a,
+    const StackDepotReverseMap::IdDescPair &b) {
+  return a.id < b.id;
+}
+
+StackDepotReverseMap::StackDepotReverseMap()
+    : map_(StackDepotGetStats()->n_uniq_ids + 100) {
+  for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
+    atomic_uintptr_t *p = &theDepot.tab[idx];
+    uptr v = atomic_load(p, memory_order_consume);
+    StackDepotNode *s = (StackDepotNode*)(v & ~1);
+    for (; s; s = s->link) {
+      IdDescPair pair = {s->id, s};
+      map_.push_back(pair);
+    }
+  }
+  InternalSort(&map_, map_.size(), IdDescPair::IdComparator);
+}
+
+StackTrace StackDepotReverseMap::Get(u32 id) {
+  if (!map_.size())
+    return StackTrace();
+  IdDescPair pair = {id, nullptr};
+  uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair,
+                                  IdDescPair::IdComparator);
+  if (idx > map_.size())
+    return StackTrace();
+  return map_[idx].desc->load();
+}
+
+} // namespace __sanitizer
diff --git a/lsan/src/sanitizer_stacktrace.cc b/lsan/src/sanitizer_stacktrace.cc
new file mode 100644 (file)
index 0000000..796d472
--- /dev/null
@@ -0,0 +1,127 @@
+//===-- sanitizer_stacktrace.cc -------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_stacktrace.h"
+
+namespace __sanitizer {
+
+uptr StackTrace::GetNextInstructionPc(uptr pc) {
+#if defined(__mips__)
+  return pc + 8;
+#elif defined(__powerpc__)
+  return pc + 4;
+#else
+  return pc + 1;
+#endif
+}
+
+uptr StackTrace::GetCurrentPc() {
+  return GET_CALLER_PC();
+}
+
+void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
+  size = cnt + !!extra_top_pc;
+  CHECK_LE(size, kStackTraceMax);
+  internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
+  if (extra_top_pc)
+    trace_buffer[cnt] = extra_top_pc;
+  top_frame_bp = 0;
+}
+
+// Check if given pointer points into allocated stack area.
+static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
+  return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
+}
+
+// In GCC on ARM bp points to saved lr, not fp, so we should check the next
+// cell in stack to be a saved frame pointer. GetCanonicFrame returns the
+// pointer to saved frame pointer in any case.
+static inline uhwptr *GetCanonicFrame(uptr bp,
+                                      uptr stack_top,
+                                      uptr stack_bottom) {
+#ifdef __arm__
+  if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
+  uhwptr *bp_prev = (uhwptr *)bp;
+  if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
+  // The next frame pointer does not look right. This could be a GCC frame, step
+  // back by 1 word and try again.
+  if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom))
+    return bp_prev - 1;
+  // Nope, this does not look right either. This means the frame after next does
+  // not have a valid frame pointer, but we can still extract the caller PC.
+  // Unfortunately, there is no way to decide between GCC and LLVM frame
+  // layouts. Assume GCC.
+  return bp_prev - 1;
+#else
+  return (uhwptr*)bp;
+#endif
+}
+
+void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
+                                         uptr stack_bottom, u32 max_depth) {
+  CHECK_GE(max_depth, 2);
+  trace_buffer[0] = pc;
+  size = 1;
+  if (stack_top < 4096) return;  // Sanity check for stack top.
+  uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
+  // Lowest possible address that makes sense as the next frame pointer.
+  // Goes up as we walk the stack.
+  uptr bottom = stack_bottom;
+  // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
+  while (IsValidFrame((uptr)frame, stack_top, bottom) &&
+         IsAligned((uptr)frame, sizeof(*frame)) &&
+         size < max_depth) {
+#ifdef __powerpc__
+    // PowerPC ABIs specify that the return address is saved at offset
+    // 16 of the *caller's* stack frame.  Thus we must dereference the
+    // back chain to find the caller frame before extracting it.
+    uhwptr *caller_frame = (uhwptr*)frame[0];
+    if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
+        !IsAligned((uptr)caller_frame, sizeof(uhwptr)))
+      break;
+    uhwptr pc1 = caller_frame[2];
+#else
+    uhwptr pc1 = frame[1];
+#endif
+    if (pc1 != pc) {
+      trace_buffer[size++] = (uptr) pc1;
+    }
+    bottom = (uptr)frame;
+    frame = GetCanonicFrame((uptr)frame[0], stack_top, bottom);
+  }
+}
+
+static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) {
+  return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold;
+}
+
+void BufferedStackTrace::PopStackFrames(uptr count) {
+  CHECK_LT(count, size);
+  size -= count;
+  for (uptr i = 0; i < size; ++i) {
+    trace_buffer[i] = trace_buffer[i + count];
+  }
+}
+
+uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
+  // Use threshold to find PC in stack trace, as PC we want to unwind from may
+  // slightly differ from return address in the actual unwinded stack trace.
+  const int kPcThreshold = 304;
+  for (uptr i = 0; i < size; ++i) {
+    if (MatchPc(pc, trace[i], kPcThreshold))
+      return i;
+  }
+  return 0;
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_stacktrace_libcdep.cc b/lsan/src/sanitizer_stacktrace_libcdep.cc
new file mode 100644 (file)
index 0000000..addf44f
--- /dev/null
@@ -0,0 +1,74 @@
+//===-- sanitizer_stacktrace_libcdep.cc -----------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+void StackTrace::Print() const {
+  if (trace == nullptr || size == 0) {
+    Printf("    <empty stack>\n\n");
+    return;
+  }
+  InternalScopedString frame_desc(GetPageSizeCached() * 2);
+  uptr frame_num = 0;
+  for (uptr i = 0; i < size && trace[i]; i++) {
+    // PCs in stack traces are actually the return addresses, that is,
+    // addresses of the next instructions after the call.
+    uptr pc = GetPreviousInstructionPc(trace[i]);
+    SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
+    CHECK(frames);
+    for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+      frame_desc.clear();
+      RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
+                  cur->info, common_flags()->symbolize_vs_style,
+                  common_flags()->strip_path_prefix);
+      Printf("%s\n", frame_desc.data());
+    }
+    frames->ClearAll();
+  }
+  // Always print a trailing empty line after stack trace.
+  Printf("\n");
+}
+
+void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
+                                uptr stack_top, uptr stack_bottom,
+                                bool request_fast_unwind) {
+  top_frame_bp = (max_depth > 0) ? bp : 0;
+  // Avoid doing any work for small max_depth.
+  if (max_depth == 0) {
+    size = 0;
+    return;
+  }
+  if (max_depth == 1) {
+    size = 1;
+    trace_buffer[0] = pc;
+    return;
+  }
+  if (!WillUseFastUnwind(request_fast_unwind)) {
+#if SANITIZER_CAN_SLOW_UNWIND
+    if (context)
+      SlowUnwindStackWithContext(pc, context, max_depth);
+    else
+      SlowUnwindStack(pc, max_depth);
+#else
+    UNREACHABLE("slow unwind requested but not available");
+#endif
+  } else {
+    FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
+  }
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_stacktrace_printer.cc b/lsan/src/sanitizer_stacktrace_printer.cc
new file mode 100644 (file)
index 0000000..bcc9de7
--- /dev/null
@@ -0,0 +1,142 @@
+//===-- sanitizer_common.cc -----------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizers' run-time libraries.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_stacktrace_printer.h"
+
+namespace __sanitizer {
+
+static const char *StripFunctionName(const char *function, const char *prefix) {
+  if (!function) return nullptr;
+  if (!prefix) return function;
+  uptr prefix_len = internal_strlen(prefix);
+  if (0 == internal_strncmp(function, prefix, prefix_len))
+    return function + prefix_len;
+  return function;
+}
+
+static const char kDefaultFormat[] = "    #%n %p %F %L";
+
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+                 const AddressInfo &info, bool vs_style,
+                 const char *strip_path_prefix, const char *strip_func_prefix) {
+  if (0 == internal_strcmp(format, "DEFAULT"))
+    format = kDefaultFormat;
+  for (const char *p = format; *p != '\0'; p++) {
+    if (*p != '%') {
+      buffer->append("%c", *p);
+      continue;
+    }
+    p++;
+    switch (*p) {
+    case '%':
+      buffer->append("%%");
+      break;
+    // Frame number and all fields of AddressInfo structure.
+    case 'n':
+      buffer->append("%zu", frame_no);
+      break;
+    case 'p':
+      buffer->append("0x%zx", info.address);
+      break;
+    case 'm':
+      buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
+      break;
+    case 'o':
+      buffer->append("0x%zx", info.module_offset);
+      break;
+    case 'f':
+      buffer->append("%s", StripFunctionName(info.function, strip_func_prefix));
+      break;
+    case 'q':
+      buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
+                                  ? info.function_offset
+                                  : 0x0);
+      break;
+    case 's':
+      buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
+      break;
+    case 'l':
+      buffer->append("%d", info.line);
+      break;
+    case 'c':
+      buffer->append("%d", info.column);
+      break;
+    // Smarter special cases.
+    case 'F':
+      // Function name and offset, if file is unknown.
+      if (info.function) {
+        buffer->append("in %s",
+                       StripFunctionName(info.function, strip_func_prefix));
+        if (!info.file && info.function_offset != AddressInfo::kUnknown)
+          buffer->append("+0x%zx", info.function_offset);
+      }
+      break;
+    case 'S':
+      // File/line information.
+      RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
+                           strip_path_prefix);
+      break;
+    case 'L':
+      // Source location, or module location.
+      if (info.file) {
+        RenderSourceLocation(buffer, info.file, info.line, info.column,
+                             vs_style, strip_path_prefix);
+      } else if (info.module) {
+        RenderModuleLocation(buffer, info.module, info.module_offset,
+                             strip_path_prefix);
+      } else {
+        buffer->append("(<unknown module>)");
+      }
+      break;
+    case 'M':
+      // Module basename and offset, or PC.
+      if (info.address & kExternalPCBit)
+        {} // There PCs are not meaningful.
+      else if (info.module)
+        buffer->append("(%s+%p)", StripModuleName(info.module),
+                       (void *)info.module_offset);
+      else
+        buffer->append("(%p)", (void *)info.address);
+      break;
+    default:
+      Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
+             *p);
+      Die();
+    }
+  }
+}
+
+void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+                          int line, int column, bool vs_style,
+                          const char *strip_path_prefix) {
+  if (vs_style && line > 0) {
+    buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
+    if (column > 0)
+      buffer->append(",%d", column);
+    buffer->append(")");
+    return;
+  }
+
+  buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
+  if (line > 0) {
+    buffer->append(":%d", line);
+    if (column > 0)
+      buffer->append(":%d", column);
+  }
+}
+
+void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+                          uptr offset, const char *strip_path_prefix) {
+  buffer->append("(%s+0x%zx)", StripPathPrefix(module, strip_path_prefix),
+                 offset);
+}
+
+} // namespace __sanitizer
diff --git a/lsan/src/sanitizer_stoptheworld_linux_libcdep.cc b/lsan/src/sanitizer_stoptheworld_linux_libcdep.cc
new file mode 100644 (file)
index 0000000..da5348e
--- /dev/null
@@ -0,0 +1,514 @@
+//===-- sanitizer_stoptheworld_linux_libcdep.cc ---------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// See sanitizer_stoptheworld.h for details.
+// This implementation was inspired by Markus Gutschke's linuxthreads.cc.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
+                        defined(__aarch64__) || defined(__arm__) || \
+                        defined(__i386__))
+
+#include "sanitizer_stoptheworld.h"
+
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_atomic.h"
+
+#include <errno.h>
+#include <sched.h> // for CLONE_* definitions
+#include <stddef.h>
+#include <sys/prctl.h> // for PR_* definitions
+#include <sys/ptrace.h> // for PTRACE_* definitions
+#include <sys/types.h> // for pid_t
+#include <sys/uio.h> // for iovec
+#include <elf.h> // for NT_PRSTATUS
+#if SANITIZER_ANDROID && defined(__arm__)
+# include <linux/user.h>  // for pt_regs
+#else
+# ifdef __aarch64__
+// GLIBC 2.20+ sys/user does not include asm/ptrace.h
+#  include <asm/ptrace.h>
+# endif
+# include <sys/user.h>  // for user_regs_struct
+#endif
+#include <sys/wait.h> // for signal-related stuff
+
+#ifdef sa_handler
+# undef sa_handler
+#endif
+
+#ifdef sa_sigaction
+# undef sa_sigaction
+#endif
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+
+// This module works by spawning a Linux task which then attaches to every
+// thread in the caller process with ptrace. This suspends the threads, and
+// PTRACE_GETREGS can then be used to obtain their register state. The callback
+// supplied to StopTheWorld() is run in the tracer task while the threads are
+// suspended.
+// The tracer task must be placed in a different thread group for ptrace to
+// work, so it cannot be spawned as a pthread. Instead, we use the low-level
+// clone() interface (we want to share the address space with the caller
+// process, so we prefer clone() over fork()).
+//
+// We don't use any libc functions, relying instead on direct syscalls. There
+// are two reasons for this:
+// 1. calling a library function while threads are suspended could cause a
+// deadlock, if one of the treads happens to be holding a libc lock;
+// 2. it's generally not safe to call libc functions from the tracer task,
+// because clone() does not set up a thread-local storage for it. Any
+// thread-local variables used by libc will be shared between the tracer task
+// and the thread which spawned it.
+
+COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));
+
+namespace __sanitizer {
+
+// Structure for passing arguments into the tracer thread.
+struct TracerThreadArgument {
+  StopTheWorldCallback callback;
+  void *callback_argument;
+  // The tracer thread waits on this mutex while the parent finishes its
+  // preparations.
+  BlockingMutex mutex;
+  // Tracer thread signals its completion by setting done.
+  atomic_uintptr_t done;
+  uptr parent_pid;
+};
+
+// This class handles thread suspending/unsuspending in the tracer thread.
+class ThreadSuspender {
+ public:
+  explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
+    : arg(arg)
+    , pid_(pid) {
+      CHECK_GE(pid, 0);
+    }
+  bool SuspendAllThreads();
+  void ResumeAllThreads();
+  void KillAllThreads();
+  SuspendedThreadsList &suspended_threads_list() {
+    return suspended_threads_list_;
+  }
+  TracerThreadArgument *arg;
+ private:
+  SuspendedThreadsList suspended_threads_list_;
+  pid_t pid_;
+  bool SuspendThread(SuspendedThreadID thread_id);
+};
+
+bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
+  // Are we already attached to this thread?
+  // Currently this check takes linear time, however the number of threads is
+  // usually small.
+  if (suspended_threads_list_.Contains(tid))
+    return false;
+  int pterrno;
+  if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
+                       &pterrno)) {
+    // Either the thread is dead, or something prevented us from attaching.
+    // Log this event and move on.
+    VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
+    return false;
+  } else {
+    VReport(2, "Attached to thread %d.\n", tid);
+    // The thread is not guaranteed to stop before ptrace returns, so we must
+    // wait on it. Note: if the thread receives a signal concurrently,
+    // we can get notification about the signal before notification about stop.
+    // In such case we need to forward the signal to the thread, otherwise
+    // the signal will be missed (as we do PTRACE_DETACH with arg=0) and
+    // any logic relying on signals will break. After forwarding we need to
+    // continue to wait for stopping, because the thread is not stopped yet.
+    // We do ignore delivery of SIGSTOP, because we want to make stop-the-world
+    // as invisible as possible.
+    for (;;) {
+      int status;
+      uptr waitpid_status;
+      HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL));
+      int wperrno;
+      if (internal_iserror(waitpid_status, &wperrno)) {
+        // Got a ECHILD error. I don't think this situation is possible, but it
+        // doesn't hurt to report it.
+        VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",
+                tid, wperrno);
+        internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
+        return false;
+      }
+      if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
+        internal_ptrace(PTRACE_CONT, tid, nullptr,
+                        (void*)(uptr)WSTOPSIG(status));
+        continue;
+      }
+      break;
+    }
+    suspended_threads_list_.Append(tid);
+    return true;
+  }
+}
+
+void ThreadSuspender::ResumeAllThreads() {
+  for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) {
+    pid_t tid = suspended_threads_list_.GetThreadID(i);
+    int pterrno;
+    if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr),
+                          &pterrno)) {
+      VReport(2, "Detached from thread %d.\n", tid);
+    } else {
+      // Either the thread is dead, or we are already detached.
+      // The latter case is possible, for instance, if this function was called
+      // from a signal handler.
+      VReport(1, "Could not detach from thread %d (errno %d).\n", tid, pterrno);
+    }
+  }
+}
+
+void ThreadSuspender::KillAllThreads() {
+  for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++)
+    internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i),
+                    nullptr, nullptr);
+}
+
+bool ThreadSuspender::SuspendAllThreads() {
+  ThreadLister thread_lister(pid_);
+  bool added_threads;
+  do {
+    // Run through the directory entries once.
+    added_threads = false;
+    pid_t tid = thread_lister.GetNextTID();
+    while (tid >= 0) {
+      if (SuspendThread(tid))
+        added_threads = true;
+      tid = thread_lister.GetNextTID();
+    }
+    if (thread_lister.error()) {
+      // Detach threads and fail.
+      ResumeAllThreads();
+      return false;
+    }
+    thread_lister.Reset();
+  } while (added_threads);
+  return true;
+}
+
+// Pointer to the ThreadSuspender instance for use in signal handler.
+static ThreadSuspender *thread_suspender_instance = nullptr;
+
+// Synchronous signals that should not be blocked.
+static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
+                                    SIGXCPU, SIGXFSZ };
+
+static void TracerThreadDieCallback() {
+  // Generally a call to Die() in the tracer thread should be fatal to the
+  // parent process as well, because they share the address space.
+  // This really only works correctly if all the threads are suspended at this
+  // point. So we correctly handle calls to Die() from within the callback, but
+  // not those that happen before or after the callback. Hopefully there aren't
+  // a lot of opportunities for that to happen...
+  ThreadSuspender *inst = thread_suspender_instance;
+  if (inst && stoptheworld_tracer_pid == internal_getpid()) {
+    inst->KillAllThreads();
+    thread_suspender_instance = nullptr;
+  }
+}
+
+// Signal handler to wake up suspended threads when the tracer thread dies.
+static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {
+  SignalContext ctx = SignalContext::Create(siginfo, uctx);
+  VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n",
+      signum, ctx.addr, ctx.pc, ctx.sp);
+  ThreadSuspender *inst = thread_suspender_instance;
+  if (inst) {
+    if (signum == SIGABRT)
+      inst->KillAllThreads();
+    else
+      inst->ResumeAllThreads();
+    RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+    thread_suspender_instance = nullptr;
+    atomic_store(&inst->arg->done, 1, memory_order_relaxed);
+  }
+  internal__exit((signum == SIGABRT) ? 1 : 2);
+}
+
+// Size of alternative stack for signal handlers in the tracer thread.
+static const int kHandlerStackSize = 4096;
+
+// This function will be run as a cloned task.
+static int TracerThread(void* argument) {
+  TracerThreadArgument *tracer_thread_argument =
+      (TracerThreadArgument *)argument;
+
+  internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+  // Check if parent is already dead.
+  if (internal_getppid() != tracer_thread_argument->parent_pid)
+    internal__exit(4);
+
+  // Wait for the parent thread to finish preparations.
+  tracer_thread_argument->mutex.Lock();
+  tracer_thread_argument->mutex.Unlock();
+
+  RAW_CHECK(AddDieCallback(TracerThreadDieCallback));
+
+  ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
+  // Global pointer for the signal handler.
+  thread_suspender_instance = &thread_suspender;
+
+  // Alternate stack for signal handling.
+  InternalScopedBuffer<char> handler_stack_memory(kHandlerStackSize);
+  struct sigaltstack handler_stack;
+  internal_memset(&handler_stack, 0, sizeof(handler_stack));
+  handler_stack.ss_sp = handler_stack_memory.data();
+  handler_stack.ss_size = kHandlerStackSize;
+  internal_sigaltstack(&handler_stack, nullptr);
+
+  // Install our handler for synchronous signals. Other signals should be
+  // blocked by the mask we inherited from the parent thread.
+  for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {
+    __sanitizer_sigaction act;
+    internal_memset(&act, 0, sizeof(act));
+    act.sigaction = TracerThreadSignalHandler;
+    act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+    internal_sigaction_norestorer(kSyncSignals[i], &act, 0);
+  }
+
+  int exit_code = 0;
+  if (!thread_suspender.SuspendAllThreads()) {
+    VReport(1, "Failed suspending threads.\n");
+    exit_code = 3;
+  } else {
+    tracer_thread_argument->callback(thread_suspender.suspended_threads_list(),
+                                     tracer_thread_argument->callback_argument);
+    thread_suspender.ResumeAllThreads();
+    exit_code = 0;
+  }
+  RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+  thread_suspender_instance = nullptr;
+  atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
+  return exit_code;
+}
+
+class ScopedStackSpaceWithGuard {
+ public:
+  explicit ScopedStackSpaceWithGuard(uptr stack_size) {
+    stack_size_ = stack_size;
+    guard_size_ = GetPageSizeCached();
+    // FIXME: Omitting MAP_STACK here works in current kernels but might break
+    // in the future.
+    guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,
+                                   "ScopedStackWithGuard");
+    CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
+  }
+  ~ScopedStackSpaceWithGuard() {
+    UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
+  }
+  void *Bottom() const {
+    return (void *)(guard_start_ + stack_size_ + guard_size_);
+  }
+
+ private:
+  uptr stack_size_;
+  uptr guard_size_;
+  uptr guard_start_;
+};
+
+// We have a limitation on the stack frame size, so some stuff had to be moved
+// into globals.
+static __sanitizer_sigset_t blocked_sigset;
+static __sanitizer_sigset_t old_sigset;
+
+class StopTheWorldScope {
+ public:
+  StopTheWorldScope() {
+    // Make this process dumpable. Processes that are not dumpable cannot be
+    // attached to.
+    process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+    if (!process_was_dumpable_)
+      internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+  }
+
+  ~StopTheWorldScope() {
+    // Restore the dumpable flag.
+    if (!process_was_dumpable_)
+      internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
+  }
+
+ private:
+  int process_was_dumpable_;
+};
+
+// When sanitizer output is being redirected to file (i.e. by using log_path),
+// the tracer should write to the parent's log instead of trying to open a new
+// file. Alert the logging code to the fact that we have a tracer.
+struct ScopedSetTracerPID {
+  explicit ScopedSetTracerPID(uptr tracer_pid) {
+    stoptheworld_tracer_pid = tracer_pid;
+    stoptheworld_tracer_ppid = internal_getpid();
+  }
+  ~ScopedSetTracerPID() {
+    stoptheworld_tracer_pid = 0;
+    stoptheworld_tracer_ppid = 0;
+  }
+};
+
+void StopTheWorld(StopTheWorldCallback callback, void *argument) {
+  StopTheWorldScope in_stoptheworld;
+  // Prepare the arguments for TracerThread.
+  struct TracerThreadArgument tracer_thread_argument;
+  tracer_thread_argument.callback = callback;
+  tracer_thread_argument.callback_argument = argument;
+  tracer_thread_argument.parent_pid = internal_getpid();
+  atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
+  const uptr kTracerStackSize = 2 * 1024 * 1024;
+  ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
+  // Block the execution of TracerThread until after we have set ptrace
+  // permissions.
+  tracer_thread_argument.mutex.Lock();
+  // Signal handling story.
+  // We don't want async signals to be delivered to the tracer thread,
+  // so we block all async signals before creating the thread. An async signal
+  // handler can temporary modify errno, which is shared with this thread.
+  // We ought to use pthread_sigmask here, because sigprocmask has undefined
+  // behavior in multithreaded programs. However, on linux sigprocmask is
+  // equivalent to pthread_sigmask with the exception that pthread_sigmask
+  // does not allow to block some signals used internally in pthread
+  // implementation. We are fine with blocking them here, we are really not
+  // going to pthread_cancel the thread.
+  // The tracer thread should not raise any synchronous signals. But in case it
+  // does, we setup a special handler for sync signals that properly kills the
+  // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers
+  // in the tracer thread won't interfere with user program. Double note: if a
+  // user does something along the lines of 'kill -11 pid', that can kill the
+  // process even if user setup own handler for SEGV.
+  // Thing to watch out for: this code should not change behavior of user code
+  // in any observable way. In particular it should not override user signal
+  // handlers.
+  internal_sigfillset(&blocked_sigset);
+  for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)
+    internal_sigdelset(&blocked_sigset, kSyncSignals[i]);
+  int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
+  CHECK_EQ(rv, 0);
+  uptr tracer_pid = internal_clone(
+      TracerThread, tracer_stack.Bottom(),
+      CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
+      &tracer_thread_argument, nullptr /* parent_tidptr */,
+      nullptr /* newtls */, nullptr /* child_tidptr */);
+  internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);
+  int local_errno = 0;
+  if (internal_iserror(tracer_pid, &local_errno)) {
+    VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
+    tracer_thread_argument.mutex.Unlock();
+  } else {
+    ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid);
+    // On some systems we have to explicitly declare that we want to be traced
+    // by the tracer thread.
+#ifdef PR_SET_PTRACER
+    internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0);
+#endif
+    // Allow the tracer thread to start.
+    tracer_thread_argument.mutex.Unlock();
+    // NOTE: errno is shared between this thread and the tracer thread.
+    // internal_waitpid() may call syscall() which can access/spoil errno,
+    // so we can't call it now. Instead we for the tracer thread to finish using
+    // the spin loop below. Man page for sched_yield() says "In the Linux
+    // implementation, sched_yield() always succeeds", so let's hope it does not
+    // spoil errno. Note that this spin loop runs only for brief periods before
+    // the tracer thread has suspended us and when it starts unblocking threads.
+    while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
+      sched_yield();
+    // Now the tracer thread is about to exit and does not touch errno,
+    // wait for it.
+    for (;;) {
+      uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL);
+      if (!internal_iserror(waitpid_status, &local_errno))
+        break;
+      if (local_errno == EINTR)
+        continue;
+      VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
+              local_errno);
+      break;
+    }
+  }
+}
+
+// Platform-specific methods from SuspendedThreadsList.
+#if SANITIZER_ANDROID && defined(__arm__)
+typedef pt_regs regs_struct;
+#define REG_SP ARM_sp
+
+#elif SANITIZER_LINUX && defined(__arm__)
+typedef user_regs regs_struct;
+#define REG_SP uregs[13]
+
+#elif defined(__i386__) || defined(__x86_64__)
+typedef user_regs_struct regs_struct;
+#if defined(__i386__)
+#define REG_SP esp
+#else
+#define REG_SP rsp
+#endif
+
+#elif defined(__powerpc__) || defined(__powerpc64__)
+typedef pt_regs regs_struct;
+#define REG_SP gpr[PT_R1]
+
+#elif defined(__mips__)
+typedef struct user regs_struct;
+#define REG_SP regs[EF_REG29]
+
+#elif defined(__aarch64__)
+typedef struct user_pt_regs regs_struct;
+#define REG_SP sp
+#define ARCH_IOVEC_FOR_GETREGSET
+
+#else
+#error "Unsupported architecture"
+#endif // SANITIZER_ANDROID && defined(__arm__)
+
+int SuspendedThreadsList::GetRegistersAndSP(uptr index,
+                                            uptr *buffer,
+                                            uptr *sp) const {
+  pid_t tid = GetThreadID(index);
+  regs_struct regs;
+  int pterrno;
+#ifdef ARCH_IOVEC_FOR_GETREGSET
+  struct iovec regset_io;
+  regset_io.iov_base = &regs;
+  regset_io.iov_len = sizeof(regs_struct);
+  bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid,
+                                (void*)NT_PRSTATUS, (void*)&regset_io),
+                                &pterrno);
+#else
+  bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr,
+                                &regs), &pterrno);
+#endif
+  if (isErr) {
+    VReport(1, "Could not get registers from thread %d (errno %d).\n", tid,
+            pterrno);
+    return -1;
+  }
+
+  *sp = regs.REG_SP;
+  internal_memcpy(buffer, &regs, sizeof(regs));
+  return 0;
+}
+
+uptr SuspendedThreadsList::RegisterCount() {
+  return sizeof(regs_struct) / sizeof(uptr);
+}
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
+       // || defined(__aarch64__) || defined(__arm__))
diff --git a/lsan/src/sanitizer_suppressions.cc b/lsan/src/sanitizer_suppressions.cc
new file mode 100644 (file)
index 0000000..2b6a781
--- /dev/null
@@ -0,0 +1,166 @@
+//===-- sanitizer_suppressions.cc -----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Suppression parsing/matching code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_suppressions.h"
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+
+namespace __sanitizer {
+
+SuppressionContext::SuppressionContext(const char *suppression_types[],
+                                       int suppression_types_num)
+    : suppression_types_(suppression_types),
+      suppression_types_num_(suppression_types_num), suppressions_(1),
+      can_parse_(true) {
+  CHECK_LE(suppression_types_num_, kMaxSuppressionTypes);
+  internal_memset(has_suppression_type_, 0, suppression_types_num_);
+}
+
+static bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
+                                                /*out*/char *new_file_path,
+                                                uptr new_file_path_size) {
+  InternalScopedString exec(kMaxPathLength);
+  if (ReadBinaryNameCached(exec.data(), exec.size())) {
+    const char *file_name_pos = StripModuleName(exec.data());
+    uptr path_to_exec_len = file_name_pos - exec.data();
+    internal_strncat(new_file_path, exec.data(),
+                     Min(path_to_exec_len, new_file_path_size - 1));
+    internal_strncat(new_file_path, file_path,
+                     new_file_path_size - internal_strlen(new_file_path) - 1);
+    return true;
+  }
+  return false;
+}
+
+void SuppressionContext::ParseFromFile(const char *filename) {
+  if (filename[0] == '\0')
+    return;
+
+  // If we cannot find the file, check if its location is relative to
+  // the location of the executable.
+  InternalScopedString new_file_path(kMaxPathLength);
+  if (!FileExists(filename) && !IsAbsolutePath(filename) &&
+      GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(),
+                                          new_file_path.size())) {
+    filename = new_file_path.data();
+  }
+
+  // Read the file.
+  VPrintf(1, "%s: reading suppressions file at %s\n",
+          SanitizerToolName, filename);
+  char *file_contents;
+  uptr buffer_size;
+  uptr contents_size;
+  if (!ReadFileToBuffer(filename, &file_contents, &buffer_size,
+                        &contents_size)) {
+    Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
+           filename);
+    Die();
+  }
+
+  Parse(file_contents);
+}
+
+bool SuppressionContext::Match(const char *str, const char *type,
+                               Suppression **s) {
+  can_parse_ = false;
+  if (!HasSuppressionType(type))
+    return false;
+  for (uptr i = 0; i < suppressions_.size(); i++) {
+    Suppression &cur = suppressions_[i];
+    if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) {
+      *s = &cur;
+      return true;
+    }
+  }
+  return false;
+}
+
+static const char *StripPrefix(const char *str, const char *prefix) {
+  while (str && *str == *prefix) {
+    str++;
+    prefix++;
+  }
+  if (!*prefix)
+    return str;
+  return 0;
+}
+
+void SuppressionContext::Parse(const char *str) {
+  // Context must not mutate once Match has been called.
+  CHECK(can_parse_);
+  const char *line = str;
+  while (line) {
+    while (line[0] == ' ' || line[0] == '\t')
+      line++;
+    const char *end = internal_strchr(line, '\n');
+    if (end == 0)
+      end = line + internal_strlen(line);
+    if (line != end && line[0] != '#') {
+      const char *end2 = end;
+      while (line != end2 &&
+             (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r'))
+        end2--;
+      int type;
+      for (type = 0; type < suppression_types_num_; type++) {
+        const char *next_char = StripPrefix(line, suppression_types_[type]);
+        if (next_char && *next_char == ':') {
+          line = ++next_char;
+          break;
+        }
+      }
+      if (type == suppression_types_num_) {
+        Printf("%s: failed to parse suppressions\n", SanitizerToolName);
+        Die();
+      }
+      Suppression s = {};
+      s.type = suppression_types_[type];
+      s.templ = (char*)InternalAlloc(end2 - line + 1);
+      internal_memcpy(s.templ, line, end2 - line);
+      s.templ[end2 - line] = 0;
+      suppressions_.push_back(s);
+      has_suppression_type_[type] = true;
+    }
+    if (end[0] == 0)
+      break;
+    line = end + 1;
+  }
+}
+
+uptr SuppressionContext::SuppressionCount() const {
+  return suppressions_.size();
+}
+
+bool SuppressionContext::HasSuppressionType(const char *type) const {
+  for (int i = 0; i < suppression_types_num_; i++) {
+    if (0 == internal_strcmp(type, suppression_types_[i]))
+      return has_suppression_type_[i];
+  }
+  return false;
+}
+
+const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
+  CHECK_LT(i, suppressions_.size());
+  return &suppressions_[i];
+}
+
+void SuppressionContext::GetMatched(
+    InternalMmapVector<Suppression *> *matched) {
+  for (uptr i = 0; i < suppressions_.size(); i++)
+    if (atomic_load_relaxed(&suppressions_[i].hit_count))
+      matched->push_back(&suppressions_[i]);
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_symbolizer.cc b/lsan/src/sanitizer_symbolizer.cc
new file mode 100644 (file)
index 0000000..0e58d5f
--- /dev/null
@@ -0,0 +1,111 @@
+//===-- sanitizer_symbolizer.cc -------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_platform.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+AddressInfo::AddressInfo() {
+  internal_memset(this, 0, sizeof(AddressInfo));
+  function_offset = kUnknown;
+}
+
+void AddressInfo::Clear() {
+  InternalFree(module);
+  InternalFree(function);
+  InternalFree(file);
+  internal_memset(this, 0, sizeof(AddressInfo));
+  function_offset = kUnknown;
+}
+
+void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset) {
+  module = internal_strdup(mod_name);
+  module_offset = mod_offset;
+}
+
+SymbolizedStack::SymbolizedStack() : next(nullptr), info() {}
+
+SymbolizedStack *SymbolizedStack::New(uptr addr) {
+  void *mem = InternalAlloc(sizeof(SymbolizedStack));
+  SymbolizedStack *res = new(mem) SymbolizedStack();
+  res->info.address = addr;
+  return res;
+}
+
+void SymbolizedStack::ClearAll() {
+  info.Clear();
+  if (next)
+    next->ClearAll();
+  InternalFree(this);
+}
+
+DataInfo::DataInfo() {
+  internal_memset(this, 0, sizeof(DataInfo));
+}
+
+void DataInfo::Clear() {
+  InternalFree(module);
+  InternalFree(name);
+  internal_memset(this, 0, sizeof(DataInfo));
+}
+
+Symbolizer *Symbolizer::symbolizer_;
+StaticSpinMutex Symbolizer::init_mu_;
+LowLevelAllocator Symbolizer::symbolizer_allocator_;
+
+void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
+                          Symbolizer::EndSymbolizationHook end_hook) {
+  CHECK(start_hook_ == 0 && end_hook_ == 0);
+  start_hook_ = start_hook;
+  end_hook_ = end_hook;
+}
+
+const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) {
+  mu_->CheckLocked();
+
+  // 'str' will be the same string multiple times in a row, optimize this case.
+  if (last_match_ && !internal_strcmp(last_match_, str))
+    return last_match_;
+
+  // FIXME: this is linear search.
+  // We should optimize this further if this turns out to be a bottleneck later.
+  for (uptr i = 0; i < storage_.size(); ++i) {
+    if (!internal_strcmp(storage_[i], str)) {
+      last_match_ = storage_[i];
+      return last_match_;
+    }
+  }
+  last_match_ = internal_strdup(str);
+  storage_.push_back(last_match_);
+  return last_match_;
+}
+
+Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
+    : module_names_(&mu_), n_modules_(0), modules_fresh_(false), tools_(tools),
+      start_hook_(0), end_hook_(0) {}
+
+Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
+    : sym_(sym) {
+  if (sym_->start_hook_)
+    sym_->start_hook_();
+}
+
+Symbolizer::SymbolizerScope::~SymbolizerScope() {
+  if (sym_->end_hook_)
+    sym_->end_hook_();
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_symbolizer_libbacktrace.cc b/lsan/src/sanitizer_symbolizer_libbacktrace.cc
new file mode 100644 (file)
index 0000000..949ad78
--- /dev/null
@@ -0,0 +1,207 @@
+//===-- sanitizer_symbolizer_libbacktrace.cc ------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Libbacktrace implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_libbacktrace.h"
+
+#if SANITIZER_LIBBACKTRACE
+# include "backtrace-supported.h"
+# if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC
+#  include "backtrace.h"
+#  if SANITIZER_CP_DEMANGLE
+#   undef ARRAY_SIZE
+#   include "demangle.h"
+#  endif
+# else
+#  define SANITIZER_LIBBACKTRACE 0
+# endif
+#endif
+
+namespace __sanitizer {
+
+static char *DemangleAlloc(const char *name, bool always_alloc);
+
+#if SANITIZER_LIBBACKTRACE
+
+namespace {
+
+# if SANITIZER_CP_DEMANGLE
+struct CplusV3DemangleData {
+  char *buf;
+  uptr size, allocated;
+};
+
+extern "C" {
+static void CplusV3DemangleCallback(const char *s, size_t l, void *vdata) {
+  CplusV3DemangleData *data = (CplusV3DemangleData *)vdata;
+  uptr needed = data->size + l + 1;
+  if (needed > data->allocated) {
+    data->allocated *= 2;
+    if (needed > data->allocated)
+      data->allocated = needed;
+    char *buf = (char *)InternalAlloc(data->allocated);
+    if (data->buf) {
+      internal_memcpy(buf, data->buf, data->size);
+      InternalFree(data->buf);
+    }
+    data->buf = buf;
+  }
+  internal_memcpy(data->buf + data->size, s, l);
+  data->buf[data->size + l] = '\0';
+  data->size += l;
+}
+}  // extern "C"
+
+char *CplusV3Demangle(const char *name) {
+  CplusV3DemangleData data;
+  data.buf = 0;
+  data.size = 0;
+  data.allocated = 0;
+  if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI,
+                                 CplusV3DemangleCallback, &data)) {
+    if (data.size + 64 > data.allocated)
+      return data.buf;
+    char *buf = internal_strdup(data.buf);
+    InternalFree(data.buf);
+    return buf;
+  }
+  if (data.buf)
+    InternalFree(data.buf);
+  return 0;
+}
+# endif  // SANITIZER_CP_DEMANGLE
+
+struct SymbolizeCodeCallbackArg {
+  SymbolizedStack *first;
+  SymbolizedStack *last;
+  uptr frames_symbolized;
+
+  AddressInfo *get_new_frame(uintptr_t addr) {
+    CHECK(last);
+    if (frames_symbolized > 0) {
+      SymbolizedStack *cur = SymbolizedStack::New(addr);
+      AddressInfo *info = &cur->info;
+      info->FillModuleInfo(first->info.module, first->info.module_offset);
+      last->next = cur;
+      last = cur;
+    }
+    CHECK_EQ(addr, first->info.address);
+    CHECK_EQ(addr, last->info.address);
+    return &last->info;
+  }
+};
+
+extern "C" {
+static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr,
+                                       const char *filename, int lineno,
+                                       const char *function) {
+  SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
+  if (function) {
+    AddressInfo *info = cdata->get_new_frame(addr);
+    info->function = DemangleAlloc(function, /*always_alloc*/ true);
+    if (filename)
+      info->file = internal_strdup(filename);
+    info->line = lineno;
+    cdata->frames_symbolized++;
+  }
+  return 0;
+}
+
+static void SymbolizeCodeCallback(void *vdata, uintptr_t addr,
+                                  const char *symname, uintptr_t, uintptr_t) {
+  SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
+  if (symname) {
+    AddressInfo *info = cdata->get_new_frame(addr);
+    info->function = DemangleAlloc(symname, /*always_alloc*/ true);
+    cdata->frames_symbolized++;
+  }
+}
+
+static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname,
+                                  uintptr_t symval, uintptr_t symsize) {
+  DataInfo *info = (DataInfo *)vdata;
+  if (symname && symval) {
+    info->name = DemangleAlloc(symname, /*always_alloc*/ true);
+    info->start = symval;
+    info->size = symsize;
+  }
+}
+
+static void ErrorCallback(void *, const char *, int) {}
+}  // extern "C"
+
+}  // namespace
+
+LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
+  // State created in backtrace_create_state is leaked.
+  void *state = (void *)(backtrace_create_state("/proc/self/exe", 0,
+                                                ErrorCallback, NULL));
+  if (!state)
+    return 0;
+  return new(*alloc) LibbacktraceSymbolizer(state);
+}
+
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+  SymbolizeCodeCallbackArg data;
+  data.first = stack;
+  data.last = stack;
+  data.frames_symbolized = 0;
+  backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,
+                   ErrorCallback, &data);
+  if (data.frames_symbolized > 0)
+    return true;
+  backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,
+                    ErrorCallback, &data);
+  return (data.frames_symbolized > 0);
+}
+
+bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeDataCallback,
+                     ErrorCallback, info);
+  return true;
+}
+
+#else  // SANITIZER_LIBBACKTRACE
+
+LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
+  return 0;
+}
+
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+  (void)state_;
+  return false;
+}
+
+bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  return false;
+}
+
+#endif  // SANITIZER_LIBBACKTRACE
+
+static char *DemangleAlloc(const char *name, bool always_alloc) {
+#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE
+  if (char *demangled = CplusV3Demangle(name))
+    return demangled;
+#endif
+  if (always_alloc)
+    return internal_strdup(name);
+  return 0;
+}
+
+const char *LibbacktraceSymbolizer::Demangle(const char *name) {
+  return DemangleAlloc(name, /*always_alloc*/ false);
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_symbolizer_libcdep.cc b/lsan/src/sanitizer_symbolizer_libcdep.cc
new file mode 100644 (file)
index 0000000..4264b5e
--- /dev/null
@@ -0,0 +1,426 @@
+//===-- sanitizer_symbolizer_libcdep.cc -----------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+const char *ExtractToken(const char *str, const char *delims, char **result) {
+  uptr prefix_len = internal_strcspn(str, delims);
+  *result = (char*)InternalAlloc(prefix_len + 1);
+  internal_memcpy(*result, str, prefix_len);
+  (*result)[prefix_len] = '\0';
+  const char *prefix_end = str + prefix_len;
+  if (*prefix_end != '\0') prefix_end++;
+  return prefix_end;
+}
+
+const char *ExtractInt(const char *str, const char *delims, int *result) {
+  char *buff;
+  const char *ret = ExtractToken(str, delims, &buff);
+  if (buff != 0) {
+    *result = (int)internal_atoll(buff);
+  }
+  InternalFree(buff);
+  return ret;
+}
+
+const char *ExtractUptr(const char *str, const char *delims, uptr *result) {
+  char *buff;
+  const char *ret = ExtractToken(str, delims, &buff);
+  if (buff != 0) {
+    *result = (uptr)internal_atoll(buff);
+  }
+  InternalFree(buff);
+  return ret;
+}
+
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+                                      char **result) {
+  const char *found_delimiter = internal_strstr(str, delimiter);
+  uptr prefix_len =
+      found_delimiter ? found_delimiter - str : internal_strlen(str);
+  *result = (char *)InternalAlloc(prefix_len + 1);
+  internal_memcpy(*result, str, prefix_len);
+  (*result)[prefix_len] = '\0';
+  const char *prefix_end = str + prefix_len;
+  if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter);
+  return prefix_end;
+}
+
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+  BlockingMutexLock l(&mu_);
+  const char *module_name;
+  uptr module_offset;
+  SymbolizedStack *res = SymbolizedStack::New(addr);
+  if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+    return res;
+  // Always fill data about module name and offset.
+  res->info.FillModuleInfo(module_name, module_offset);
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    if (tool->SymbolizePC(addr, res)) {
+      return res;
+    }
+  }
+  return res;
+}
+
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  BlockingMutexLock l(&mu_);
+  const char *module_name;
+  uptr module_offset;
+  if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+    return false;
+  info->Clear();
+  info->module = internal_strdup(module_name);
+  info->module_offset = module_offset;
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    if (tool->SymbolizeData(addr, info)) {
+      return true;
+    }
+  }
+  return true;
+}
+
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+                                             uptr *module_address) {
+  BlockingMutexLock l(&mu_);
+  const char *internal_module_name = nullptr;
+  if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,
+                                         module_address))
+    return false;
+
+  if (module_name)
+    *module_name = module_names_.GetOwnedCopy(internal_module_name);
+  return true;
+}
+
+void Symbolizer::Flush() {
+  BlockingMutexLock l(&mu_);
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    tool->Flush();
+  }
+}
+
+const char *Symbolizer::Demangle(const char *name) {
+  BlockingMutexLock l(&mu_);
+  for (auto iter = Iterator(&tools_); iter.hasNext();) {
+    auto *tool = iter.next();
+    SymbolizerScope sym_scope(this);
+    if (const char *demangled = tool->Demangle(name))
+      return demangled;
+  }
+  return PlatformDemangle(name);
+}
+
+void Symbolizer::PrepareForSandboxing() {
+  BlockingMutexLock l(&mu_);
+  PlatformPrepareForSandboxing();
+}
+
+bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
+                                                   const char **module_name,
+                                                   uptr *module_offset) {
+  LoadedModule *module = FindModuleForAddress(address);
+  if (module == 0)
+    return false;
+  *module_name = module->full_name();
+  *module_offset = address - module->base_address();
+  return true;
+}
+
+LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
+  bool modules_were_reloaded = false;
+  if (!modules_fresh_) {
+    for (uptr i = 0; i < n_modules_; i++)
+      modules_[i].clear();
+    n_modules_ =
+        GetListOfModules(modules_, kMaxNumberOfModules, /* filter */ nullptr);
+    CHECK_GT(n_modules_, 0);
+    CHECK_LT(n_modules_, kMaxNumberOfModules);
+    modules_fresh_ = true;
+    modules_were_reloaded = true;
+  }
+  for (uptr i = 0; i < n_modules_; i++) {
+    if (modules_[i].containsAddress(address)) {
+      return &modules_[i];
+    }
+  }
+  // Reload the modules and look up again, if we haven't tried it yet.
+  if (!modules_were_reloaded) {
+    // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
+    // It's too aggressive to reload the list of modules each time we fail
+    // to find a module for a given address.
+    modules_fresh_ = false;
+    return FindModuleForAddress(address);
+  }
+  return 0;
+}
+
+Symbolizer *Symbolizer::GetOrInit() {
+  SpinMutexLock l(&init_mu_);
+  if (symbolizer_)
+    return symbolizer_;
+  symbolizer_ = PlatformInit();
+  CHECK(symbolizer_);
+  return symbolizer_;
+}
+
+// For now we assume the following protocol:
+// For each request of the form
+//   <module_name> <module_offset>
+// passed to STDIN, external symbolizer prints to STDOUT response:
+//   <function_name>
+//   <file_name>:<line_number>:<column_number>
+//   <function_name>
+//   <file_name>:<line_number>:<column_number>
+//   ...
+//   <empty line>
+class LLVMSymbolizerProcess : public SymbolizerProcess {
+ public:
+  explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
+
+ private:
+  bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+    // Empty line marks the end of llvm-symbolizer output.
+    return length >= 2 && buffer[length - 1] == '\n' &&
+           buffer[length - 2] == '\n';
+  }
+
+  void GetArgV(const char *path_to_binary,
+               const char *(&argv)[kArgVMax]) const override {
+#if defined(__x86_64h__)
+    const char* const kSymbolizerArch = "--default-arch=x86_64h";
+#elif defined(__x86_64__)
+    const char* const kSymbolizerArch = "--default-arch=x86_64";
+#elif defined(__i386__)
+    const char* const kSymbolizerArch = "--default-arch=i386";
+#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__)
+    const char* const kSymbolizerArch = "--default-arch=powerpc64";
+#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
+    const char* const kSymbolizerArch = "--default-arch=powerpc64le";
+#else
+    const char* const kSymbolizerArch = "--default-arch=unknown";
+#endif
+
+    const char *const inline_flag = common_flags()->symbolize_inline_frames
+                                        ? "--inlining=true"
+                                        : "--inlining=false";
+    int i = 0;
+    argv[i++] = path_to_binary;
+    argv[i++] = inline_flag;
+    argv[i++] = kSymbolizerArch;
+    argv[i++] = nullptr;
+  }
+};
+
+LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
+    : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
+
+// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on
+// Windows, so extract tokens from the right hand side first. The column info is
+// also optional.
+static const char *ParseFileLineInfo(AddressInfo *info, const char *str) {
+  char *file_line_info = 0;
+  str = ExtractToken(str, "\n", &file_line_info);
+  CHECK(file_line_info);
+  // Parse the last :<int>, which must be there.
+  char *last_colon = internal_strrchr(file_line_info, ':');
+  CHECK(last_colon);
+  int line_or_column = internal_atoll(last_colon + 1);
+  // Truncate the string at the last colon and find the next-to-last colon.
+  *last_colon = '\0';
+  last_colon = internal_strrchr(file_line_info, ':');
+  if (last_colon && IsDigit(last_colon[1])) {
+    // If the second-to-last colon is followed by a digit, it must be the line
+    // number, and the previous parsed number was a column.
+    info->line = internal_atoll(last_colon + 1);
+    info->column = line_or_column;
+    *last_colon = '\0';
+  } else {
+    // Otherwise, we have line info but no column info.
+    info->line = line_or_column;
+    info->column = 0;
+  }
+  ExtractToken(file_line_info, "", &info->file);
+  InternalFree(file_line_info);
+  return str;
+}
+
+// Parses one or more two-line strings in the following format:
+//   <function_name>
+//   <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format.
+void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {
+  bool top_frame = true;
+  SymbolizedStack *last = res;
+  while (true) {
+    char *function_name = 0;
+    str = ExtractToken(str, "\n", &function_name);
+    CHECK(function_name);
+    if (function_name[0] == '\0') {
+      // There are no more frames.
+      InternalFree(function_name);
+      break;
+    }
+    SymbolizedStack *cur;
+    if (top_frame) {
+      cur = res;
+      top_frame = false;
+    } else {
+      cur = SymbolizedStack::New(res->info.address);
+      cur->info.FillModuleInfo(res->info.module, res->info.module_offset);
+      last->next = cur;
+      last = cur;
+    }
+
+    AddressInfo *info = &cur->info;
+    info->function = function_name;
+    str = ParseFileLineInfo(info, str);
+
+    // Functions and filenames can be "??", in which case we write 0
+    // to address info to mark that names are unknown.
+    if (0 == internal_strcmp(info->function, "??")) {
+      InternalFree(info->function);
+      info->function = 0;
+    }
+    if (0 == internal_strcmp(info->file, "??")) {
+      InternalFree(info->file);
+      info->file = 0;
+    }
+  }
+}
+
+// Parses a two-line string in the following format:
+//   <symbol_name>
+//   <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
+  str = ExtractToken(str, "\n", &info->name);
+  str = ExtractUptr(str, " ", &info->start);
+  str = ExtractUptr(str, "\n", &info->size);
+}
+
+bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+  if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module,
+                                    stack->info.module_offset)) {
+    ParseSymbolizePCOutput(buf, stack);
+    return true;
+  }
+  return false;
+}
+
+bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  if (const char *buf =
+          SendCommand(/*is_data*/ true, info->module, info->module_offset)) {
+    ParseSymbolizeDataOutput(buf, info);
+    info->start += (addr - info->module_offset); // Add the base address.
+    return true;
+  }
+  return false;
+}
+
+const char *LLVMSymbolizer::SendCommand(bool is_data, const char *module_name,
+                                        uptr module_offset) {
+  CHECK(module_name);
+  internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n",
+                    is_data ? "DATA " : "", module_name, module_offset);
+  return symbolizer_process_->SendCommand(buffer_);
+}
+
+SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
+    : path_(path),
+      input_fd_(kInvalidFd),
+      output_fd_(kInvalidFd),
+      times_restarted_(0),
+      failed_to_start_(false),
+      reported_invalid_path_(false),
+      use_forkpty_(use_forkpty) {
+  CHECK(path_);
+  CHECK_NE(path_[0], '\0');
+}
+
+const char *SymbolizerProcess::SendCommand(const char *command) {
+  for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
+    // Start or restart symbolizer if we failed to send command to it.
+    if (const char *res = SendCommandImpl(command))
+      return res;
+    Restart();
+  }
+  if (!failed_to_start_) {
+    Report("WARNING: Failed to use and restart external symbolizer!\n");
+    failed_to_start_ = true;
+  }
+  return 0;
+}
+
+const char *SymbolizerProcess::SendCommandImpl(const char *command) {
+  if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
+      return 0;
+  if (!WriteToSymbolizer(command, internal_strlen(command)))
+      return 0;
+  if (!ReadFromSymbolizer(buffer_, kBufferSize))
+      return 0;
+  return buffer_;
+}
+
+bool SymbolizerProcess::Restart() {
+  if (input_fd_ != kInvalidFd)
+    CloseFile(input_fd_);
+  if (output_fd_ != kInvalidFd)
+    CloseFile(output_fd_);
+  return StartSymbolizerSubprocess();
+}
+
+bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
+  if (max_length == 0)
+    return true;
+  uptr read_len = 0;
+  while (true) {
+    uptr just_read = 0;
+    bool success = ReadFromFile(input_fd_, buffer + read_len,
+                                max_length - read_len - 1, &just_read);
+    // We can't read 0 bytes, as we don't expect external symbolizer to close
+    // its stdout.
+    if (!success || just_read == 0) {
+      Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
+      return false;
+    }
+    read_len += just_read;
+    if (ReachedEndOfOutput(buffer, read_len))
+      break;
+  }
+  buffer[read_len] = '\0';
+  return true;
+}
+
+bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
+  if (length == 0)
+    return true;
+  uptr write_len = 0;
+  bool success = WriteToFile(output_fd_, buffer, length, &write_len);
+  if (!success || write_len != length) {
+    Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
+    return false;
+  }
+  return true;
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_symbolizer_mac.cc b/lsan/src/sanitizer_symbolizer_mac.cc
new file mode 100644 (file)
index 0000000..e65976c
--- /dev/null
@@ -0,0 +1,182 @@
+//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Implementation of Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_mac.h"
+#include "sanitizer_symbolizer_mac.h"
+
+namespace __sanitizer {
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util.h>
+
+bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+  Dl_info info;
+  int result = dladdr((const void *)addr, &info);
+  if (!result) return false;
+  const char *demangled = DemangleCXXABI(info.dli_sname);
+  stack->info.function = internal_strdup(demangled);
+  return true;
+}
+
+bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
+  Dl_info info;
+  int result = dladdr((const void *)addr, &info);
+  if (!result) return false;
+  const char *demangled = DemangleCXXABI(info.dli_sname);
+  datainfo->name = internal_strdup(demangled);
+  datainfo->start = (uptr)info.dli_saddr;
+  return true;
+}
+
+class AtosSymbolizerProcess : public SymbolizerProcess {
+ public:
+  explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
+      : SymbolizerProcess(path, /*use_forkpty*/ true) {
+    // Put the string command line argument in the object so that it outlives
+    // the call to GetArgV.
+    internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
+  }
+
+ private:
+  bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+    return (length >= 1 && buffer[length - 1] == '\n');
+  }
+
+  void GetArgV(const char *path_to_binary,
+               const char *(&argv)[kArgVMax]) const override {
+    int i = 0;
+    argv[i++] = path_to_binary;
+    argv[i++] = "-p";
+    argv[i++] = &pid_str_[0];
+    if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
+      // On Mavericks atos prints a deprecation warning which we suppress by
+      // passing -d. The warning isn't present on other OSX versions, even the
+      // newer ones.
+      argv[i++] = "-d";
+    }
+    argv[i++] = nullptr;
+  }
+
+  char pid_str_[16];
+};
+
+static const char *kAtosErrorMessages[] = {
+  "atos cannot examine process",
+  "unable to get permission to examine process",
+  "An admin user name and password is required",
+  "could not load inserted library",
+  "architecture mismatch between analysis process",
+};
+
+static bool IsAtosErrorMessage(const char *str) {
+  for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
+    if (internal_strstr(str, kAtosErrorMessages[i])) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
+                               char **out_module, char **out_file, uptr *line,
+                               uptr *start_address) {
+  // Trim ending newlines.
+  char *trim;
+  ExtractTokenUpToDelimiter(str, "\n", &trim);
+
+  // The line from `atos` is in one of these formats:
+  //   myfunction (in library.dylib) (sourcefile.c:17)
+  //   myfunction (in library.dylib) + 0x1fe
+  //   myfunction (in library.dylib) + 15
+  //   0xdeadbeef (in library.dylib) + 0x1fe
+  //   0xdeadbeef (in library.dylib) + 15
+  //   0xdeadbeef (in library.dylib)
+  //   0xdeadbeef
+
+  if (IsAtosErrorMessage(trim)) {
+    Report("atos returned an error: %s\n", trim);
+    InternalFree(trim);
+    return false;
+  }
+
+  const char *rest = trim;
+  char *symbol_name;
+  rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
+  if (internal_strncmp(symbol_name, "0x", 2) != 0)
+    *out_name = symbol_name;
+  else
+    InternalFree(symbol_name);
+  rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
+
+  if (rest[0] == '(') {
+    if (out_file) {
+      rest++;
+      rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
+      char *extracted_line_number;
+      rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
+      if (line) *line = (uptr)internal_atoll(extracted_line_number);
+      InternalFree(extracted_line_number);
+    }
+  } else if (rest[0] == '+') {
+    rest += 2;
+    uptr offset = internal_atoll(rest);
+    if (start_address) *start_address = addr - offset;
+  }
+
+  InternalFree(trim);
+  return true;
+}
+
+AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
+    : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
+
+bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+  if (!process_) return false;
+  char command[32];
+  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+  const char *buf = process_->SendCommand(command);
+  if (!buf) return false;
+  uptr line;
+  if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
+                          &stack->info.file, &line, nullptr)) {
+    process_ = nullptr;
+    return false;
+  }
+  stack->info.line = (int)line;
+  return true;
+}
+
+bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  if (!process_) return false;
+  char command[32];
+  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+  const char *buf = process_->SendCommand(command);
+  if (!buf) return false;
+  if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
+                          nullptr, &info->start)) {
+    process_ = nullptr;
+    return false;
+  }
+  return true;
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_MAC
diff --git a/lsan/src/sanitizer_symbolizer_posix_libcdep.cc b/lsan/src/sanitizer_symbolizer_posix_libcdep.cc
new file mode 100644 (file)
index 0000000..e4ff525
--- /dev/null
@@ -0,0 +1,468 @@
+//===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// POSIX-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_POSIX
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_symbolizer_internal.h"
+#include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if SANITIZER_MAC
+#include <util.h>  // for forkpty()
+#endif  // SANITIZER_MAC
+
+// C++ demangling function, as required by Itanium C++ ABI. This is weak,
+// because we do not require a C++ ABI library to be linked to a program
+// using sanitizers; if it's not present, we'll just use the mangled name.
+namespace __cxxabiv1 {
+  extern "C" SANITIZER_WEAK_ATTRIBUTE
+  char *__cxa_demangle(const char *mangled, char *buffer,
+                                  size_t *length, int *status);
+}
+
+namespace __sanitizer {
+
+// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
+const char *DemangleCXXABI(const char *name) {
+  // FIXME: __cxa_demangle aggressively insists on allocating memory.
+  // There's not much we can do about that, short of providing our
+  // own demangler (libc++abi's implementation could be adapted so that
+  // it does not allocate). For now, we just call it anyway, and we leak
+  // the returned value.
+  if (__cxxabiv1::__cxa_demangle)
+    if (const char *demangled_name =
+          __cxxabiv1::__cxa_demangle(name, 0, 0, 0))
+      return demangled_name;
+
+  return name;
+}
+
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+  if (!FileExists(path_)) {
+    if (!reported_invalid_path_) {
+      Report("WARNING: invalid path to external symbolizer!\n");
+      reported_invalid_path_ = true;
+    }
+    return false;
+  }
+
+  int pid;
+  if (use_forkpty_) {
+#if SANITIZER_MAC
+    fd_t fd = kInvalidFd;
+    // Use forkpty to disable buffering in the new terminal.
+    pid = forkpty(&fd, 0, 0, 0);
+    if (pid == -1) {
+      // forkpty() failed.
+      Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
+             errno);
+      return false;
+    } else if (pid == 0) {
+      // Child subprocess.
+      const char *argv[kArgVMax];
+      GetArgV(path_, argv);
+      execv(path_, const_cast<char **>(&argv[0]));
+      internal__exit(1);
+    }
+
+    // Continue execution in parent process.
+    input_fd_ = output_fd_ = fd;
+
+    // Disable echo in the new terminal, disable CR.
+    struct termios termflags;
+    tcgetattr(fd, &termflags);
+    termflags.c_oflag &= ~ONLCR;
+    termflags.c_lflag &= ~ECHO;
+    tcsetattr(fd, TCSANOW, &termflags);
+#else  // SANITIZER_MAC
+    UNIMPLEMENTED();
+#endif  // SANITIZER_MAC
+  } else {
+    int *infd = NULL;
+    int *outfd = NULL;
+    // The client program may close its stdin and/or stdout and/or stderr
+    // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
+    // In this case the communication between the forked processes may be
+    // broken if either the parent or the child tries to close or duplicate
+    // these descriptors. The loop below produces two pairs of file
+    // descriptors, each greater than 2 (stderr).
+    int sock_pair[5][2];
+    for (int i = 0; i < 5; i++) {
+      if (pipe(sock_pair[i]) == -1) {
+        for (int j = 0; j < i; j++) {
+          internal_close(sock_pair[j][0]);
+          internal_close(sock_pair[j][1]);
+        }
+        Report("WARNING: Can't create a socket pair to start "
+               "external symbolizer (errno: %d)\n", errno);
+        return false;
+      } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
+        if (infd == NULL) {
+          infd = sock_pair[i];
+        } else {
+          outfd = sock_pair[i];
+          for (int j = 0; j < i; j++) {
+            if (sock_pair[j] == infd) continue;
+            internal_close(sock_pair[j][0]);
+            internal_close(sock_pair[j][1]);
+          }
+          break;
+        }
+      }
+    }
+    CHECK(infd);
+    CHECK(outfd);
+
+    // Real fork() may call user callbacks registered with pthread_atfork().
+    pid = internal_fork();
+    if (pid == -1) {
+      // Fork() failed.
+      internal_close(infd[0]);
+      internal_close(infd[1]);
+      internal_close(outfd[0]);
+      internal_close(outfd[1]);
+      Report("WARNING: failed to fork external symbolizer "
+             " (errno: %d)\n", errno);
+      return false;
+    } else if (pid == 0) {
+      // Child subprocess.
+      internal_close(STDOUT_FILENO);
+      internal_close(STDIN_FILENO);
+      internal_dup2(outfd[0], STDIN_FILENO);
+      internal_dup2(infd[1], STDOUT_FILENO);
+      internal_close(outfd[0]);
+      internal_close(outfd[1]);
+      internal_close(infd[0]);
+      internal_close(infd[1]);
+      for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
+        internal_close(fd);
+      const char *argv[kArgVMax];
+      GetArgV(path_, argv);
+      execv(path_, const_cast<char **>(&argv[0]));
+      internal__exit(1);
+    }
+
+    // Continue execution in parent process.
+    internal_close(outfd[0]);
+    internal_close(infd[1]);
+    input_fd_ = infd[0];
+    output_fd_ = outfd[1];
+  }
+
+  // Check that symbolizer subprocess started successfully.
+  int pid_status;
+  SleepForMillis(kSymbolizerStartupTimeMillis);
+  int exited_pid = waitpid(pid, &pid_status, WNOHANG);
+  if (exited_pid != 0) {
+    // Either waitpid failed, or child has already exited.
+    Report("WARNING: external symbolizer didn't start up correctly!\n");
+    return false;
+  }
+
+  return true;
+}
+
+class Addr2LineProcess : public SymbolizerProcess {
+ public:
+  Addr2LineProcess(const char *path, const char *module_name)
+      : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}
+
+  const char *module_name() const { return module_name_; }
+
+ private:
+  void GetArgV(const char *path_to_binary,
+               const char *(&argv)[kArgVMax]) const override {
+    int i = 0;
+    argv[i++] = path_to_binary;
+    argv[i++] = "-iCfe";
+    argv[i++] = module_name_;
+    argv[i++] = nullptr;
+  }
+
+  bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
+
+  bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
+    if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
+      return false;
+    // We should cut out output_terminator_ at the end of given buffer,
+    // appended by addr2line to mark the end of its meaningful output.
+    // We cannot scan buffer from it's beginning, because it is legal for it
+    // to start with output_terminator_ in case given offset is invalid. So,
+    // scanning from second character.
+    char *garbage = internal_strstr(buffer + 1, output_terminator_);
+    // This should never be NULL since buffer must end up with
+    // output_terminator_.
+    CHECK(garbage);
+    // Trim the buffer.
+    garbage[0] = '\0';
+    return true;
+  }
+
+  const char *module_name_;  // Owned, leaked.
+  static const char output_terminator_[];
+};
+
+const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
+
+bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
+                                          uptr length) const {
+  const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
+  // Skip, if we read just kTerminatorLen bytes, because Addr2Line output
+  // should consist at least of two pairs of lines:
+  // 1. First one, corresponding to given offset to be symbolized
+  // (may be equal to output_terminator_, if offset is not valid).
+  // 2. Second one for output_terminator_, itself to mark the end of output.
+  if (length <= kTerminatorLen) return false;
+  // Addr2Line output should end up with output_terminator_.
+  return !internal_memcmp(buffer + length - kTerminatorLen,
+                          output_terminator_, kTerminatorLen);
+}
+
+class Addr2LinePool : public SymbolizerTool {
+ public:
+  explicit Addr2LinePool(const char *addr2line_path,
+                         LowLevelAllocator *allocator)
+      : addr2line_path_(addr2line_path), allocator_(allocator),
+        addr2line_pool_(16) {}
+
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+    if (const char *buf =
+            SendCommand(stack->info.module, stack->info.module_offset)) {
+      ParseSymbolizePCOutput(buf, stack);
+      return true;
+    }
+    return false;
+  }
+
+  bool SymbolizeData(uptr addr, DataInfo *info) override {
+    return false;
+  }
+
+ private:
+  const char *SendCommand(const char *module_name, uptr module_offset) {
+    Addr2LineProcess *addr2line = 0;
+    for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
+      if (0 ==
+          internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {
+        addr2line = addr2line_pool_[i];
+        break;
+      }
+    }
+    if (!addr2line) {
+      addr2line =
+          new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
+      addr2line_pool_.push_back(addr2line);
+    }
+    CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
+    char buffer[kBufferSize];
+    internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
+                      module_offset, dummy_address_);
+    return addr2line->SendCommand(buffer);
+  }
+
+  static const uptr kBufferSize = 64;
+  const char *addr2line_path_;
+  LowLevelAllocator *allocator_;
+  InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
+  static const uptr dummy_address_ =
+      FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
+};
+
+#if SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset,
+                                char *Buffer, int MaxLength);
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset,
+                                char *Buffer, int MaxLength);
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_symbolize_flush();
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
+                                   int MaxLength);
+}  // extern "C"
+
+class InternalSymbolizer : public SymbolizerTool {
+ public:
+  static InternalSymbolizer *get(LowLevelAllocator *alloc) {
+    if (__sanitizer_symbolize_code != 0 &&
+        __sanitizer_symbolize_data != 0) {
+      return new(*alloc) InternalSymbolizer();
+    }
+    return 0;
+  }
+
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+    bool result = __sanitizer_symbolize_code(
+        stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
+    if (result) ParseSymbolizePCOutput(buffer_, stack);
+    return result;
+  }
+
+  bool SymbolizeData(uptr addr, DataInfo *info) override {
+    bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
+                                             buffer_, kBufferSize);
+    if (result) {
+      ParseSymbolizeDataOutput(buffer_, info);
+      info->start += (addr - info->module_offset);  // Add the base address.
+    }
+    return result;
+  }
+
+  void Flush() override {
+    if (__sanitizer_symbolize_flush)
+      __sanitizer_symbolize_flush();
+  }
+
+  const char *Demangle(const char *name) override {
+    if (__sanitizer_symbolize_demangle) {
+      for (uptr res_length = 1024;
+           res_length <= InternalSizeClassMap::kMaxSize;) {
+        char *res_buff = static_cast<char*>(InternalAlloc(res_length));
+        uptr req_length =
+            __sanitizer_symbolize_demangle(name, res_buff, res_length);
+        if (req_length > res_length) {
+          res_length = req_length + 1;
+          InternalFree(res_buff);
+          continue;
+        }
+        return res_buff;
+      }
+    }
+    return name;
+  }
+
+ private:
+  InternalSymbolizer() { }
+
+  static const int kBufferSize = 16 * 1024;
+  static const int kMaxDemangledNameSize = 1024;
+  char buffer_[kBufferSize];
+};
+#else  // SANITIZER_SUPPORTS_WEAK_HOOKS
+
+class InternalSymbolizer : public SymbolizerTool {
+ public:
+  static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
+};
+
+#endif  // SANITIZER_SUPPORTS_WEAK_HOOKS
+
+const char *Symbolizer::PlatformDemangle(const char *name) {
+  return DemangleCXXABI(name);
+}
+
+void Symbolizer::PlatformPrepareForSandboxing() {}
+
+static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
+  const char *path = common_flags()->external_symbolizer_path;
+  const char *binary_name = path ? StripModuleName(path) : "";
+  if (path && path[0] == '\0') {
+    VReport(2, "External symbolizer is explicitly disabled.\n");
+    return nullptr;
+  } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
+    VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
+    return new(*allocator) LLVMSymbolizer(path, allocator);
+  } else if (!internal_strcmp(binary_name, "atos")) {
+#if SANITIZER_MAC
+    VReport(2, "Using atos at user-specified path: %s\n", path);
+    return new(*allocator) AtosSymbolizer(path, allocator);
+#else  // SANITIZER_MAC
+    Report("ERROR: Using `atos` is only supported on Darwin.\n");
+    Die();
+#endif  // SANITIZER_MAC
+  } else if (!internal_strcmp(binary_name, "addr2line")) {
+    VReport(2, "Using addr2line at user-specified path: %s\n", path);
+    return new(*allocator) Addr2LinePool(path, allocator);
+  } else if (path) {
+    Report("ERROR: External symbolizer path is set to '%s' which isn't "
+           "a known symbolizer. Please set the path to the llvm-symbolizer "
+           "binary or other known tool.\n", path);
+    Die();
+  }
+
+  // Otherwise symbolizer program is unknown, let's search $PATH
+  CHECK(path == nullptr);
+  if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
+    VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
+    return new(*allocator) LLVMSymbolizer(found_path, allocator);
+  }
+#if SANITIZER_MAC
+  if (const char *found_path = FindPathToBinary("atos")) {
+    VReport(2, "Using atos found at: %s\n", found_path);
+    return new(*allocator) AtosSymbolizer(found_path, allocator);
+  }
+#endif  // SANITIZER_MAC
+  if (common_flags()->allow_addr2line) {
+    if (const char *found_path = FindPathToBinary("addr2line")) {
+      VReport(2, "Using addr2line found at: %s\n", found_path);
+      return new(*allocator) Addr2LinePool(found_path, allocator);
+    }
+  }
+  return nullptr;
+}
+
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+                                  LowLevelAllocator *allocator) {
+  if (!common_flags()->symbolize) {
+    VReport(2, "Symbolizer is disabled.\n");
+    return;
+  }
+  if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
+    VReport(2, "Using internal symbolizer.\n");
+    list->push_back(tool);
+    return;
+  }
+  if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
+    VReport(2, "Using libbacktrace symbolizer.\n");
+    list->push_back(tool);
+    return;
+  }
+
+  if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+    list->push_back(tool);
+  }
+
+#if SANITIZER_MAC
+  VReport(2, "Using dladdr symbolizer.\n");
+  list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif  // SANITIZER_MAC
+
+  if (list->size() == 0) {
+    Report("WARNING: no internal or external symbolizer found.\n");
+  }
+}
+
+Symbolizer *Symbolizer::PlatformInit() {
+  IntrusiveList<SymbolizerTool> list;
+  list.clear();
+  ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+  return new(symbolizer_allocator_) Symbolizer(list);
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_POSIX
diff --git a/lsan/src/sanitizer_symbolizer_win.cc b/lsan/src/sanitizer_symbolizer_win.cc
new file mode 100644 (file)
index 0000000..dadb0ea
--- /dev/null
@@ -0,0 +1,282 @@
+//===-- sanitizer_symbolizer_win.cc ---------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Windows-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <dbghelp.h>
+#pragma comment(lib, "dbghelp.lib")
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+namespace {
+
+class WinSymbolizerTool : public SymbolizerTool {
+ public:
+  bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+  bool SymbolizeData(uptr addr, DataInfo *info) override {
+    return false;
+  }
+  const char *Demangle(const char *name) override;
+};
+
+bool is_dbghelp_initialized = false;
+
+bool TrySymInitialize() {
+  SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+  return SymInitialize(GetCurrentProcess(), 0, TRUE);
+  // FIXME: We don't call SymCleanup() on exit yet - should we?
+}
+
+// Initializes DbgHelp library, if it's not yet initialized. Calls to this
+// function should be synchronized with respect to other calls to DbgHelp API
+// (e.g. from WinSymbolizerTool).
+void InitializeDbgHelpIfNeeded() {
+  if (is_dbghelp_initialized)
+    return;
+  if (!TrySymInitialize()) {
+    // OK, maybe the client app has called SymInitialize already.
+    // That's a bit unfortunate for us as all the DbgHelp functions are
+    // single-threaded and we can't coordinate with the app.
+    // FIXME: Can we stop the other threads at this point?
+    // Anyways, we have to reconfigure stuff to make sure that SymInitialize
+    // has all the appropriate options set.
+    // Cross our fingers and reinitialize DbgHelp.
+    Report("*** WARNING: Failed to initialize DbgHelp!              ***\n");
+    Report("*** Most likely this means that the app is already      ***\n");
+    Report("*** using DbgHelp, possibly with incompatible flags.    ***\n");
+    Report("*** Due to technical reasons, symbolization might crash ***\n");
+    Report("*** or produce wrong results.                           ***\n");
+    SymCleanup(GetCurrentProcess());
+    TrySymInitialize();
+  }
+  is_dbghelp_initialized = true;
+
+  // When an executable is run from a location different from the one where it
+  // was originally built, we may not see the nearby PDB files.
+  // To work around this, let's append the directory of the main module
+  // to the symbol search path.  All the failures below are not fatal.
+  const size_t kSymPathSize = 2048;
+  static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
+  if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
+    Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
+    return;
+  }
+  size_t sz = wcslen(path_buffer);
+  if (sz) {
+    CHECK_EQ(0, wcscat_s(path_buffer, L";"));
+    sz++;
+  }
+  DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
+  if (res == 0 || res == MAX_PATH) {
+    Report("*** WARNING: Failed to getting the EXE directory ***\n");
+    return;
+  }
+  // Write the zero character in place of the last backslash to get the
+  // directory of the main module at the end of path_buffer.
+  wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
+  CHECK_NE(last_bslash, 0);
+  *last_bslash = L'\0';
+  if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
+    Report("*** WARNING: Failed to SymSetSearchPathW\n");
+    return;
+  }
+}
+
+}  // namespace
+
+bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
+  InitializeDbgHelpIfNeeded();
+
+  // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+  char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+  PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+  symbol->MaxNameLen = MAX_SYM_NAME;
+  DWORD64 offset = 0;
+  BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+                                 (DWORD64)addr, &offset, symbol);
+  if (!got_objname)
+    return false;
+
+  DWORD unused;
+  IMAGEHLP_LINE64 line_info;
+  line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+  BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
+                                           &unused, &line_info);
+  frame->info.function = internal_strdup(symbol->Name);
+  frame->info.function_offset = (uptr)offset;
+  if (got_fileline) {
+    frame->info.file = internal_strdup(line_info.FileName);
+    frame->info.line = line_info.LineNumber;
+  }
+  // Only consider this a successful symbolization attempt if we got file info.
+  // Otherwise, try llvm-symbolizer.
+  return got_fileline;
+}
+
+const char *WinSymbolizerTool::Demangle(const char *name) {
+  CHECK(is_dbghelp_initialized);
+  static char demangle_buffer[1000];
+  if (name[0] == '\01' &&
+      UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
+                           UNDNAME_NAME_ONLY))
+    return demangle_buffer;
+  else
+    return name;
+}
+
+const char *Symbolizer::PlatformDemangle(const char *name) {
+  return name;
+}
+
+void Symbolizer::PlatformPrepareForSandboxing() {
+  // Do nothing.
+}
+
+namespace {
+struct ScopedHandle {
+  ScopedHandle() : h_(nullptr) {}
+  explicit ScopedHandle(HANDLE h) : h_(h) {}
+  ~ScopedHandle() {
+    if (h_)
+      ::CloseHandle(h_);
+  }
+  HANDLE get() { return h_; }
+  HANDLE *receive() { return &h_; }
+  HANDLE release() {
+    HANDLE h = h_;
+    h_ = nullptr;
+    return h;
+  }
+  HANDLE h_;
+};
+} // namespace
+
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+  // Create inherited pipes for stdin and stdout.
+  ScopedHandle stdin_read, stdin_write;
+  ScopedHandle stdout_read, stdout_write;
+  SECURITY_ATTRIBUTES attrs;
+  attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+  attrs.bInheritHandle = TRUE;
+  attrs.lpSecurityDescriptor = nullptr;
+  if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) ||
+      !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) {
+    VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
+            SanitizerToolName, path_, GetLastError());
+    return false;
+  }
+
+  // Don't inherit the writing end of stdin or the reading end of stdout.
+  if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) ||
+      !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
+    VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
+            SanitizerToolName, path_, GetLastError());
+    return false;
+  }
+
+  // Compute the command line. Wrap double quotes around everything.
+  const char *argv[kArgVMax];
+  GetArgV(path_, argv);
+  InternalScopedString command_line(kMaxPathLength * 3);
+  for (int i = 0; argv[i]; i++) {
+    const char *arg = argv[i];
+    int arglen = internal_strlen(arg);
+    // Check that tool command lines are simple and that complete escaping is
+    // unnecessary.
+    CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
+    CHECK(!internal_strstr(arg, "\\\\") &&
+          "double backslashes in args unsupported");
+    CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
+          "args ending in backslash and empty args unsupported");
+    command_line.append("\"%s\" ", arg);
+  }
+  VReport(3, "Launching symbolizer command: %s\n", command_line.data());
+
+  // Launch llvm-symbolizer with stdin and stdout redirected.
+  STARTUPINFOA si;
+  memset(&si, 0, sizeof(si));
+  si.cb = sizeof(si);
+  si.dwFlags |= STARTF_USESTDHANDLES;
+  si.hStdInput = stdin_read.get();
+  si.hStdOutput = stdout_write.get();
+  PROCESS_INFORMATION pi;
+  memset(&pi, 0, sizeof(pi));
+  if (!CreateProcessA(path_,               // Executable
+                      command_line.data(), // Command line
+                      nullptr,             // Process handle not inheritable
+                      nullptr,             // Thread handle not inheritable
+                      TRUE,                // Set handle inheritance to TRUE
+                      0,                   // Creation flags
+                      nullptr,             // Use parent's environment block
+                      nullptr,             // Use parent's starting directory
+                      &si, &pi)) {
+    VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
+            SanitizerToolName, path_, GetLastError());
+    return false;
+  }
+
+  // Process creation succeeded, so transfer handle ownership into the fields.
+  input_fd_ = stdout_read.release();
+  output_fd_ = stdin_write.release();
+
+  // The llvm-symbolizer process is responsible for quitting itself when the
+  // stdin pipe is closed, so we don't need these handles. Close them to prevent
+  // leaks. If we ever want to try to kill the symbolizer process from the
+  // parent, we'll want to hang on to these handles.
+  CloseHandle(pi.hProcess);
+  CloseHandle(pi.hThread);
+  return true;
+}
+
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+                                  LowLevelAllocator *allocator) {
+  if (!common_flags()->symbolize) {
+    VReport(2, "Symbolizer is disabled.\n");
+    return;
+  }
+
+  // Add llvm-symbolizer in case the binary has dwarf.
+  const char *user_path = common_flags()->external_symbolizer_path;
+  const char *path =
+      user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe");
+  if (path) {
+    VReport(2, "Using llvm-symbolizer at %spath: %s\n",
+            user_path ? "user-specified " : "", path);
+    list->push_back(new(*allocator) LLVMSymbolizer(path, allocator));
+  } else {
+    if (user_path && user_path[0] == '\0') {
+      VReport(2, "External symbolizer is explicitly disabled.\n");
+    } else {
+      VReport(2, "External symbolizer is not present.\n");
+    }
+  }
+
+  // Add the dbghelp based symbolizer.
+  list->push_back(new(*allocator) WinSymbolizerTool());
+}
+
+Symbolizer *Symbolizer::PlatformInit() {
+  IntrusiveList<SymbolizerTool> list;
+  list.clear();
+  ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+
+  return new(symbolizer_allocator_) Symbolizer(list);
+}
+
+}  // namespace __sanitizer
+
+#endif  // _WIN32
diff --git a/lsan/src/sanitizer_thread_registry.cc b/lsan/src/sanitizer_thread_registry.cc
new file mode 100644 (file)
index 0000000..0e92da5
--- /dev/null
@@ -0,0 +1,313 @@
+//===-- sanitizer_thread_registry.cc --------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizer tools.
+//
+// General thread bookkeeping functionality.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_thread_registry.h"
+
+namespace __sanitizer {
+
+ThreadContextBase::ThreadContextBase(u32 tid)
+    : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
+      status(ThreadStatusInvalid),
+      detached(false), parent_tid(0), next(0) {
+  name[0] = '\0';
+}
+
+ThreadContextBase::~ThreadContextBase() {
+  // ThreadContextBase should never be deleted.
+  CHECK(0);
+}
+
+void ThreadContextBase::SetName(const char *new_name) {
+  name[0] = '\0';
+  if (new_name) {
+    internal_strncpy(name, new_name, sizeof(name));
+    name[sizeof(name) - 1] = '\0';
+  }
+}
+
+void ThreadContextBase::SetDead() {
+  CHECK(status == ThreadStatusRunning ||
+        status == ThreadStatusFinished);
+  status = ThreadStatusDead;
+  user_id = 0;
+  OnDead();
+}
+
+void ThreadContextBase::SetJoined(void *arg) {
+  // FIXME(dvyukov): print message and continue (it's user error).
+  CHECK_EQ(false, detached);
+  CHECK_EQ(ThreadStatusFinished, status);
+  status = ThreadStatusDead;
+  user_id = 0;
+  OnJoined(arg);
+}
+
+void ThreadContextBase::SetFinished() {
+  if (!detached)
+    status = ThreadStatusFinished;
+  OnFinished();
+}
+
+void ThreadContextBase::SetStarted(uptr _os_id, void *arg) {
+  status = ThreadStatusRunning;
+  os_id = _os_id;
+  OnStarted(arg);
+}
+
+void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
+                                   bool _detached, u32 _parent_tid, void *arg) {
+  status = ThreadStatusCreated;
+  user_id = _user_id;
+  unique_id = _unique_id;
+  detached = _detached;
+  // Parent tid makes no sense for the main thread.
+  if (tid != 0)
+    parent_tid = _parent_tid;
+  OnCreated(arg);
+}
+
+void ThreadContextBase::Reset() {
+  status = ThreadStatusInvalid;
+  SetName(0);
+  OnReset();
+}
+
+// ThreadRegistry implementation.
+
+const u32 ThreadRegistry::kUnknownTid = ~0U;
+
+ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
+                               u32 thread_quarantine_size, u32 max_reuse)
+    : context_factory_(factory),
+      max_threads_(max_threads),
+      thread_quarantine_size_(thread_quarantine_size),
+      max_reuse_(max_reuse),
+      mtx_(),
+      n_contexts_(0),
+      total_threads_(0),
+      alive_threads_(0),
+      max_alive_threads_(0),
+      running_threads_(0) {
+  threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),
+                                             "ThreadRegistry");
+  dead_threads_.clear();
+  invalid_threads_.clear();
+}
+
+void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
+                                        uptr *alive) {
+  BlockingMutexLock l(&mtx_);
+  if (total) *total = n_contexts_;
+  if (running) *running = running_threads_;
+  if (alive) *alive = alive_threads_;
+}
+
+uptr ThreadRegistry::GetMaxAliveThreads() {
+  BlockingMutexLock l(&mtx_);
+  return max_alive_threads_;
+}
+
+u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
+                                 void *arg) {
+  BlockingMutexLock l(&mtx_);
+  u32 tid = kUnknownTid;
+  ThreadContextBase *tctx = QuarantinePop();
+  if (tctx) {
+    tid = tctx->tid;
+  } else if (n_contexts_ < max_threads_) {
+    // Allocate new thread context and tid.
+    tid = n_contexts_++;
+    tctx = context_factory_(tid);
+    threads_[tid] = tctx;
+  } else {
+#ifndef SANITIZER_GO
+    Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
+           SanitizerToolName, max_threads_);
+#else
+    Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
+        " dying\n", max_threads_);
+#endif
+    Die();
+  }
+  CHECK_NE(tctx, 0);
+  CHECK_NE(tid, kUnknownTid);
+  CHECK_LT(tid, max_threads_);
+  CHECK_EQ(tctx->status, ThreadStatusInvalid);
+  alive_threads_++;
+  if (max_alive_threads_ < alive_threads_) {
+    max_alive_threads_++;
+    CHECK_EQ(alive_threads_, max_alive_threads_);
+  }
+  tctx->SetCreated(user_id, total_threads_++, detached,
+                   parent_tid, arg);
+  return tid;
+}
+
+void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
+                                                    void *arg) {
+  CheckLocked();
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx == 0)
+      continue;
+    cb(tctx, arg);
+  }
+}
+
+u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
+  BlockingMutexLock l(&mtx_);
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx != 0 && cb(tctx, arg))
+      return tctx->tid;
+  }
+  return kUnknownTid;
+}
+
+ThreadContextBase *
+ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
+  CheckLocked();
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx != 0 && cb(tctx, arg))
+      return tctx;
+  }
+  return 0;
+}
+
+ThreadContextBase *
+ThreadRegistry::FindThreadContext(FindThreadCallback cb, void *arg) {
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx != 0 && cb(tctx, arg))
+      return tctx;
+  }
+  return 0;
+}
+
+static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
+                                            void *arg) {
+  return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
+      tctx->status != ThreadStatusDead);
+}
+
+ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(uptr os_id) {
+  return FindThreadContextLocked(FindThreadContextByOsIdCallback,
+                                 (void *)os_id);
+}
+
+ThreadContextBase *ThreadRegistry::FindThreadContextByOsID(uptr os_id) {
+  return FindThreadContext(FindThreadContextByOsIdCallback, (void *)os_id);
+}
+
+void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(ThreadStatusRunning, tctx->status);
+  tctx->SetName(name);
+}
+
+void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
+  BlockingMutexLock l(&mtx_);
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx != 0 && tctx->user_id == user_id &&
+        tctx->status != ThreadStatusInvalid) {
+      tctx->SetName(name);
+      return;
+    }
+  }
+}
+
+void ThreadRegistry::DetachThread(u32 tid, void *arg) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  if (tctx->status == ThreadStatusInvalid) {
+    Report("%s: Detach of non-existent thread\n", SanitizerToolName);
+    return;
+  }
+  tctx->OnDetached(arg);
+  if (tctx->status == ThreadStatusFinished) {
+    tctx->SetDead();
+    QuarantinePush(tctx);
+  } else {
+    tctx->detached = true;
+  }
+}
+
+void ThreadRegistry::JoinThread(u32 tid, void *arg) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  if (tctx->status == ThreadStatusInvalid) {
+    Report("%s: Join of non-existent thread\n", SanitizerToolName);
+    return;
+  }
+  tctx->SetJoined(arg);
+  QuarantinePush(tctx);
+}
+
+void ThreadRegistry::FinishThread(u32 tid) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_GT(alive_threads_, 0);
+  alive_threads_--;
+  CHECK_GT(running_threads_, 0);
+  running_threads_--;
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(ThreadStatusRunning, tctx->status);
+  tctx->SetFinished();
+  if (tctx->detached) {
+    tctx->SetDead();
+    QuarantinePush(tctx);
+  }
+}
+
+void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) {
+  BlockingMutexLock l(&mtx_);
+  running_threads_++;
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(ThreadStatusCreated, tctx->status);
+  tctx->SetStarted(os_id, arg);
+}
+
+void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {
+  dead_threads_.push_back(tctx);
+  if (dead_threads_.size() <= thread_quarantine_size_)
+    return;
+  tctx = dead_threads_.front();
+  dead_threads_.pop_front();
+  CHECK_EQ(tctx->status, ThreadStatusDead);
+  tctx->Reset();
+  tctx->reuse_count++;
+  if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_)
+    return;
+  invalid_threads_.push_back(tctx);
+}
+
+ThreadContextBase *ThreadRegistry::QuarantinePop() {
+  if (invalid_threads_.size() == 0)
+    return 0;
+  ThreadContextBase *tctx = invalid_threads_.front();
+  invalid_threads_.pop_front();
+  return tctx;
+}
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_tls_get_addr.cc b/lsan/src/sanitizer_tls_get_addr.cc
new file mode 100644 (file)
index 0000000..2b7625c
--- /dev/null
@@ -0,0 +1,144 @@
+//===-- sanitizer_tls_get_addr.cc -----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle the __tls_get_addr call.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_tls_get_addr.h"
+
+#include "sanitizer_flags.h"
+#include "sanitizer_platform_interceptors.h"
+
+namespace __sanitizer {
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+// The actual parameter that comes to __tls_get_addr
+// is a pointer to a struct with two words in it:
+struct TlsGetAddrParam {
+  uptr dso_id;
+  uptr offset;
+};
+
+// Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
+// which has such header.
+struct Glibc_2_19_tls_header {
+  uptr size;
+  uptr start;
+};
+
+// This must be static TLS
+//__attribute__((tls_model("initial-exec")))
+static __thread DTLS dtls;
+
+// Make sure we properly destroy the DTLS objects:
+// this counter should never get too large.
+static atomic_uintptr_t number_of_live_dtls;
+
+static const uptr kDestroyedThread = -1;
+
+static inline void DTLS_Deallocate(DTLS::DTV *dtv, uptr size) {
+  if (!size) return;
+  VPrintf(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv, size);
+  UnmapOrDie(dtv, size * sizeof(DTLS::DTV));
+  atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed);
+}
+
+static inline void DTLS_Resize(uptr new_size) {
+  if (dtls.dtv_size >= new_size) return;
+  new_size = RoundUpToPowerOfTwo(new_size);
+  new_size = Max(new_size, 4096UL / sizeof(DTLS::DTV));
+  DTLS::DTV *new_dtv =
+      (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize");
+  uptr num_live_dtls =
+      atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed);
+  VPrintf(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls, num_live_dtls);
+  CHECK_LT(num_live_dtls, 1 << 20);
+  uptr old_dtv_size = dtls.dtv_size;
+  DTLS::DTV *old_dtv = dtls.dtv;
+  if (old_dtv_size)
+    internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV));
+  dtls.dtv = new_dtv;
+  dtls.dtv_size = new_size;
+  if (old_dtv_size)
+    DTLS_Deallocate(old_dtv, old_dtv_size);
+}
+
+void DTLS_Destroy() {
+  if (!common_flags()->intercept_tls_get_addr) return;
+  VPrintf(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls, dtls.dtv_size);
+  uptr s = dtls.dtv_size;
+  dtls.dtv_size = kDestroyedThread;  // Do this before unmap for AS-safety.
+  DTLS_Deallocate(dtls.dtv, s);
+}
+
+#if defined(__powerpc64__)
+// This is glibc's TLS_DTV_OFFSET:
+// "Dynamic thread vector pointers point 0x8000 past the start of each
+//  TLS block."
+static const uptr kDtvOffset = 0x8000;
+#else
+static const uptr kDtvOffset = 0;
+#endif
+
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
+                                uptr static_tls_begin, uptr static_tls_end) {
+  if (!common_flags()->intercept_tls_get_addr) return 0;
+  TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void);
+  uptr dso_id = arg->dso_id;
+  if (dtls.dtv_size == kDestroyedThread) return 0;
+  DTLS_Resize(dso_id + 1);
+  if (dtls.dtv[dso_id].beg) return 0;
+  uptr tls_size = 0;
+  uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
+  VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
+             "num_live_dtls %zd\n",
+          arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg,
+          atomic_load(&number_of_live_dtls, memory_order_relaxed));
+  if (dtls.last_memalign_ptr == tls_beg) {
+    tls_size = dtls.last_memalign_size;
+    VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
+        tls_beg, tls_size);
+  } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
+    // This is the static TLS block which was initialized / unpoisoned at thread
+    // creation.
+    VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg);
+    tls_size = 0;
+  } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
+    // We may want to check gnu_get_libc_version().
+    Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
+    tls_size = header->size;
+    tls_beg = header->start;
+    VPrintf(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n",
+        tls_beg, tls_size);
+  } else {
+    VPrintf(2, "__tls_get_addr: Can't guess glibc version\n");
+    // This may happen inside the DTOR of main thread, so just ignore it.
+    tls_size = 0;
+  }
+  dtls.dtv[dso_id].beg = tls_beg;
+  dtls.dtv[dso_id].size = tls_size;
+  return dtls.dtv + dso_id;
+}
+
+void DTLS_on_libc_memalign(void *ptr, uptr size) {
+  if (!common_flags()->intercept_tls_get_addr) return;
+  VPrintf(2, "DTLS_on_libc_memalign: %p %p\n", ptr, size);
+  dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr);
+  dtls.last_memalign_size = size;
+}
+
+DTLS *DTLS_Get() { return &dtls; }
+
+#else
+void DTLS_on_libc_memalign(void *ptr, uptr size) {}
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res) { return 0; }
+DTLS *DTLS_Get() { return 0; }
+void DTLS_Destroy() {}
+#endif  // SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+}  // namespace __sanitizer
diff --git a/lsan/src/sanitizer_unwind_linux_libcdep.cc b/lsan/src/sanitizer_unwind_linux_libcdep.cc
new file mode 100644 (file)
index 0000000..408c21c
--- /dev/null
@@ -0,0 +1,156 @@
+//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the unwind.h-based (aka "slow") stack unwinding routines
+// available to the tools on Linux, Android, and FreeBSD.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#include "sanitizer_common.h"
+#include "sanitizer_stacktrace.h"
+
+#if SANITIZER_ANDROID
+#include <dlfcn.h>  // for dlopen()
+#endif
+
+#if SANITIZER_FREEBSD
+#define _GNU_SOURCE  // to declare _Unwind_Backtrace() from <unwind.h>
+#endif
+#include <unwind.h>
+
+namespace __sanitizer {
+
+//------------------------- SlowUnwindStack -----------------------------------
+
+typedef struct {
+  uptr absolute_pc;
+  uptr stack_top;
+  uptr stack_size;
+} backtrace_frame_t;
+
+extern "C" {
+typedef void *(*acquire_my_map_info_list_func)();
+typedef void (*release_my_map_info_list_func)(void *map);
+typedef sptr (*unwind_backtrace_signal_arch_func)(
+    void *siginfo, void *sigcontext, void *map_info_list,
+    backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
+acquire_my_map_info_list_func acquire_my_map_info_list;
+release_my_map_info_list_func release_my_map_info_list;
+unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
+} // extern "C"
+
+#if SANITIZER_ANDROID
+void SanitizerInitializeUnwinder() {
+  void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
+  if (!p) {
+    VReport(1,
+            "Failed to open libcorkscrew.so. You may see broken stack traces "
+            "in SEGV reports.");
+    return;
+  }
+  acquire_my_map_info_list =
+      (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
+  release_my_map_info_list =
+      (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
+  unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
+      p, "unwind_backtrace_signal_arch");
+  if (!acquire_my_map_info_list || !release_my_map_info_list ||
+      !unwind_backtrace_signal_arch) {
+    VReport(1,
+            "Failed to find one of the required symbols in libcorkscrew.so. "
+            "You may see broken stack traces in SEGV reports.");
+    acquire_my_map_info_list = 0;
+    unwind_backtrace_signal_arch = 0;
+    release_my_map_info_list = 0;
+  }
+}
+#endif
+
+#ifdef __arm__
+#define UNWIND_STOP _URC_END_OF_STACK
+#define UNWIND_CONTINUE _URC_NO_REASON
+#else
+#define UNWIND_STOP _URC_NORMAL_STOP
+#define UNWIND_CONTINUE _URC_NO_REASON
+#endif
+
+uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
+#if defined(__arm__) && !SANITIZER_MAC
+  uptr val;
+  _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
+      15 /* r15 = PC */, _UVRSD_UINT32, &val);
+  CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
+  // Clear the Thumb bit.
+  return val & ~(uptr)1;
+#else
+  return _Unwind_GetIP(ctx);
+#endif
+}
+
+struct UnwindTraceArg {
+  BufferedStackTrace *stack;
+  u32 max_depth;
+};
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+  UnwindTraceArg *arg = (UnwindTraceArg*)param;
+  CHECK_LT(arg->stack->size, arg->max_depth);
+  uptr pc = Unwind_GetIP(ctx);
+  arg->stack->trace_buffer[arg->stack->size++] = pc;
+  if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
+  return UNWIND_CONTINUE;
+}
+
+void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
+  CHECK_GE(max_depth, 2);
+  size = 0;
+  UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
+  _Unwind_Backtrace(Unwind_Trace, &arg);
+  // We need to pop a few frames so that pc is on top.
+  uptr to_pop = LocatePcInTrace(pc);
+  // trace_buffer[0] belongs to the current function so we always pop it,
+  // unless there is only 1 frame in the stack trace (1 frame is always better
+  // than 0!).
+  // 1-frame stacks don't normally happen, but this depends on the actual
+  // unwinder implementation (libgcc, libunwind, etc) which is outside of our
+  // control.
+  if (to_pop == 0 && size > 1)
+    to_pop = 1;
+  PopStackFrames(to_pop);
+  trace_buffer[0] = pc;
+}
+
+void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
+                                                    u32 max_depth) {
+  CHECK_GE(max_depth, 2);
+  if (!unwind_backtrace_signal_arch) {
+    SlowUnwindStack(pc, max_depth);
+    return;
+  }
+
+  void *map = acquire_my_map_info_list();
+  CHECK(map);
+  InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
+  // siginfo argument appears to be unused.
+  sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
+                                          frames.data(),
+                                          /* ignore_depth */ 0, max_depth);
+  release_my_map_info_list(map);
+  if (res < 0) return;
+  CHECK_LE((uptr)res, kStackTraceMax);
+
+  size = 0;
+  // +2 compensate for libcorkscrew unwinder returning addresses of call
+  // instructions instead of raw return addresses.
+  for (sptr i = 0; i < res; ++i)
+    trace_buffer[size++] = frames[i].absolute_pc + 2;
+}
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lsan/src/sanitizer_win.cc b/lsan/src/sanitizer_win.cc
new file mode 100644 (file)
index 0000000..0f6c506
--- /dev/null
@@ -0,0 +1,769 @@
+//===-- sanitizer_win.cc --------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements windows-specific functions from
+// sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#include <windows.h>
+#include <dbghelp.h>
+#include <io.h>
+#include <psapi.h>
+#include <stdlib.h>
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace.h"
+
+namespace __sanitizer {
+
+#include "sanitizer_syscall_generic.inc"
+
+// --------------------- sanitizer_common.h
+uptr GetPageSize() {
+  // FIXME: there is an API for getting the system page size (GetSystemInfo or
+  // GetNativeSystemInfo), but if we use it here we get test failures elsewhere.
+  return 1U << 14;
+}
+
+uptr GetMmapGranularity() {
+  return 1U << 16;  // FIXME: is this configurable?
+}
+
+uptr GetMaxVirtualAddress() {
+  SYSTEM_INFO si;
+  GetSystemInfo(&si);
+  return (uptr)si.lpMaximumApplicationAddress;
+}
+
+bool FileExists(const char *filename) {
+  return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES;
+}
+
+uptr internal_getpid() {
+  return GetProcessId(GetCurrentProcess());
+}
+
+// In contrast to POSIX, on Windows GetCurrentThreadId()
+// returns a system-unique identifier.
+uptr GetTid() {
+  return GetCurrentThreadId();
+}
+
+uptr GetThreadSelf() {
+  return GetTid();
+}
+
+#if !SANITIZER_GO
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+                                uptr *stack_bottom) {
+  CHECK(stack_top);
+  CHECK(stack_bottom);
+  MEMORY_BASIC_INFORMATION mbi;
+  CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0);
+  // FIXME: is it possible for the stack to not be a single allocation?
+  // Are these values what ASan expects to get (reserved, not committed;
+  // including stack guard page) ?
+  *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
+  *stack_bottom = (uptr)mbi.AllocationBase;
+}
+#endif  // #if !SANITIZER_GO
+
+void *MmapOrDie(uptr size, const char *mem_type) {
+  void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+  if (rv == 0)
+    ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
+  return rv;
+}
+
+void UnmapOrDie(void *addr, uptr size) {
+  if (!size || !addr)
+    return;
+
+  if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
+    Report("ERROR: %s failed to "
+           "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
+           SanitizerToolName, size, size, addr, GetLastError());
+    CHECK("unable to unmap" && 0);
+  }
+}
+
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
+  // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
+  // but on Win64 it does.
+  (void)name; // unsupported
+  void *p = VirtualAlloc((LPVOID)fixed_addr, size,
+      MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+  if (p == 0)
+    Report("ERROR: %s failed to "
+           "allocate %p (%zd) bytes at %p (error code: %d)\n",
+           SanitizerToolName, size, size, fixed_addr, GetLastError());
+  return p;
+}
+
+void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
+  return MmapFixedNoReserve(fixed_addr, size);
+}
+
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
+  // FIXME: make this really NoReserve?
+  return MmapOrDie(size, mem_type);
+}
+
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+  (void)name; // unsupported
+  void *res = VirtualAlloc((LPVOID)fixed_addr, size,
+                           MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+  if (res == 0)
+    Report("WARNING: %s failed to "
+           "mprotect %p (%zd) bytes at %p (error code: %d)\n",
+           SanitizerToolName, size, size, fixed_addr, GetLastError());
+  return res;
+}
+
+bool MprotectNoAccess(uptr addr, uptr size) {
+  DWORD old_protection;
+  return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection);
+}
+
+
+void FlushUnneededShadowMemory(uptr addr, uptr size) {
+  // This is almost useless on 32-bits.
+  // FIXME: add madvise-analog when we move to 64-bits.
+}
+
+void NoHugePagesInRegion(uptr addr, uptr size) {
+  // FIXME: probably similar to FlushUnneededShadowMemory.
+}
+
+void DontDumpShadowMemory(uptr addr, uptr length) {
+  // This is almost useless on 32-bits.
+  // FIXME: add madvise-analog when we move to 64-bits.
+}
+
+bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
+  MEMORY_BASIC_INFORMATION mbi;
+  CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi)));
+  return mbi.Protect == PAGE_NOACCESS &&
+         (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end;
+}
+
+void *MapFileToMemory(const char *file_name, uptr *buff_size) {
+  UNIMPLEMENTED();
+}
+
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
+  UNIMPLEMENTED();
+}
+
+static const int kMaxEnvNameLength = 128;
+static const DWORD kMaxEnvValueLength = 32767;
+
+namespace {
+
+struct EnvVariable {
+  char name[kMaxEnvNameLength];
+  char value[kMaxEnvValueLength];
+};
+
+}  // namespace
+
+static const int kEnvVariables = 5;
+static EnvVariable env_vars[kEnvVariables];
+static int num_env_vars;
+
+const char *GetEnv(const char *name) {
+  // Note: this implementation caches the values of the environment variables
+  // and limits their quantity.
+  for (int i = 0; i < num_env_vars; i++) {
+    if (0 == internal_strcmp(name, env_vars[i].name))
+      return env_vars[i].value;
+  }
+  CHECK_LT(num_env_vars, kEnvVariables);
+  DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value,
+                                     kMaxEnvValueLength);
+  if (rv > 0 && rv < kMaxEnvValueLength) {
+    CHECK_LT(internal_strlen(name), kMaxEnvNameLength);
+    internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength);
+    num_env_vars++;
+    return env_vars[num_env_vars - 1].value;
+  }
+  return 0;
+}
+
+const char *GetPwd() {
+  UNIMPLEMENTED();
+}
+
+u32 GetUid() {
+  UNIMPLEMENTED();
+}
+
+namespace {
+struct ModuleInfo {
+  const char *filepath;
+  uptr base_address;
+  uptr end_address;
+};
+
+#ifndef SANITIZER_GO
+int CompareModulesBase(const void *pl, const void *pr) {
+  const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr;
+  if (l->base_address < r->base_address)
+    return -1;
+  return l->base_address > r->base_address;
+}
+#endif
+}  // namespace
+
+#ifndef SANITIZER_GO
+void DumpProcessMap() {
+  Report("Dumping process modules:\n");
+  InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
+  uptr num_modules =
+      GetListOfModules(modules.data(), kMaxNumberOfModules, nullptr);
+
+  InternalScopedBuffer<ModuleInfo> module_infos(num_modules);
+  for (size_t i = 0; i < num_modules; ++i) {
+    module_infos[i].filepath = modules[i].full_name();
+    module_infos[i].base_address = modules[i].base_address();
+    module_infos[i].end_address = modules[i].ranges().next()->end;
+  }
+  qsort(module_infos.data(), num_modules, sizeof(ModuleInfo),
+        CompareModulesBase);
+
+  for (size_t i = 0; i < num_modules; ++i) {
+    const ModuleInfo &mi = module_infos[i];
+    if (mi.end_address != 0) {
+      Printf("\t%p-%p %s\n", mi.base_address, mi.end_address,
+             mi.filepath[0] ? mi.filepath : "[no name]");
+    } else if (mi.filepath[0]) {
+      Printf("\t??\?-??? %s\n", mi.filepath);
+    } else {
+      Printf("\t???\n");
+    }
+  }
+}
+#endif
+
+void DisableCoreDumperIfNecessary() {
+  // Do nothing.
+}
+
+void ReExec() {
+  UNIMPLEMENTED();
+}
+
+void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+#if !SANITIZER_GO
+  CovPrepareForSandboxing(args);
+#endif
+}
+
+bool StackSizeIsUnlimited() {
+  UNIMPLEMENTED();
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+  UNIMPLEMENTED();
+}
+
+bool AddressSpaceIsUnlimited() {
+  UNIMPLEMENTED();
+}
+
+void SetAddressSpaceUnlimited() {
+  UNIMPLEMENTED();
+}
+
+bool IsPathSeparator(const char c) {
+  return c == '\\' || c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+  UNIMPLEMENTED();
+}
+
+void SleepForSeconds(int seconds) {
+  Sleep(seconds * 1000);
+}
+
+void SleepForMillis(int millis) {
+  Sleep(millis);
+}
+
+u64 NanoTime() {
+  return 0;
+}
+
+void Abort() {
+  if (::IsDebuggerPresent())
+    __debugbreak();
+  internal__exit(3);
+}
+
+// Read the file to extract the ImageBase field from the PE header. If ASLR is
+// disabled and this virtual address is available, the loader will typically
+// load the image at this address. Therefore, we call it the preferred base. Any
+// addresses in the DWARF typically assume that the object has been loaded at
+// this address.
+static uptr GetPreferredBase(const char *modname) {
+  fd_t fd = OpenFile(modname, RdOnly, nullptr);
+  if (fd == kInvalidFd)
+    return 0;
+  FileCloser closer(fd);
+
+  // Read just the DOS header.
+  IMAGE_DOS_HEADER dos_header;
+  uptr bytes_read;
+  if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) ||
+      bytes_read != sizeof(dos_header))
+    return 0;
+
+  // The file should start with the right signature.
+  if (dos_header.e_magic != IMAGE_DOS_SIGNATURE)
+    return 0;
+
+  // The layout at e_lfanew is:
+  // "PE\0\0"
+  // IMAGE_FILE_HEADER
+  // IMAGE_OPTIONAL_HEADER
+  // Seek to e_lfanew and read all that data.
+  char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)];
+  if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) ==
+      INVALID_SET_FILE_POINTER)
+    return 0;
+  if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) ||
+      bytes_read != sizeof(buf))
+    return 0;
+
+  // Check for "PE\0\0" before the PE header.
+  char *pe_sig = &buf[0];
+  if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0)
+    return 0;
+
+  // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted.
+  IMAGE_OPTIONAL_HEADER *pe_header =
+      (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER));
+
+  // Check for more magic in the PE header.
+  if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+    return 0;
+
+  // Finally, return the ImageBase.
+  return (uptr)pe_header->ImageBase;
+}
+
+#ifndef SANITIZER_GO
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
+                      string_predicate_t filter) {
+  HANDLE cur_process = GetCurrentProcess();
+
+  // Query the list of modules.  Start by assuming there are no more than 256
+  // modules and retry if that's not sufficient.
+  HMODULE *hmodules = 0;
+  uptr modules_buffer_size = sizeof(HMODULE) * 256;
+  DWORD bytes_required;
+  while (!hmodules) {
+    hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__);
+    CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size,
+                             &bytes_required));
+    if (bytes_required > modules_buffer_size) {
+      // Either there turned out to be more than 256 hmodules, or new hmodules
+      // could have loaded since the last try.  Retry.
+      UnmapOrDie(hmodules, modules_buffer_size);
+      hmodules = 0;
+      modules_buffer_size = bytes_required;
+    }
+  }
+
+  // |num_modules| is the number of modules actually present,
+  // |count| is the number of modules we return.
+  size_t nun_modules = bytes_required / sizeof(HMODULE),
+         count = 0;
+  for (size_t i = 0; i < nun_modules && count < max_modules; ++i) {
+    HMODULE handle = hmodules[i];
+    MODULEINFO mi;
+    if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi)))
+      continue;
+
+    // Get the UTF-16 path and convert to UTF-8.
+    wchar_t modname_utf16[kMaxPathLength];
+    int modname_utf16_len =
+        GetModuleFileNameW(handle, modname_utf16, kMaxPathLength);
+    if (modname_utf16_len == 0)
+      modname_utf16[0] = '\0';
+    char module_name[kMaxPathLength];
+    int module_name_len =
+        ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1,
+                              &module_name[0], kMaxPathLength, NULL, NULL);
+    module_name[module_name_len] = '\0';
+
+    if (filter && !filter(module_name))
+      continue;
+
+    uptr base_address = (uptr)mi.lpBaseOfDll;
+    uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage;
+
+    // Adjust the base address of the module so that we get a VA instead of an
+    // RVA when computing the module offset. This helps llvm-symbolizer find the
+    // right DWARF CU. In the common case that the image is loaded at it's
+    // preferred address, we will now print normal virtual addresses.
+    uptr preferred_base = GetPreferredBase(&module_name[0]);
+    uptr adjusted_base = base_address - preferred_base;
+
+    LoadedModule *cur_module = &modules[count];
+    cur_module->set(module_name, adjusted_base);
+    // We add the whole module as one single address range.
+    cur_module->addAddressRange(base_address, end_address, /*executable*/ true);
+    count++;
+  }
+  UnmapOrDie(hmodules, modules_buffer_size);
+
+  return count;
+};
+
+// We can't use atexit() directly at __asan_init time as the CRT is not fully
+// initialized at this point.  Place the functions into a vector and use
+// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).
+InternalMmapVectorNoCtor<void (*)(void)> atexit_functions;
+
+int Atexit(void (*function)(void)) {
+  atexit_functions.push_back(function);
+  return 0;
+}
+
+static int RunAtexit() {
+  int ret = 0;
+  for (uptr i = 0; i < atexit_functions.size(); ++i) {
+    ret |= atexit(atexit_functions[i]);
+  }
+  return ret;
+}
+
+#pragma section(".CRT$XID", long, read)  // NOLINT
+__declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit;
+#endif
+
+// ------------------ sanitizer_libc.h
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) {
+  fd_t res;
+  if (mode == RdOnly) {
+    res = CreateFile(filename, GENERIC_READ,
+                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                     nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+  } else if (mode == WrOnly) {
+    res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
+                     FILE_ATTRIBUTE_NORMAL, nullptr);
+  } else {
+    UNIMPLEMENTED();
+  }
+  CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd);
+  CHECK(res != kStderrFd || kStderrFd == kInvalidFd);
+  if (res == kInvalidFd && last_error)
+    *last_error = GetLastError();
+  return res;
+}
+
+void CloseFile(fd_t fd) {
+  CloseHandle(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+                  error_t *error_p) {
+  CHECK(fd != kInvalidFd);
+
+  // bytes_read can't be passed directly to ReadFile:
+  // uptr is unsigned long long on 64-bit Windows.
+  unsigned long num_read_long;
+
+  bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr);
+  if (!success && error_p)
+    *error_p = GetLastError();
+  if (bytes_read)
+    *bytes_read = num_read_long;
+  return success;
+}
+
+bool SupportsColoredOutput(fd_t fd) {
+  // FIXME: support colored output.
+  return false;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+                 error_t *error_p) {
+  CHECK(fd != kInvalidFd);
+
+  // Handle null optional parameters.
+  error_t dummy_error;
+  error_p = error_p ? error_p : &dummy_error;
+  uptr dummy_bytes_written;
+  bytes_written = bytes_written ? bytes_written : &dummy_bytes_written;
+
+  // Initialize output parameters in case we fail.
+  *error_p = 0;
+  *bytes_written = 0;
+
+  // Map the conventional Unix fds 1 and 2 to Windows handles. They might be
+  // closed, in which case this will fail.
+  if (fd == kStdoutFd || fd == kStderrFd) {
+    fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
+    if (fd == 0) {
+      *error_p = ERROR_INVALID_HANDLE;
+      return false;
+    }
+  }
+
+  DWORD bytes_written_32;
+  if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) {
+    *error_p = GetLastError();
+    return false;
+  } else {
+    *bytes_written = bytes_written_32;
+    return true;
+  }
+}
+
+bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
+  UNIMPLEMENTED();
+}
+
+uptr internal_sched_yield() {
+  Sleep(0);
+  return 0;
+}
+
+void internal__exit(int exitcode) {
+  ExitProcess(exitcode);
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+  UNIMPLEMENTED();
+}
+
+uptr GetRSS() {
+  return 0;
+}
+
+void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
+void internal_join_thread(void *th) { }
+
+// ---------------------- BlockingMutex ---------------- {{{1
+const uptr LOCK_UNINITIALIZED = 0;
+const uptr LOCK_READY = (uptr)-1;
+
+BlockingMutex::BlockingMutex(LinkerInitialized li) {
+  // FIXME: see comments in BlockingMutex::Lock() for the details.
+  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
+
+  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
+  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+  owner_ = LOCK_READY;
+}
+
+BlockingMutex::BlockingMutex() {
+  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
+  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+  owner_ = LOCK_READY;
+}
+
+void BlockingMutex::Lock() {
+  if (owner_ == LOCK_UNINITIALIZED) {
+    // FIXME: hm, global BlockingMutex objects are not initialized?!?
+    // This might be a side effect of the clang+cl+link Frankenbuild...
+    new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));
+
+    // FIXME: If it turns out the linker doesn't invoke our
+    // constructors, we should probably manually Lock/Unlock all the global
+    // locks while we're starting in one thread to avoid double-init races.
+  }
+  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+  CHECK_EQ(owner_, LOCK_READY);
+  owner_ = GetThreadSelf();
+}
+
+void BlockingMutex::Unlock() {
+  CHECK_EQ(owner_, GetThreadSelf());
+  owner_ = LOCK_READY;
+  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+}
+
+void BlockingMutex::CheckLocked() {
+  CHECK_EQ(owner_, GetThreadSelf());
+}
+
+uptr GetTlsSize() {
+  return 0;
+}
+
+void InitTlsSize() {
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+                          uptr *tls_addr, uptr *tls_size) {
+#ifdef SANITIZER_GO
+  *stk_addr = 0;
+  *stk_size = 0;
+  *tls_addr = 0;
+  *tls_size = 0;
+#else
+  uptr stack_top, stack_bottom;
+  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+  *stk_addr = stack_bottom;
+  *stk_size = stack_top - stack_bottom;
+  *tls_addr = 0;
+  *tls_size = 0;
+#endif
+}
+
+#if !SANITIZER_GO
+void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
+  CHECK_GE(max_depth, 2);
+  // FIXME: CaptureStackBackTrace might be too slow for us.
+  // FIXME: Compare with StackWalk64.
+  // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
+  size = CaptureStackBackTrace(2, Min(max_depth, kStackTraceMax),
+                               (void**)trace, 0);
+  if (size == 0)
+    return;
+
+  // Skip the RTL frames by searching for the PC in the stacktrace.
+  uptr pc_location = LocatePcInTrace(pc);
+  PopStackFrames(pc_location);
+}
+
+void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
+                                                    u32 max_depth) {
+  CONTEXT ctx = *(CONTEXT *)context;
+  STACKFRAME64 stack_frame;
+  memset(&stack_frame, 0, sizeof(stack_frame));
+  size = 0;
+#if defined(_WIN64)
+  int machine_type = IMAGE_FILE_MACHINE_AMD64;
+  stack_frame.AddrPC.Offset = ctx.Rip;
+  stack_frame.AddrFrame.Offset = ctx.Rbp;
+  stack_frame.AddrStack.Offset = ctx.Rsp;
+#else
+  int machine_type = IMAGE_FILE_MACHINE_I386;
+  stack_frame.AddrPC.Offset = ctx.Eip;
+  stack_frame.AddrFrame.Offset = ctx.Ebp;
+  stack_frame.AddrStack.Offset = ctx.Esp;
+#endif
+  stack_frame.AddrPC.Mode = AddrModeFlat;
+  stack_frame.AddrFrame.Mode = AddrModeFlat;
+  stack_frame.AddrStack.Mode = AddrModeFlat;
+  while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
+                     &stack_frame, &ctx, NULL, &SymFunctionTableAccess64,
+                     &SymGetModuleBase64, NULL) &&
+         size < Min(max_depth, kStackTraceMax)) {
+    trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset;
+  }
+}
+#endif  // #if !SANITIZER_GO
+
+void ReportFile::Write(const char *buffer, uptr length) {
+  SpinMutexLock l(mu);
+  ReopenIfNecessary();
+  if (!WriteToFile(fd, buffer, length)) {
+    // stderr may be closed, but we may be able to print to the debugger
+    // instead.  This is the case when launching a program from Visual Studio,
+    // and the following routine should write to its console.
+    OutputDebugStringA(buffer);
+  }
+}
+
+void SetAlternateSignalStack() {
+  // FIXME: Decide what to do on Windows.
+}
+
+void UnsetAlternateSignalStack() {
+  // FIXME: Decide what to do on Windows.
+}
+
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {
+  (void)handler;
+  // FIXME: Decide what to do on Windows.
+}
+
+bool IsDeadlySignal(int signum) {
+  // FIXME: Decide what to do on Windows.
+  return false;
+}
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+  SYSTEM_INFO si;
+  GetNativeSystemInfo(&si);
+  uptr page_size = si.dwPageSize;
+  uptr page_mask = ~(page_size - 1);
+
+  for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask;
+       page <= end;) {
+    MEMORY_BASIC_INFORMATION info;
+    if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info))
+      return false;
+
+    if (info.Protect == 0 || info.Protect == PAGE_NOACCESS ||
+        info.Protect == PAGE_EXECUTE)
+      return false;
+
+    if (info.RegionSize == 0)
+      return false;
+
+    page += info.RegionSize;
+  }
+
+  return true;
+}
+
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+  EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo;
+  CONTEXT *context_record = (CONTEXT*)context;
+
+  uptr pc = (uptr)exception_record->ExceptionAddress;
+#ifdef _WIN64
+  uptr bp = (uptr)context_record->Rbp;
+  uptr sp = (uptr)context_record->Rsp;
+#else
+  uptr bp = (uptr)context_record->Ebp;
+  uptr sp = (uptr)context_record->Esp;
+#endif
+  uptr access_addr = exception_record->ExceptionInformation[1];
+
+  return SignalContext(context, access_addr, pc, sp, bp);
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+  // FIXME: Actually implement this function.
+  CHECK_GT(buf_len, 0);
+  buf[0] = 0;
+  return 0;
+}
+
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+  return ReadBinaryName(buf, buf_len);
+}
+
+void CheckVMASize() {
+  // Do nothing.
+}
+
+char **GetArgv() {
+  // FIXME: Actually implement this function.
+  return 0;
+}
+
+}  // namespace __sanitizer
+
+#endif  // _WIN32
index d6a30db..ffc6115 100644 (file)
@@ -84,6 +84,13 @@ Summary:    Swap elf parsing tool
 SWAP probe is a part of data collection back-end for DA.
 This tool will be installed in target
 
+%package -n swap-probe-lsan
+Provides: swap-probe-lsan
+Summary:    Swap lsan tool
+
+%description -n swap-probe-lsan
+This tool will be installed in target
+
 %prep
 %setup -q -n %{name}_%{version}
 
@@ -126,5 +133,9 @@ mkdir -p %{buildroot}/usr/local/bin
 /usr/local/include/parse_elf.h
 %{_prefix}/lib/libparserelf.so
 
+%files -n swap-probe-lsan
+%manifest swap-probe-lsan.manifest
+%defattr(-,root,root,-)
+%{_prefix}/lib/da_swap_lsan.so
 
 %changelog
diff --git a/swap-probe-lsan.manifest b/swap-probe-lsan.manifest
new file mode 100644 (file)
index 0000000..86dbb26
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_" />
+    </request>
+</manifest>