diff --git a/compiler-rt/include/sanitizer/tsan_interface.h b/compiler-rt/include/sanitizer/tsan_interface.h
--- a/compiler-rt/include/sanitizer/tsan_interface.h
+++ b/compiler-rt/include/sanitizer/tsan_interface.h
@@ -154,6 +154,12 @@
 // Do not establish a happens-before relation between fibers
 static const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0;
 
+// User-provided callback invoked on tsan initialization.
+const void __tsan_on_initialize();
+
+// User-provided callback invoked on tsan shutdown.
+const int __tsan_on_finalize(int failed);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface.h b/compiler-rt/lib/tsan/rtl/tsan_interface.h
--- a/compiler-rt/lib/tsan/rtl/tsan_interface.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface.h
@@ -415,6 +415,13 @@
 SANITIZER_INTERFACE_ATTRIBUTE
 void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc,
                                          u8 *a);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_on_initialize();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_on_finalize(int failed);
+
 }  // extern "C"
 
 }  // namespace __tsan
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
@@ -21,6 +21,7 @@
 #include "sanitizer_common/sanitizer_stackdepot.h"
 #include "sanitizer_common/sanitizer_symbolizer.h"
 #include "tsan_defs.h"
+#include "tsan_interface.h"
 #include "tsan_mman.h"
 #include "tsan_platform.h"
 #include "tsan_suppressions.h"
@@ -57,12 +58,23 @@
 bool OnFinalize(bool failed);
 void OnInitialize();
 #else
+#include <dlfcn.h>
 SANITIZER_WEAK_CXX_DEFAULT_IMPL
 bool OnFinalize(bool failed) {
+#if !SANITIZER_GO
+  if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_finalize"))
+    return reinterpret_cast<decltype(&__tsan_on_finalize)>(ptr)(failed);
+#endif
   return failed;
 }
 SANITIZER_WEAK_CXX_DEFAULT_IMPL
-void OnInitialize() {}
+void OnInitialize() {
+#if !SANITIZER_GO
+  if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_initialize")) {
+    return reinterpret_cast<decltype(&__tsan_on_initialize)>(ptr)();
+  }
+#endif
+}
 #endif
 
 static char thread_registry_placeholder[sizeof(ThreadRegistry)];
diff --git a/compiler-rt/test/tsan/on_initialize_finalize_hooks.cpp b/compiler-rt/test/tsan/on_initialize_finalize_hooks.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/tsan/on_initialize_finalize_hooks.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_tsan -O1 %s -o %t.lib -fno-sanitize=thread -shared -fPIC -DBUILD_LIB=1
+// RUN: %clang_tsan -O1 %s %t.lib -o %t
+// RUN: %run %t | FileCheck %s
+
+// Test that initialization/finalization hooks are called, even when they are
+// not defined in the main executable, but by another another library that
+// doesn't directly link against the TSan runtime.
+
+#include <stdio.h>
+
+#if BUILD_LIB
+
+extern "C" void __tsan_on_initialize() {
+  printf("__tsan_on_initialize()\n");
+}
+
+extern "C" int __tsan_on_finalize(int failed) {
+  printf("__tsan_on_finalize()\n");
+  return failed;
+}
+
+#else // BUILD_LIB
+
+int main() {
+  printf("main()\n");
+  return 0;
+}
+
+#endif // BUILD_LIB
+
+// CHECK: __tsan_on_initialize()
+// CHECK: main()
+// CHECK: __tsan_on_finalize()