[PPC64, TSAN] LLVM basic enablement of thread sanitizer for PPC64 (BE and LE)

This patch is by Simone Atzeni with portions by Adhemerval Zanella.

This contains the LLVM patches to enable the thread sanitizer for
PPC64, both big- and little-endian.  Two different virtual memory
sizes are supported:  Old kernels use a 44-bit address space, while
newer kernels require a 46-bit address space.

There are two companion patches that will be added shortly.  There is
a Clang patch to actually turn on the use of the thread sanitizer for
PPC64.  There is also a patch that I wrote to provide interceptor
support for setjmp/longjmp on PPC64.

Patch discussion at reviews.llvm.org/D12841.

llvm-svn: 255057
This commit is contained in:
Bill Schmidt 2015-12-08 21:54:39 +00:00
parent 48eaa54151
commit 2979162732
18 changed files with 270 additions and 11 deletions

View File

@ -278,7 +278,7 @@ set(ALL_LSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC64}
${MIPS32} ${MIPS64})
set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64})
set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
${MIPS32} ${MIPS64} ${PPC64})
set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64})

View File

@ -89,7 +89,8 @@ const int FUTEX_WAKE = 1;
// 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)
#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
SANITIZER_WORDSIZE == 64)
# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
#else
# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
@ -983,6 +984,88 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
: "x30", "memory");
return res;
}
#elif defined(__powerpc64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
long long res;
/* Stack frame offsets. */
#if _CALL_ELF != 2
#define FRAME_MIN_SIZE 112
#define FRAME_TOC_SAVE 40
#else
#define FRAME_MIN_SIZE 32
#define FRAME_TOC_SAVE 24
#endif
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__("r3") = fn;
register void *__cstack __asm__("r4") = child_stack;
register int __flags __asm__("r5") = flags;
register void * __arg __asm__("r6") = arg;
register int * __ptidptr __asm__("r7") = parent_tidptr;
register void * __newtls __asm__("r8") = newtls;
register int * __ctidptr __asm__("r9") = child_tidptr;
__asm__ __volatile__(
/* fn, arg, child_stack are saved acrVoss the syscall */
"mr 28, %5\n\t"
"mr 29, %6\n\t"
"mr 27, %8\n\t"
/* syscall
r3 == flags
r4 == child_stack
r5 == parent_tidptr
r6 == newtls
r7 == child_tidptr */
"mr 3, %7\n\t"
"mr 5, %9\n\t"
"mr 6, %10\n\t"
"mr 7, %11\n\t"
"li 0, %3\n\t"
"sc\n\t"
/* Test if syscall was successful */
"cmpdi cr1, 3, 0\n\t"
"crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
"bne- cr1, 1f\n\t"
/* Do the function call */
"std 2, %13(1)\n\t"
#if _CALL_ELF != 2
"ld 0, 0(28)\n\t"
"ld 2, 8(28)\n\t"
"mtctr 0\n\t"
#else
"mr 12, 28\n\t"
"mtctr 12\n\t"
#endif
"mr 3, 27\n\t"
"bctrl\n\t"
"ld 2, %13(1)\n\t"
/* Call _exit(r3) */
"li 0, %4\n\t"
"sc\n\t"
/* Return to parent */
"1:\n\t"
"mr %0, 3\n\t"
: "=r" (res)
: "0" (-1), "i" (EINVAL),
"i" (__NR_clone), "i" (__NR_exit),
"r" (__fn), "r" (__cstack), "r" (__flags),
"r" (__arg), "r" (__ptidptr), "r" (__newtls),
"r" (__ctidptr), "i" (FRAME_MIN_SIZE), "i" (FRAME_TOC_SAVE)
: "cr0", "cr1", "memory", "ctr",
"r0", "r29", "r27", "r28");
return res;
}
#endif // defined(__x86_64__) && SANITIZER_LINUX
#if SANITIZER_ANDROID

View File

@ -44,7 +44,8 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
// 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__)
#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) \
|| defined(__powerpc64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr);
#endif

View File

@ -15,7 +15,7 @@
#include "sanitizer_platform.h"
#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
defined(__aarch64__))
defined(__aarch64__) || defined(__powerpc64__))
#include "sanitizer_stoptheworld.h"
@ -511,5 +511,5 @@ uptr SuspendedThreadsList::RegisterCount() {
}
} // namespace __sanitizer
#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
// || defined(__aarch64__)
#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
// || defined(__aarch64__) || defined(__powerpc64__)

