tsan: ignore interceptors coming from specified libraries

LibIgnore allows to ignore all interceptors called from a particular set
of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions
from the provided SuppressionContext; finds code ranges for the libraries;
and checks whether the provided PC value belongs to the code ranges.

Also make malloc and friends interceptors use SCOPED_INTERCEPTOR_RAW instead of
SCOPED_TSAN_INTERCEPTOR, because if they are called from an ignored lib,
then must call our internal allocator instead of libc malloc.



git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@191897 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 63c4963..6a960c9 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -6,6 +6,7 @@
   sanitizer_common.cc
   sanitizer_flags.cc
   sanitizer_libc.cc
+  sanitizer_libignore.cc
   sanitizer_linux.cc
   sanitizer_mac.cc
   sanitizer_platform_limits_linux.cc
@@ -44,6 +45,7 @@
   sanitizer_internal_defs.h
   sanitizer_lfstack.h
   sanitizer_libc.h
+  sanitizer_libignore.h
   sanitizer_linux.h
   sanitizer_list.h
   sanitizer_mutex.h
diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc
new file mode 100644
index 0000000..9a0213e
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_libignore.cc
@@ -0,0 +1,85 @@
+//===-- sanitizer_libignore.cc --------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include "sanitizer_libignore.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+LibIgnore::LibIgnore(LinkerInitialized) {
+}
+
+void LibIgnore::Init(const SuppressionContext &supp) {
+  BlockingMutexLock lock(&mutex_);
+  CHECK_EQ(count_, 0);
+  const uptr n = supp.SuppressionCount();
+  for (uptr i = 0; i < n; i++) {
+    const Suppression *s = supp.SuppressionAt(i);
+    if (s->type != SuppressionLib)
+      continue;
+    if (count_ >= kMaxLibs) {
+      Report("%s: too many called_from_lib suppressions (max: %d)\n",
+             SanitizerToolName, kMaxLibs);
+      Die();
+    }
+    Lib *lib = &libs_[count_++];
+    lib->templ = internal_strdup(s->templ);
+    lib->name = 0;
+    lib->loaded = false;
+  }
+}
+
+void LibIgnore::OnLibraryLoaded() {
+  BlockingMutexLock lock(&mutex_);
+  MemoryMappingLayout proc_maps(/*cache_enabled*/false);
+  InternalScopedBuffer<char> fn(4096);
+  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, fn.data(), fn.size(), &prot)) {
+      if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 &&
+          TemplateMatch(lib->templ, fn.data())) {
+        if (loaded) {
+          Report("%s: called_from_lib suppression '%s' is matched against"
+                 " 2 libraries: '%s' and '%s'\n",
+                 SanitizerToolName, lib->templ, lib->name, fn.data());
+          Die();
+        }
+        loaded = true;
+        if (!lib->loaded) {
+          lib->loaded = true;
+          lib->name = internal_strdup(fn.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();
+}
+
+}  // namespace __sanitizer
+
+#endif  // #if SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_libignore.h b/lib/sanitizer_common/sanitizer_libignore.h
new file mode 100644
index 0000000..4e6f5a9
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_libignore.h
@@ -0,0 +1,83 @@
+//===-- sanitizer_libignore.h -----------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// 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 remembers all "called_from_lib" suppressions
+// from the provided SuppressionContext; 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_suppressions.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_mutex.h"
+
+namespace __sanitizer {
+
+class LibIgnore {
+ public:
+  explicit LibIgnore(LinkerInitialized);
+
+  // Fetches all "called_from_lib" suppressions from the SuppressionContext.
+  void Init(const SuppressionContext &supp);
+
+  // Must be called after a new dynamic library is loaded.
+  void OnLibraryLoaded();
+
+  // 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;
+    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/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc
index 0676c0b..5f3d2ce 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.cc
+++ b/lib/sanitizer_common/sanitizer_suppressions.cc
@@ -20,7 +20,7 @@
 namespace __sanitizer {
 
 static const char *const kTypeStrings[SuppressionTypeCount] = {
-  "none", "race", "mutex", "thread", "signal", "leak"
+  "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib"
 };
 
 bool TemplateMatch(char *templ, const char *str) {
@@ -129,10 +129,15 @@
   }
 }
 
-uptr SuppressionContext::SuppressionCount() {
+uptr SuppressionContext::SuppressionCount() const {
   return suppressions_.size();
 }
 
+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++)
diff --git a/lib/sanitizer_common/sanitizer_suppressions.h b/lib/sanitizer_common/sanitizer_suppressions.h
index e0b562e..92cb093 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.h
+++ b/lib/sanitizer_common/sanitizer_suppressions.h
@@ -25,6 +25,7 @@
   SuppressionThread,
   SuppressionSignal,
   SuppressionLeak,
+  SuppressionLib,
   SuppressionTypeCount
 };
 
@@ -40,7 +41,8 @@
   SuppressionContext() : suppressions_(1), can_parse_(true) {}
   void Parse(const char *str);
   bool Match(const char* str, SuppressionType type, Suppression **s);
-  uptr SuppressionCount();
+  uptr SuppressionCount() const;
+  const Suppression *SuppressionAt(uptr i) const;
   void GetMatched(InternalMmapVector<Suppression *> *matched);
 
  private:
@@ -52,7 +54,6 @@
 
 const char *SuppressionTypeString(SuppressionType t);
 
-// Exposed for testing.
 bool TemplateMatch(char *templ, const char *str);
 
 }  // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
index f44911e..ea8741d 100644
--- a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
@@ -65,8 +65,10 @@
   CHECK(!internal_strcmp(SuppressionTypeString(SuppressionThread), "thread"));
   CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal"));
   CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak"));
+  CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib),
+      "called_from_lib"));
   // Ensure this test is up-to-date when suppression types are added.
