
Summary: This is the first in a series of patches to refactor sanitizer_procmaps to allow MachO section information to be exposed on darwin. In addition, grouping all segment information in a single struct is cleaner than passing it through a large set of output parameters, and avoids the need for annotations of NULL parameters for unneeded information. The filename string is optional and must be managed and supplied by the calling function. This is to allow the MemoryMappedSegment struct to be stored on the stack without causing overly large stack sizes. Reviewers: alekseyshl, kubamracek, glider Subscribers: emaste, llvm-commits Differential Revision: https://reviews.llvm.org/D35135 llvm-svn: 307688
330 lines
9.3 KiB
C++
330 lines
9.3 KiB
C++
//===-- dd_interceptors.cc ------------------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "dd_rtl.h"
|
|
#include "interception/interception.h"
|
|
#include "sanitizer_common/sanitizer_procmaps.h"
|
|
#include <pthread.h>
|
|
#include <stdlib.h>
|
|
|
|
using namespace __dsan;
|
|
|
|
__attribute__((tls_model("initial-exec")))
|
|
static __thread Thread *thr;
|
|
__attribute__((tls_model("initial-exec")))
|
|
static __thread volatile int initing;
|
|
static bool inited;
|
|
static uptr g_data_start;
|
|
static uptr g_data_end;
|
|
|
|
static bool InitThread() {
|
|
if (initing)
|
|
return false;
|
|
if (thr != 0)
|
|
return true;
|
|
initing = true;
|
|
if (!inited) {
|
|
inited = true;
|
|
Initialize();
|
|
}
|
|
thr = (Thread*)InternalAlloc(sizeof(*thr));
|
|
internal_memset(thr, 0, sizeof(*thr));
|
|
ThreadInit(thr);
|
|
initing = false;
|
|
return true;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_mutex_destroy, pthread_mutex_t *m) {
|
|
InitThread();
|
|
MutexDestroy(thr, (uptr)m);
|
|
return REAL(pthread_mutex_destroy)(m);
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_mutex_lock, pthread_mutex_t *m) {
|
|
InitThread();
|
|
MutexBeforeLock(thr, (uptr)m, true);
|
|
int res = REAL(pthread_mutex_lock)(m);
|
|
MutexAfterLock(thr, (uptr)m, true, false);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_mutex_trylock, pthread_mutex_t *m) {
|
|
InitThread();
|
|
int res = REAL(pthread_mutex_trylock)(m);
|
|
if (res == 0)
|
|
MutexAfterLock(thr, (uptr)m, true, true);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_mutex_unlock, pthread_mutex_t *m) {
|
|
InitThread();
|
|
MutexBeforeUnlock(thr, (uptr)m, true);
|
|
return REAL(pthread_mutex_unlock)(m);
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_spin_destroy, pthread_spinlock_t *m) {
|
|
InitThread();
|
|
int res = REAL(pthread_spin_destroy)(m);
|
|
MutexDestroy(thr, (uptr)m);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_spin_lock, pthread_spinlock_t *m) {
|
|
InitThread();
|
|
MutexBeforeLock(thr, (uptr)m, true);
|
|
int res = REAL(pthread_spin_lock)(m);
|
|
MutexAfterLock(thr, (uptr)m, true, false);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_spin_trylock, pthread_spinlock_t *m) {
|
|
InitThread();
|
|
int res = REAL(pthread_spin_trylock)(m);
|
|
if (res == 0)
|
|
MutexAfterLock(thr, (uptr)m, true, true);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_spin_unlock, pthread_spinlock_t *m) {
|
|
InitThread();
|
|
MutexBeforeUnlock(thr, (uptr)m, true);
|
|
return REAL(pthread_spin_unlock)(m);
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_destroy, pthread_rwlock_t *m) {
|
|
InitThread();
|
|
MutexDestroy(thr, (uptr)m);
|
|
return REAL(pthread_rwlock_destroy)(m);
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_rdlock, pthread_rwlock_t *m) {
|
|
InitThread();
|
|
MutexBeforeLock(thr, (uptr)m, false);
|
|
int res = REAL(pthread_rwlock_rdlock)(m);
|
|
MutexAfterLock(thr, (uptr)m, false, false);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_tryrdlock, pthread_rwlock_t *m) {
|
|
InitThread();
|
|
int res = REAL(pthread_rwlock_tryrdlock)(m);
|
|
if (res == 0)
|
|
MutexAfterLock(thr, (uptr)m, false, true);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_timedrdlock, pthread_rwlock_t *m,
|
|
const timespec *abstime) {
|
|
InitThread();
|
|
int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
|
|
if (res == 0)
|
|
MutexAfterLock(thr, (uptr)m, false, true);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_wrlock, pthread_rwlock_t *m) {
|
|
InitThread();
|
|
MutexBeforeLock(thr, (uptr)m, true);
|
|
int res = REAL(pthread_rwlock_wrlock)(m);
|
|
MutexAfterLock(thr, (uptr)m, true, false);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_trywrlock, pthread_rwlock_t *m) {
|
|
InitThread();
|
|
int res = REAL(pthread_rwlock_trywrlock)(m);
|
|
if (res == 0)
|
|
MutexAfterLock(thr, (uptr)m, true, true);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_timedwrlock, pthread_rwlock_t *m,
|
|
const timespec *abstime) {
|
|
InitThread();
|
|
int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
|
|
if (res == 0)
|
|
MutexAfterLock(thr, (uptr)m, true, true);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_rwlock_unlock, pthread_rwlock_t *m) {
|
|
InitThread();
|
|
MutexBeforeUnlock(thr, (uptr)m, true); // note: not necessary write unlock
|
|
return REAL(pthread_rwlock_unlock)(m);
|
|
}
|
|
|
|
static pthread_cond_t *init_cond(pthread_cond_t *c, bool force = false) {
|
|
atomic_uintptr_t *p = (atomic_uintptr_t*)c;
|
|
uptr cond = atomic_load(p, memory_order_acquire);
|
|
if (!force && cond != 0)
|
|
return (pthread_cond_t*)cond;
|
|
void *newcond = malloc(sizeof(pthread_cond_t));
|
|
internal_memset(newcond, 0, sizeof(pthread_cond_t));
|
|
if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
|
|
memory_order_acq_rel))
|
|
return (pthread_cond_t*)newcond;
|
|
free(newcond);
|
|
return (pthread_cond_t*)cond;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_cond_init, pthread_cond_t *c,
|
|
const pthread_condattr_t *a) {
|
|
InitThread();
|
|
pthread_cond_t *cond = init_cond(c, true);
|
|
return REAL(pthread_cond_init)(cond, a);
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_cond_wait, pthread_cond_t *c, pthread_mutex_t *m) {
|
|
InitThread();
|
|
pthread_cond_t *cond = init_cond(c);
|
|
MutexBeforeUnlock(thr, (uptr)m, true);
|
|
MutexBeforeLock(thr, (uptr)m, true);
|
|
int res = REAL(pthread_cond_wait)(cond, m);
|
|
MutexAfterLock(thr, (uptr)m, true, false);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_cond_timedwait, pthread_cond_t *c, pthread_mutex_t *m,
|
|
const timespec *abstime) {
|
|
InitThread();
|
|
pthread_cond_t *cond = init_cond(c);
|
|
MutexBeforeUnlock(thr, (uptr)m, true);
|
|
MutexBeforeLock(thr, (uptr)m, true);
|
|
int res = REAL(pthread_cond_timedwait)(cond, m, abstime);
|
|
MutexAfterLock(thr, (uptr)m, true, false);
|
|
return res;
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_cond_signal, pthread_cond_t *c) {
|
|
InitThread();
|
|
pthread_cond_t *cond = init_cond(c);
|
|
return REAL(pthread_cond_signal)(cond);
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *c) {
|
|
InitThread();
|
|
pthread_cond_t *cond = init_cond(c);
|
|
return REAL(pthread_cond_broadcast)(cond);
|
|
}
|
|
|
|
INTERCEPTOR(int, pthread_cond_destroy, pthread_cond_t *c) {
|
|
InitThread();
|
|
pthread_cond_t *cond = init_cond(c);
|
|
int res = REAL(pthread_cond_destroy)(cond);
|
|
free(cond);
|
|
atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
|
|
return res;
|
|
}
|
|
|
|
// for symbolizer
|
|
INTERCEPTOR(char*, realpath, const char *path, char *resolved_path) {
|
|
InitThread();
|
|
return REAL(realpath)(path, resolved_path);
|
|
}
|
|
|
|
INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
|
|
InitThread();
|
|
return REAL(read)(fd, ptr, count);
|
|
}
|
|
|
|
INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
|
|
InitThread();
|
|
return REAL(pread)(fd, ptr, count, offset);
|
|
}
|
|
|
|
extern "C" {
|
|
void __dsan_before_mutex_lock(uptr m, int writelock) {
|
|
if (!InitThread())
|
|
return;
|
|
MutexBeforeLock(thr, m, writelock);
|
|
}
|
|
|
|
void __dsan_after_mutex_lock(uptr m, int writelock, int trylock) {
|
|
if (!InitThread())
|
|
return;
|
|
MutexAfterLock(thr, m, writelock, trylock);
|
|
}
|
|
|
|
void __dsan_before_mutex_unlock(uptr m, int writelock) {
|
|
if (!InitThread())
|
|
return;
|
|
MutexBeforeUnlock(thr, m, writelock);
|
|
}
|
|
|
|
void __dsan_mutex_destroy(uptr m) {
|
|
if (!InitThread())
|
|
return;
|
|
// if (m >= g_data_start && m < g_data_end)
|
|
// return;
|
|
MutexDestroy(thr, m);
|
|
}
|
|
} // extern "C"
|
|
|
|
namespace __dsan {
|
|
|
|
static void InitDataSeg() {
|
|
MemoryMappingLayout proc_maps(true);
|
|
char name[128];
|
|
MemoryMappedSegment segment(name, ARRAY_SIZE(name));
|
|
bool prev_is_data = false;
|
|
while (proc_maps.Next(&segment)) {
|
|
bool is_data = segment.offset != 0 && segment.filename[0] != 0;
|
|
// BSS may get merged with [heap] in /proc/self/maps. This is not very
|
|
// reliable.
|
|
bool is_bss = segment.offset == 0 &&
|
|
(segment.filename[0] == 0 ||
|
|
internal_strcmp(segment.filename, "[heap]") == 0) &&
|
|
prev_is_data;
|
|
if (g_data_start == 0 && is_data) g_data_start = segment.start;
|
|
if (is_bss) g_data_end = segment.end;
|
|
prev_is_data = is_data;
|
|
}
|
|
VPrintf(1, "guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
|
|
CHECK_LT(g_data_start, g_data_end);
|
|
CHECK_GE((uptr)&g_data_start, g_data_start);
|
|
CHECK_LT((uptr)&g_data_start, g_data_end);
|
|
}
|
|
|
|
void InitializeInterceptors() {
|
|
INTERCEPT_FUNCTION(pthread_mutex_destroy);
|
|
INTERCEPT_FUNCTION(pthread_mutex_lock);
|
|
INTERCEPT_FUNCTION(pthread_mutex_trylock);
|
|
INTERCEPT_FUNCTION(pthread_mutex_unlock);
|
|
|
|
INTERCEPT_FUNCTION(pthread_spin_destroy);
|
|
INTERCEPT_FUNCTION(pthread_spin_lock);
|
|
INTERCEPT_FUNCTION(pthread_spin_trylock);
|
|
INTERCEPT_FUNCTION(pthread_spin_unlock);
|
|
|
|
INTERCEPT_FUNCTION(pthread_rwlock_destroy);
|
|
INTERCEPT_FUNCTION(pthread_rwlock_rdlock);
|
|
INTERCEPT_FUNCTION(pthread_rwlock_tryrdlock);
|
|
INTERCEPT_FUNCTION(pthread_rwlock_timedrdlock);
|
|
INTERCEPT_FUNCTION(pthread_rwlock_wrlock);
|
|
INTERCEPT_FUNCTION(pthread_rwlock_trywrlock);
|
|
INTERCEPT_FUNCTION(pthread_rwlock_timedwrlock);
|
|
INTERCEPT_FUNCTION(pthread_rwlock_unlock);
|
|
|
|
INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2");
|
|
INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2");
|
|
INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2");
|
|
INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2");
|
|
INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2");
|
|
INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2");
|
|
|
|
// for symbolizer
|
|
INTERCEPT_FUNCTION(realpath);
|
|
INTERCEPT_FUNCTION(read);
|
|
INTERCEPT_FUNCTION(pread);
|
|
|
|
InitDataSeg();
|
|
}
|
|
|
|
} // namespace __dsan
|