View File

@ -79,9 +79,11 @@ struct ucontext_t {
};
#endif
#if defined(__x86_64__) || defined(__mips__)
#if defined(__x86_64__) || defined(__mips__) \
|| (defined(__powerpc64__) && defined(__BIG_ENDIAN__))
#define PTHREAD_ABI_BASE "GLIBC_2.3.2"
#elif defined(__aarch64__)
#elif defined(__aarch64__) || (defined(__powerpc64__) \
&& defined(__LITTLE_ENDIAN__))
#define PTHREAD_ABI_BASE "GLIBC_2.17"
#endif

View File

@ -159,6 +159,79 @@ struct Mapping42 {
static const uptr kVdsoBeg = 0x37f00000000ull;
};
// Indicates the runtime will define the memory regions at runtime.
#define TSAN_RUNTIME_VMA 1
#elif defined(__powerpc64__)
// PPC64 supports multiple VMA which leads to multiple address transformation
// functions. To support these multiple VMAS transformations and mappings TSAN
// runtime for PPC64 uses an external memory read (vmaSize) to select which
// mapping to use. Although slower, it make a same instrumented binary run on
// multiple kernels.
/*
C/C++ on linux/powerpc64 (44-bit VMA)
0000 0000 0100 - 0001 0000 0000: main binary
0001 0000 0000 - 0001 0000 0000: -
0001 0000 0000 - 0b00 0000 0000: shadow
0b00 0000 0000 - 0b00 0000 0000: -
0b00 0000 0000 - 0d00 0000 0000: metainfo (memory blocks and sync objects)
0d00 0000 0000 - 0d00 0000 0000: -
0d00 0000 0000 - 0f00 0000 0000: traces
0f00 0000 0000 - 0f00 0000 0000: -
0f00 0000 0000 - 0f50 0000 0000: heap
0f50 0000 0000 - 0f60 0000 0000: -
0f60 0000 0000 - 1000 0000 0000: modules and main thread stack
*/
struct Mapping44 {
static const uptr kMetaShadowBeg = 0x0b0000000000ull;
static const uptr kMetaShadowEnd = 0x0d0000000000ull;
static const uptr kTraceMemBeg = 0x0d0000000000ull;
static const uptr kTraceMemEnd = 0x0f0000000000ull;
static const uptr kShadowBeg = 0x000100000000ull;
static const uptr kShadowEnd = 0x0b0000000000ull;
static const uptr kLoAppMemBeg = 0x000000000100ull;
static const uptr kLoAppMemEnd = 0x000100000000ull;
static const uptr kHeapMemBeg = 0x0f0000000000ull;
static const uptr kHeapMemEnd = 0x0f5000000000ull;
static const uptr kHiAppMemBeg = 0x0f6000000000ull;
static const uptr kHiAppMemEnd = 0x100000000000ull; // 44 bits
static const uptr kAppMemMsk = 0x0f0000000000ull;
static const uptr kAppMemXor = 0x002100000000ull;
static const uptr kVdsoBeg = 0x3c0000000000000ull;
};
/*
C/C++ on linux/powerpc64 (46-bit VMA)
0000 0000 1000 - 0100 0000 0000: main binary
0100 0000 0000 - 0200 0000 0000: -
0100 0000 0000 - 1000 0000 0000: shadow
1000 0000 0000 - 1000 0000 0000: -
1000 0000 0000 - 2000 0000 0000: metainfo (memory blocks and sync objects)
2000 0000 0000 - 2000 0000 0000: -
2000 0000 0000 - 2200 0000 0000: traces
2200 0000 0000 - 3d00 0000 0000: -
3d00 0000 0000 - 3e00 0000 0000: heap
3e00 0000 0000 - 3e80 0000 0000: -
3e80 0000 0000 - 4000 0000 0000: modules and main thread stack
*/
struct Mapping46 {
static const uptr kMetaShadowBeg = 0x100000000000ull;
static const uptr kMetaShadowEnd = 0x200000000000ull;
static const uptr kTraceMemBeg = 0x200000000000ull;
static const uptr kTraceMemEnd = 0x220000000000ull;
static const uptr kShadowBeg = 0x010000000000ull;
static const uptr kShadowEnd = 0x100000000000ull;
static const uptr kHeapMemBeg = 0x3d0000000000ull;
static const uptr kHeapMemEnd = 0x3e0000000000ull;
static const uptr kLoAppMemBeg = 0x000000001000ull;
static const uptr kLoAppMemEnd = 0x010000000000ull;
static const uptr kHiAppMemBeg = 0x3e8000000000ull;
static const uptr kHiAppMemEnd = 0x400000000000ull; // 46 bits
static const uptr kAppMemMsk = 0x3c0000000000ull;
static const uptr kAppMemXor = 0x020000000000ull;
static const uptr kVdsoBeg = 0x7800000000000000ull;
};
// Indicates the runtime will define the memory regions at runtime.
#define TSAN_RUNTIME_VMA 1
#endif
@ -274,6 +347,12 @@ uptr MappingArchImpl(void) {
else
return MappingImpl<Mapping42, Type>();
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return MappingImpl<Mapping44, Type>();
else
return MappingImpl<Mapping46, Type>();
DCHECK(0);
#else
return MappingImpl<Mapping, Type>();
#endif
@ -399,6 +478,12 @@ bool IsAppMem(uptr mem) {
else
return IsAppMemImpl<Mapping42>(mem);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return IsAppMemImpl<Mapping44>(mem);
else
return IsAppMemImpl<Mapping46>(mem);
DCHECK(0);
#else
return IsAppMemImpl<Mapping>(mem);
#endif
@ -418,6 +503,12 @@ bool IsShadowMem(uptr mem) {
else
return IsShadowMemImpl<Mapping42>(mem);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return IsShadowMemImpl<Mapping44>(mem);
else
return IsShadowMemImpl<Mapping46>(mem);
DCHECK(0);
#else
return IsShadowMemImpl<Mapping>(mem);
#endif
@ -437,6 +528,12 @@ bool IsMetaMem(uptr mem) {
else
return IsMetaMemImpl<Mapping42>(mem);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return IsMetaMemImpl<Mapping44>(mem);
else
return IsMetaMemImpl<Mapping46>(mem);
DCHECK(0);
#else
return IsMetaMemImpl<Mapping>(mem);
#endif
@ -462,6 +559,12 @@ uptr MemToShadow(uptr x) {
else
return MemToShadowImpl<Mapping42>(x);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return MemToShadowImpl<Mapping44>(x);
else
return MemToShadowImpl<Mapping46>(x);
DCHECK(0);
#else
return MemToShadowImpl<Mapping>(x);
#endif
@ -489,6 +592,12 @@ u32 *MemToMeta(uptr x) {
else
return MemToMetaImpl<Mapping42>(x);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return MemToMetaImpl<Mapping44>(x);
else
return MemToMetaImpl<Mapping46>(x);
DCHECK(0);
#else
return MemToMetaImpl<Mapping>(x);
#endif
@ -522,6 +631,12 @@ uptr ShadowToMem(uptr s) {
else
return ShadowToMemImpl<Mapping42>(s);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return ShadowToMemImpl<Mapping44>(s);
else
return ShadowToMemImpl<Mapping46>(s);
DCHECK(0);
#else
return ShadowToMemImpl<Mapping>(s);
#endif
@ -549,6 +664,12 @@ uptr GetThreadTrace(int tid) {
else
return GetThreadTraceImpl<Mapping42>(tid);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return GetThreadTraceImpl<Mapping44>(tid);
else
return GetThreadTraceImpl<Mapping46>(tid);
DCHECK(0);
#else
return GetThreadTraceImpl<Mapping>(tid);
#endif
@ -571,6 +692,12 @@ uptr GetThreadTraceHeader(int tid) {
else
return GetThreadTraceHeaderImpl<Mapping42>(tid);
DCHECK(0);
#elif defined(__powerpc64__)
if (vmaSize == 44)
return GetThreadTraceHeaderImpl<Mapping44>(tid);
else
return GetThreadTraceHeaderImpl<Mapping46>(tid);
DCHECK(0);
#else
return GetThreadTraceHeaderImpl<Mapping>(tid);
#endif

View File

@ -244,11 +244,19 @@ void InitializePlatformEarly() {
#ifdef TSAN_RUNTIME_VMA
vmaSize =
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
#if defined(__aarch64__)
if (vmaSize != 39 && vmaSize != 42) {
Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
Printf("FATAL: Found %d - Supported 39 and 42\n", vmaSize);
Die();
}
#elif defined(__powerpc64__)
if (vmaSize != 44 && vmaSize != 46) {
Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
Printf("FATAL: Found %d - Supported 42 and 46\n", vmaSize);
Die();
}
#endif
#endif
}

View File

@ -58,6 +58,18 @@ void InitializeShadowMemory() {
} else {
DCHECK(0);
}
#elif defined(__powerpc64__)
uptr kMadviseRangeBeg = 0;
uptr kMadviseRangeSize = 0;
if (vmaSize == 44) {
kMadviseRangeBeg = 0x0f60000000ull;
kMadviseRangeSize = 0x0010000000ull;
} else if (vmaSize == 46) {
kMadviseRangeBeg = 0x3f0000000000ull;
kMadviseRangeSize = 0x010000000000ull;
} else {
DCHECK(0);
}
#endif
NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
kMadviseRangeSize * kShadowMultiplier);

View File

@ -54,7 +54,7 @@ namespace __tsan {
#ifndef SANITIZER_GO
struct MapUnmapCallback;
#if defined(__mips64) || defined(__aarch64__)
#if defined(__mips64) || defined(__aarch64__) || defined(__powerpc__)
static const uptr kAllocatorSpace = 0;
static const uptr kAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kAllocatorRegionSizeLog = 20;

View File

@ -1,5 +1,5 @@
set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
if(NOT ${COMPILER_RT_DEFAULT_TARGET_ARCH} MATCHES "mips")
if(${COMPILER_RT_DEFAULT_TARGET_ARCH} MATCHES "x86_64")
list(APPEND TSAN_TEST_DEPS GotsanRuntimeCheck)
endif()
if(NOT COMPILER_RT_STANDALONE_BUILD)

View File

@ -1,6 +1,14 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
// CHECK-NOT: WARNING
// CHECK: OK
// This test is failing on powerpc64 (VMA=44). After calling pthread_cancel,
// the Thread-specific data destructors are not called, so the destructor
// "thread_finalize" (defined in tsan_interceptors.cc) can not set the status
// of the thread to "ThreadStatusFinished" failing a check in "SetJoined"
// (defined in sanitizer_thread_registry.cc). It might seem a bug on glibc,
// however the same version GLIBC-2.17 will not make fail the test on
// powerpc64 (VMA=46)
// XFAIL: powerpc64
#include "test.h"

View File

@ -1,4 +1,8 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
// This test fails on powerpc64 on both VMA (44 and 46).
// The Tsan report is returning wrong information about
// the location of the race.
// XFAIL: powerpc64
#include "java.h"
void foobar() {

View File

@ -10,6 +10,7 @@
// MAP_32BIT flag for mmap is supported only for x86_64.
// XFAIL: mips64
// XFAIL: aarch64
// XFAIL: powerpc64
// MAP_32BIT doesn't exist on OS X.
// UNSUPPORTED: darwin

View File

@ -16,6 +16,8 @@ int main() {
const size_t kLog2Size = 39;
#elif defined(__mips64) || defined(__aarch64__)
const size_t kLog2Size = 32;
#elif defined(__powerpc64__)
const size_t kLog2Size = 39;
#endif
const uintptr_t kLocation = 0x40ULL << kLog2Size;
void *p = mmap(

View File

@ -1,4 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
// This test fails on powerpc64 (VMA=46).
// The size of the write reported by Tsan for T1 is 8 instead of 1.
// XFAIL: powerpc64
#include "test.h"
pthread_mutex_t Mtx;

View File

@ -1,4 +1,8 @@
// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
// This test fails on powerpc64 (VMA=44), it does not appear to be
// a functional problem, but the Tsan report is missing some info.
// XFAIL: powerpc64
#include "test.h"
#include <signal.h>
#include <sys/types.h>

View File

@ -5,6 +5,10 @@
// Longjmp assembly has not been implemented for mips64 yet
// XFAIL: mips64
// This test fails on powerpc64 (VMA=44), a segmentation fault
// error happens at the second assignment
// "((volatile int *volatile)mem)[1] = 1".
// XFAIL: powerpc64
#include <setjmp.h>
#include <signal.h>

View File

@ -43,7 +43,7 @@ void print_address(const char *str, int n, ...) {
va_start(ap, n);
while (n--) {
void *p = va_arg(ap, void *);
#if defined(__x86_64__) || defined(__aarch64__)
#if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__)
// On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
// match to the format used in the diagnotic message.
fprintf(stderr, "0x%012lx ", (unsigned long) p);