-  CHECK_EQ(SuppressionTypeCount, 6);
+  CHECK_EQ(SuppressionTypeCount, 7);
 }
 
 class SuppressionContextTest : public ::testing::Test {
diff --git a/lib/tsan/lit_tests/ignore_lib0.cc b/lib/tsan/lit_tests/ignore_lib0.cc
new file mode 100644
index 0000000..ea0f061
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib0.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so
+// RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t
+// RUN: echo running w/o suppressions:
+// RUN: LD_LIBRARY_PATH=%T not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: LD_LIBRARY_PATH=%T TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+
+// Tests that interceptors coming from a library specified in called_from_lib
+// suppression are ignored.
+
+#ifndef LIB
+
+extern "C" void libfunc();
+
+int main() {
+  libfunc();
+}
+
+#else  // #ifdef LIB
+
+#include "ignore_lib_lib.h"
+
+#endif  // #ifdef LIB
+
+// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race
+// CHECK-NOSUPP: OK
+
+// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-WITHSUPP: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib0.cc.supp b/lib/tsan/lit_tests/ignore_lib0.cc.supp
new file mode 100644
index 0000000..7728c92
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib0.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:/libignore_lib0.so
+
diff --git a/lib/tsan/lit_tests/ignore_lib1.cc b/lib/tsan/lit_tests/ignore_lib1.cc
new file mode 100644
index 0000000..c4f2e73
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib1.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib1.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: echo running w/o suppressions:
+// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: echo running with suppressions:
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+
+// Tests that interceptors coming from a dynamically loaded library specified
+// in called_from_lib suppression are ignored.
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+  std::string lib = std::string(dirname(argv[0])) + "/libignore_lib1.so";
+  void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
+  if (h == 0)
+    exit(printf("failed to load the library (%d)\n", errno));
+  void (*f)() = (void(*)())dlsym(h, "libfunc");
+  if (f == 0)
+    exit(printf("failed to find the func (%d)\n", errno));
+  f();
+}
+
+#else  // #ifdef LIB
+
+#include "ignore_lib_lib.h"
+
+#endif  // #ifdef LIB
+
+// CHECK-NOSUPP: WARNING: ThreadSanitizer: data race
+// CHECK-NOSUPP: OK
+
+// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-WITHSUPP: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib1.cc.supp b/lib/tsan/lit_tests/ignore_lib1.cc.supp
new file mode 100644
index 0000000..9f4119e
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib1.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:/libignore_lib1.so$
+
diff --git a/lib/tsan/lit_tests/ignore_lib2.cc b/lib/tsan/lit_tests/ignore_lib2.cc
new file mode 100644
index 0000000..97f9419
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib2.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s
+
+// Tests that called_from_lib suppression matched against 2 libraries
+// causes program crash (this is not supported).
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+  std::string lib0 = std::string(dirname(argv[0])) + "/libignore_lib2_0.so";
+  std::string lib1 = std::string(dirname(argv[0])) + "/libignore_lib2_1.so";
+  dlopen(lib0.c_str(), RTLD_GLOBAL | RTLD_NOW);
+  dlopen(lib1.c_str(), RTLD_GLOBAL | RTLD_NOW);
+  fprintf(stderr, "OK\n");
+}
+
+#else  // #ifdef LIB
+
+extern "C" void libfunc() {
+}
+
+#endif  // #ifdef LIB
+
+// CHECK: ThreadSanitizer: called_from_lib suppression 'ignore_lib2' is matched against 2 libraries
+// CHECK-NOT: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib2.cc.supp b/lib/tsan/lit_tests/ignore_lib2.cc.supp
new file mode 100644
index 0000000..1419c71
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib2.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:ignore_lib2
+
diff --git a/lib/tsan/lit_tests/ignore_lib3.cc b/lib/tsan/lit_tests/ignore_lib3.cc
new file mode 100644
index 0000000..8f237fc
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib3.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" not %t 2>&1 | FileCheck %s
+
+// Tests that unloading of a library matched against called_from_lib suppression
+// causes program crash (this is not supported).
+
+#ifndef LIB
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <libgen.h>
+#include <string>
+
+int main(int argc, char **argv) {
+  std::string lib = std::string(dirname(argv[0])) + "/libignore_lib3.so";
+  void *h = dlopen(lib.c_str(), RTLD_GLOBAL | RTLD_NOW);
+  dlclose(h);
+  fprintf(stderr, "OK\n");
+}
+
+#else  // #ifdef LIB
+
+extern "C" void libfunc() {
+}
+
+#endif  // #ifdef LIB
+
+// CHECK: ThreadSanitizer: library {{.*}} that was matched against called_from_lib suppression 'ignore_lib3.so' is unloaded
+// CHECK-NOT: OK
+
diff --git a/lib/tsan/lit_tests/ignore_lib3.cc.supp b/lib/tsan/lit_tests/ignore_lib3.cc.supp
new file mode 100644
index 0000000..975dbce
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib3.cc.supp
@@ -0,0 +1,2 @@
+called_from_lib:ignore_lib3.so
+
diff --git a/lib/tsan/lit_tests/ignore_lib_lib.h b/lib/tsan/lit_tests/ignore_lib_lib.h
new file mode 100644
index 0000000..2bfe84d
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_lib_lib.h
@@ -0,0 +1,25 @@
+#include <pthread.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+void *volatile mem;
+volatile int len;
+
+void *Thread(void *p) {
+  while ((p = __atomic_load_n(&mem, __ATOMIC_ACQUIRE)) == 0)
+    usleep(100);
+  memset(p, 0, len);
+  return 0;
+}
+
+extern "C" void libfunc() {
+  pthread_t t;
+  pthread_create(&t, 0, Thread, 0);
+  len = 10;
+  __atomic_store_n(&mem, malloc(len), __ATOMIC_RELEASE);
+  pthread_join(t, 0);
+  free(mem);
+  fprintf(stderr, "OK\n");
+}
diff --git a/lib/tsan/lit_tests/java_alloc.cc b/lib/tsan/lit_tests/java_alloc.cc
index 079c9b9..4dbce70 100644
--- a/lib/tsan/lit_tests/java_alloc.cc
+++ b/lib/tsan/lit_tests/java_alloc.cc
@@ -20,7 +20,6 @@
 
 int main() {
   jptr jheap = (jptr)malloc(kHeapSize);
-  __tsan_java_preinit("[vdso]");
   __tsan_java_init(jheap, kHeapSize);
   pthread_t th;
   pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4));
