diff a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -74,10 +74,14 @@
 #include "utilities/macros.hpp"
 #include "utilities/xmlstream.hpp"
 #ifdef COMPILER1
 #include "c1/c1_Runtime1.hpp"
 #endif
+#if INCLUDE_TSAN
+#include "tsan/tsanExternalDecls.hpp"
+#include "tsan/tsanOopMap.hpp"
+#endif
 
 // Shared stub locations
 RuntimeStub*        SharedRuntime::_wrong_method_blob;
 RuntimeStub*        SharedRuntime::_wrong_method_abstract_blob;
 RuntimeStub*        SharedRuntime::_ic_miss_blob;
@@ -1006,10 +1010,180 @@
       (char *) name->bytes(), name->utf8_length(),
       (char *) sig->bytes(), sig->utf8_length());
   return 0;
 JRT_END
 
+#if INCLUDE_TSAN
+
+JRT_LEAF(void, SharedRuntime::verify_oop_index(oopDesc* obj, int index))
+  assert(oopDesc::is_oop(obj), "invalid oop");
+  assert(index >= 0, "index is less than 0");
+  int obj_size_in_bytes = obj->size() * HeapWordSize;
+  assert(index < obj_size_in_bytes, "index %d >= obj size %d", index, obj_size_in_bytes);
+JRT_END
+
+// TSAN: method entry callback from interpreter
+// (1) In order to have the line numbers in the call stack, we use the caller
+//     address instead of the method that's being called. This also matches
+//     the entry/exit convention that TSAN uses for C++.
+// We use JRT_ENTRY since call_VM_leaf doesn't set _last_Java_sp that we need.
+JRT_ENTRY(void, SharedRuntime::tsan_interp_method_entry(JavaThread *thread))
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  DEBUG_ONLY(NoHandleMark nhm;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+
+  RegisterMap unused_reg_map(thread, false);
+
+  // These asserts should be removed once
+  // we support more than just the interpreter for TSAN.
+  assert(!thread->last_frame().is_compiled_frame(),
+         "Current frame should not be a compiled frame");
+  const frame sender = thread->last_frame().real_sender(&unused_reg_map);
+  assert(!sender.is_compiled_frame(), "Sender should not be a compiled frame");
+
+  jmethodID jmethod_id = 0;
+  u2 bci = 0;
+  // TODO: is (0, 0) really the best we can do
+  // when the sender isn't an interpreted frame?
+  if (sender.is_interpreted_frame()) {
+    jmethod_id = sender.interpreter_frame_method()->find_jmethod_id_or_null();
+    bci = sender.interpreter_frame_bci();
+  }
+  __tsan_func_entry(tsan_code_location(jmethod_id, bci));
+JRT_END
+
+// TSAN: method exit callback from interpreter
+JRT_LEAF(void, SharedRuntime::tsan_interp_method_exit())
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  __tsan_func_exit();
+JRT_END
+
+void SharedRuntime::tsan_oop_lock(Thread* thread, oop obj) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+
+  TsanOopMap::add_oop(obj);
+  __tsan_java_mutex_lock((julong)(oopDesc*)obj);
+}
+
+void SharedRuntime::tsan_oop_unlock(Thread *thread, oop obj) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+  assert(TsanOopMap::exists(obj), "oop seen in unlock but not tracked");
+
+  __tsan_java_mutex_unlock((julong)(oopDesc*)obj);
+}
+
+void SharedRuntime::tsan_oop_rec_lock(Thread* thread, oop obj, int rec) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+
+  TsanOopMap::add_oop(obj);
+  __tsan_java_mutex_lock_rec((julong)(oopDesc*)obj, rec);
+}
+
+int SharedRuntime::tsan_oop_rec_unlock(Thread *thread, oop obj) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+  assert(TsanOopMap::exists(obj), "oop seen in unlock but not tracked");
+
+  return __tsan_java_mutex_unlock_rec((julong)(oopDesc*)obj);
+}
+
+JRT_LEAF(void, SharedRuntime::tsan_interp_lock(JavaThread* thread,
+                                               BasicObjectLock* elem))
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+  assert(elem != NULL, "null elem");
+
+  oop obj = elem->obj();
+  tsan_oop_lock(thread, obj);
+
+  assert(obj == elem->obj(), "oop changed");
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+JRT_END
+
+JRT_LEAF(void, SharedRuntime::tsan_interp_unlock(JavaThread* thread,
+                                                 BasicObjectLock* elem))
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+  assert(elem != NULL, "null elem");
+
+  oop obj = elem->obj();
+  tsan_oop_unlock(thread, obj);
+
+  assert(obj == elem->obj(), "oop changed");
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+JRT_END
+
+// Should be JRT_LEAF, but this is called very early during VM startup, so we
+// are sometimes in '_thread_in_vm' state.
+// NOTE: DO NOT add operations that can safepoint, enter GC, or throw an
+// exception!
+void SharedRuntime::tsan_track_obj_with_size(oopDesc* obj, int size) {
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(oopDesc::is_oop(obj), "Bad oopDesc passed to tsan_track_obj_with_size().");
+  TsanOopMap::add_oop_with_size(obj, size);
+}
+
+JRT_LEAF(void, SharedRuntime::tsan_track_obj(oopDesc* obj))
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(oopDesc::is_oop(obj), "Bad oopDesc passed to tsan_track_obj().");
+  TsanOopMap::add_oop(obj);
+JRT_END
+
+// TODO: Make tsan_acquire/release JRT_LEAF
+// Currently it can't be JRT_LEAF because there are calls from the VM
+// (instanceKlass.cpp), and JRT_LEAF only allows calls from Java/native code.
+// We need to figure out a better way of being able to call TSAN functions from
+// the VM.
+void SharedRuntime::tsan_acquire(void* address) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(address != NULL, "Cannot acquire at address 0");
+  __tsan_java_acquire(address);
+}
+
+void SharedRuntime::tsan_release(void* address) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(address != NULL, "Cannot release at address 0");
+  __tsan_java_release(address);
+}
+
+#define TSAN_MEMORY_ACCESS(name)                                               \
+  JRT_LEAF(void, SharedRuntime::tsan_##name(                                   \
+      void* addr,                                                              \
+      Method* method,                                                          \
+      address bcp))                                                            \
+    assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");                      \
+    assert(ThreadSanitizerJavaMemory, "Need -XX:+ThreadSanitizerJavaMemory");  \
+    jmethodID mid = method->find_jmethod_id_or_null();                         \
+    int bci = method->bci_from(bcp);                                           \
+    __tsan_##name##_pc(addr, tsan_code_location(mid, bci));                    \
+  JRT_END
+
+TSAN_MEMORY_ACCESS(read1)
+TSAN_MEMORY_ACCESS(read2)
+TSAN_MEMORY_ACCESS(read4)
+TSAN_MEMORY_ACCESS(read8)
+TSAN_MEMORY_ACCESS(write1)
+TSAN_MEMORY_ACCESS(write2)
+TSAN_MEMORY_ACCESS(write4)
+TSAN_MEMORY_ACCESS(write8)
+
+#endif // INCLUDE_TSAN
 
 // Finds receiver, CallInfo (i.e. receiver method), and calling bytecode)
 // for a call current in progress, i.e., arguments has been pushed on stack
 // put callee has not been invoked yet.  Used by: resolve virtual/static,
 // vtable updates, etc.  Caller frame must be compiled.