diff --git a/lib/tsan/lit_tests/java_lock.cc b/lib/tsan/lit_tests/java_lock.cc
index c019597..d9db103 100644
--- a/lib/tsan/lit_tests/java_lock.cc
+++ b/lib/tsan/lit_tests/java_lock.cc
@@ -16,7 +16,6 @@
 int main() {
   int const kHeapSize = 1024 * 1024;
   void *jheap = malloc(kHeapSize);
-  __tsan_java_preinit(0);
   __tsan_java_init((jptr)jheap, kHeapSize);
   const int kBlockSize = 16;
   __tsan_java_alloc((jptr)jheap, kBlockSize);
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 0b6291a..c4bde58 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -22,6 +22,7 @@
 #include "interception/interception.h"
 #include "tsan_interface.h"
 #include "tsan_platform.h"
+#include "tsan_suppressions.h"
 #include "tsan_rtl.h"
 #include "tsan_mman.h"
 #include "tsan_fd.h"
@@ -126,18 +127,16 @@
   SignalDesc pending_signals[kSigCount];
 };
 
-// Used to ignore interceptors coming directly from libjvm.so.
-atomic_uintptr_t libjvm_begin;
-atomic_uintptr_t libjvm_end;
+// The object is 64-byte aligned, because we want hot data to be located in
+// a single cache line if possible (it's accessed in every interceptor).
+static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)];
+static LibIgnore *libignore() {
+  return reinterpret_cast<LibIgnore*>(&libignore_placeholder[0]);
+}
 
-static bool libjvm_check(uptr pc) {
-  uptr begin = atomic_load(&libjvm_begin, memory_order_relaxed);
-  if (begin != 0 && pc >= begin) {
-    uptr end = atomic_load(&libjvm_end, memory_order_relaxed);
-    if (end != 0 && pc < end)
-      return true;
-  }
-  return false;
+void InitializeLibIgnore() {
+  libignore()->Init(*GetSuppressionContext());
+  libignore()->OnLibraryLoaded();
 }
 
 }  // namespace __tsan
@@ -162,12 +161,14 @@
  private:
   ThreadState *const thr_;
   const int in_rtl_;
+  bool in_ignored_lib_;
 };
 
 ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
                                      uptr pc)
     : thr_(thr)
-    , in_rtl_(thr->in_rtl) {
+    , in_rtl_(thr->in_rtl)
+    , in_ignored_lib_(false) {
   if (thr_->in_rtl == 0) {
     Initialize(thr);
     FuncEntry(thr, pc);
@@ -176,9 +177,18 @@
   } else {
     thr_->in_rtl++;
   }
+  if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) {
+    in_ignored_lib_ = true;
+    thr_->in_ignored_lib = true;
+    ThreadIgnoreBegin(thr_);
+  }
 }
 
 ScopedInterceptor::~ScopedInterceptor() {
+  if (in_ignored_lib_) {
+    thr_->in_ignored_lib = false;
+    ThreadIgnoreEnd(thr_);
+  }
   thr_->in_rtl--;
   if (thr_->in_rtl == 0) {
     FuncExit(thr_);
@@ -203,7 +213,7 @@
       Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
       Die(); \
     } \
-    if (thr->in_rtl > 1 || libjvm_check(pc)) \
+    if (thr->in_rtl > 1 || thr->in_ignored_lib) \
       return REAL(func)(__VA_ARGS__); \
 /**/
 
@@ -246,6 +256,28 @@
   return res;
 }
 
+TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
+  SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag);
+  // dlopen will execute global constructors, so it must be not in rtl.
+  CHECK_EQ(thr->in_rtl, 1);
+  thr->in_rtl = 0;
+  void *res = REAL(dlopen)(filename, flag);
+  thr->in_rtl = 1;
+  libignore()->OnLibraryLoaded();
+  return res;
+}
+
+TSAN_INTERCEPTOR(int, dlclose, void *handle) {
+  SCOPED_INTERCEPTOR_RAW(dlclose, handle);
+  // dlclose will execute global destructors, so it must be not in rtl.
+  CHECK_EQ(thr->in_rtl, 1);
+  thr->in_rtl = 0;
+  int res = REAL(dlclose)(handle);
+  thr->in_rtl = 1;
+  libignore()->OnLibraryUnloaded();
+  return res;
+}
+
 class AtExitContext {
  public:
   AtExitContext()
@@ -441,7 +473,7 @@
 }
 
 TSAN_INTERCEPTOR(void*, malloc, uptr size) {
-  if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+  if (cur_thread()->in_symbolizer)
     return __libc_malloc(size);
   void *p = 0;
   {
@@ -458,7 +490,7 @@
 }
 
 TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
-  if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+  if (cur_thread()->in_symbolizer)
     return __libc_calloc(size, n);
   if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n))
     return AllocatorReturnNull();
@@ -474,7 +506,7 @@
 }
 
 TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
-  if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+  if (cur_thread()->in_symbolizer)
     return __libc_realloc(p, size);
   if (p)
     invoke_free_hook(p);
@@ -489,7 +521,7 @@
 TSAN_INTERCEPTOR(void, free, void *p) {
   if (p == 0)
     return;
-  if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+  if (cur_thread()->in_symbolizer)
     return __libc_free(p);
   invoke_free_hook(p);
   SCOPED_INTERCEPTOR_RAW(free, p);
@@ -499,7 +531,7 @@
 TSAN_INTERCEPTOR(void, cfree, void *p) {
   if (p == 0)
     return;
-  if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC()))
+  if (cur_thread()->in_symbolizer)
     return __libc_free(p);
   invoke_free_hook(p);
   SCOPED_INTERCEPTOR_RAW(cfree, p);
@@ -508,13 +540,11 @@
 
 TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
   SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p);
-  if (libjvm_check(pc))
-    return malloc_usable_size(p);
   return user_alloc_usable_size(thr, pc, p);
 }
 
 #define OPERATOR_NEW_BODY(mangled_name) \
-  if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \
+  if (cur_thread()->in_symbolizer) \
     return __libc_malloc(size); \
   void *p = 0; \
   {  \
@@ -550,7 +580,7 @@
 
 #define OPERATOR_DELETE_BODY(mangled_name) \
   if (ptr == 0) return;  \
-  if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \
+  if (cur_thread()->in_symbolizer) \
     return __libc_free(ptr); \
   invoke_free_hook(ptr);  \
   SCOPED_INTERCEPTOR_RAW(mangled_name, ptr);  \
@@ -684,15 +714,6 @@
 
 TSAN_INTERCEPTOR(char*, strdup, const char *str) {
   SCOPED_TSAN_INTERCEPTOR(strdup, str);
-  if (libjvm_check(pc)) {
-    // The memory must come from libc malloc,
-    // and we must not instrument accesses in this case.
-    uptr n = internal_strlen(str) + 1;
-    void *p = __libc_malloc(n);
-    if (p == 0)
-      return 0;
-    return (char*)internal_memcpy(p, str, n);
-  }
   // strdup will call malloc, so no instrumentation is required here.
   return REAL(strdup)(str);
 }
@@ -747,23 +768,23 @@
 }
 
 TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
-  SCOPED_TSAN_INTERCEPTOR(memalign, align, sz);
+  SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
   return user_alloc(thr, pc, sz, align);
 }
 
 TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
-  SCOPED_TSAN_INTERCEPTOR(valloc, sz);
+  SCOPED_INTERCEPTOR_RAW(valloc, sz);
   return user_alloc(thr, pc, sz, GetPageSizeCached());
 }
 
 TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
-  SCOPED_TSAN_INTERCEPTOR(pvalloc, sz);
+  SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
   sz = RoundUp(sz, GetPageSizeCached());
   return user_alloc(thr, pc, sz, GetPageSizeCached());
 }
 
 TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
-  SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz);
+  SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
   *memptr = user_alloc(thr, pc, sz, align);
   return 0;
 }
@@ -852,7 +873,7 @@
 
 TSAN_INTERCEPTOR(int, pthread_create,
     void *th, void *attr, void *(*callback)(void*), void * param) {
-  SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param);
+  SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param);
   __sanitizer_pthread_attr_t myattr;
   if (attr == 0) {
     pthread_attr_init(&myattr);
@@ -886,7 +907,7 @@
 }
 
 TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
-  SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret);
+  SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
   int tid = ThreadTid(thr, pc, (uptr)th);
   int res = BLOCK_REAL(pthread_join)(th, ret);
   if (res == 0) {
@@ -1820,7 +1841,7 @@
 }
 
 TSAN_INTERCEPTOR(int, fork, int fake) {
-  SCOPED_TSAN_INTERCEPTOR(fork, fake);
+  SCOPED_INTERCEPTOR_RAW(fork, fake);
   int pid = REAL(fork)(fake);
   if (pid == 0) {
     // child
@@ -2174,6 +2195,8 @@
   TSAN_INTERCEPT(munlockall);
 
   TSAN_INTERCEPT(fork);
+  TSAN_INTERCEPT(dlopen);
+  TSAN_INTERCEPT(dlclose);
   TSAN_INTERCEPT(on_exit);
   TSAN_INTERCEPT(__cxa_atexit);
 
diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc
index 2ac3249..53f14cf 100644
--- a/lib/tsan/rtl/tsan_interface_java.cc
+++ b/lib/tsan/rtl/tsan_interface_java.cc
@@ -96,8 +96,6 @@
 
 static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
 static JavaContext *jctx;
-extern atomic_uintptr_t libjvm_begin;
-extern atomic_uintptr_t libjvm_end;
 
 static BlockDesc *getblock(uptr addr) {
   uptr i = (addr - jctx->heap_begin) / kHeapAlignment;
@@ -166,17 +164,6 @@
   ScopedJavaFunc scoped(thr, caller_pc); \
 /**/
 
-void __tsan_java_preinit(const char *libjvm_path) {
-  SCOPED_JAVA_FUNC(__tsan_java_preinit);
-  if (libjvm_path) {
-    uptr begin, end;
-    if (GetCodeRangeForFile(libjvm_path, &begin, &end)) {
-      atomic_store(&libjvm_begin, begin, memory_order_relaxed);
-      atomic_store(&libjvm_end, end, memory_order_relaxed);
-    }
-  }
-}
-
 void __tsan_java_init(jptr heap_begin, jptr heap_size) {
   SCOPED_JAVA_FUNC(__tsan_java_init);
   DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size);
diff --git a/lib/tsan/rtl/tsan_interface_java.h b/lib/tsan/rtl/tsan_interface_java.h
index a4a05aa..9ac78e0 100644
--- a/lib/tsan/rtl/tsan_interface_java.h
+++ b/lib/tsan/rtl/tsan_interface_java.h
@@ -34,11 +34,7 @@
 
 typedef unsigned long jptr;  // NOLINT
 
-// Must be called before any other callback from Java, right after dlopen
-// of JVM shared lib. If libjvm_path is specified, then all interceptors
-// coming directly from JVM will be ignored.
-void __tsan_java_preinit(const char *libjvm_path) INTERFACE_ATTRIBUTE;
-// Must be called after __tsan_java_preinit but before any other callback.
+// Must be called before any other callback from Java.
 void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE;
 // Must be called when the application exits.
 // Not necessary the last callback (concurrently running threads are OK).
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index dcd98b0..aa8934a 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -214,6 +214,7 @@
     __sanitizer_set_report_path(flags()->log_path);
   InitializeSuppressions();
 #ifndef TSAN_GO
+  InitializeLibIgnore();
   // Initialize external symbolizer before internal threads are started.
   const char *external_symbolizer = flags()->external_symbolizer_path;
   if (external_symbolizer != 0 && external_symbolizer[0] != '\0') {
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index ed7b7ed..9169aeb 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -29,6 +29,7 @@
 #include "sanitizer_common/sanitizer_allocator.h"
 #include "sanitizer_common/sanitizer_allocator_internal.h"
 #include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libignore.h"
 #include "sanitizer_common/sanitizer_suppressions.h"
 #include "sanitizer_common/sanitizer_thread_registry.h"
 #include "tsan_clock.h"
@@ -434,6 +435,7 @@
   const int unique_id;
   int in_rtl;
   bool in_symbolizer;
+  bool in_ignored_lib;
   bool is_alive;
   bool is_freeing;
   bool is_vptr_access;
@@ -598,6 +600,7 @@
 void DontNeedShadowFor(uptr addr, uptr size);
 void InitializeShadowMemory();
 void InitializeInterceptors();
+void InitializeLibIgnore();
 void InitializeDynamicAnnotations();
 
 void ReportRace(ThreadState *thr);
diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc
index 3a2173e..96d5ee4 100644
--- a/lib/tsan/rtl/tsan_stat.cc
+++ b/lib/tsan/rtl/tsan_stat.cc
@@ -371,6 +371,8 @@
   name[StatInt_sigprocmask]              = "  sigprocmask                     ";
   name[StatInt_backtrace]                = "  backtrace                       ";
   name[StatInt_backtrace_symbols]        = "  backtrace_symbols               ";
+  name[StatInt_dlopen]                   = "  dlopen                          ";
+  name[StatInt_dlclose]                  = "  dlclose                         ";
 
   name[StatAnnotation]                   = "Dynamic annotations               ";
   name[StatAnnotateHappensBefore]        = "  HappensBefore                   ";
diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h
index 526190c..43c6b1c 100644
--- a/lib/tsan/rtl/tsan_stat.h
+++ b/lib/tsan/rtl/tsan_stat.h
@@ -366,6 +366,8 @@
   StatInt_sigprocmask,
   StatInt_backtrace,
   StatInt_backtrace_symbols,
+  StatInt_dlopen,
+  StatInt_dlclose,
 
   // Dynamic annotations.
   StatAnnotation,
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index 0a365b2..4b0ac04 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -87,6 +87,11 @@
 #endif
 }
 
+SuppressionContext *GetSuppressionContext() {
+  CHECK_NE(g_ctx, 0);
+  return g_ctx;
+}
+
 SuppressionType conv(ReportType typ) {
   if (typ == ReportTypeRace)
     return SuppressionRace;
diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h
index c618b3d..fe7db58 100644
--- a/lib/tsan/rtl/tsan_suppressions.h
+++ b/lib/tsan/rtl/tsan_suppressions.h
@@ -22,6 +22,7 @@
 void PrintMatchedSuppressions();
 uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp);
 uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp);
+SuppressionContext *GetSuppressionContext();
 
 }  // namespace __tsan