diff --git a/projects/LLVMBuild.txt b/projects/LLVMBuild.txt new file mode 100644 index 00000000..3c24d1a3 --- /dev/null +++ b/projects/LLVMBuild.txt @@ -0,0 +1,21 @@ +;===- ./projects/LLVMBuild.txt ---------------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Group +name = Projects +parent = $ROOT diff --git a/projects/compiler-rt/.arcconfig b/projects/compiler-rt/.arcconfig new file mode 100644 index 00000000..413b70b0 --- /dev/null +++ b/projects/compiler-rt/.arcconfig @@ -0,0 +1,4 @@ +{ + "project_id" : "compiler-rt", + "conduit_uri" : "http://llvm-reviews.chandlerc.com/" +} diff --git a/projects/compiler-rt/.gitignore b/projects/compiler-rt/.gitignore new file mode 100644 index 00000000..7c534314 --- /dev/null +++ b/projects/compiler-rt/.gitignore @@ -0,0 +1,4 @@ +*~ +darwin_fat +clang_darwin +multi_arch diff --git a/projects/compiler-rt/BlocksRuntime/Block.h b/projects/compiler-rt/BlocksRuntime/Block.h new file mode 100644 index 00000000..55cdd01a --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/Block.h @@ -0,0 +1,59 @@ +/* + * Block.h + * + * Copyright 2008-2010 Apple, Inc. Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _BLOCK_H_ +#define _BLOCK_H_ + +#if !defined(BLOCK_EXPORT) +# if defined(__cplusplus) +# define BLOCK_EXPORT extern "C" +# else +# define BLOCK_EXPORT extern +# endif +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Create a heap based copy of a Block or simply add a reference to an existing one. + * This must be paired with Block_release to recover memory, even when running + * under Objective-C Garbage Collection. + */ +BLOCK_EXPORT void *_Block_copy(const void *aBlock); + +/* Lose the reference, and if heap based and last reference, recover the memory. */ +BLOCK_EXPORT void _Block_release(const void *aBlock); + +#if defined(__cplusplus) +} +#endif + +/* Type correct macros. */ + +#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__))) +#define Block_release(...) _Block_release((const void *)(__VA_ARGS__)) + + +#endif diff --git a/projects/compiler-rt/BlocksRuntime/Block_private.h b/projects/compiler-rt/BlocksRuntime/Block_private.h new file mode 100644 index 00000000..8ae82181 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/Block_private.h @@ -0,0 +1,179 @@ +/* + * Block_private.h + * + * Copyright 2008-2010 Apple, Inc. Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _BLOCK_PRIVATE_H_ +#define _BLOCK_PRIVATE_H_ + +#if !defined(BLOCK_EXPORT) +# if defined(__cplusplus) +# define BLOCK_EXPORT extern "C" +# else +# define BLOCK_EXPORT extern +# endif +#endif + +#ifndef _MSC_VER +#include +#else +/* MSVC doesn't have . Compensate. */ +typedef char bool; +#define true (bool)1 +#define false (bool)0 +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + + +enum { + BLOCK_REFCOUNT_MASK = (0xffff), + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CTOR = (1 << 26), /* Helpers have C++ code. */ + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) +}; + + +/* Revised new layout. */ +struct Block_descriptor { + unsigned long int reserved; + unsigned long int size; + void (*copy)(void *dst, void *src); + void (*dispose)(void *); +}; + + +struct Block_layout { + void *isa; + int flags; + int reserved; + void (*invoke)(void *, ...); + struct Block_descriptor *descriptor; + /* Imported variables. */ +}; + + +struct Block_byref { + void *isa; + struct Block_byref *forwarding; + int flags; /* refcount; */ + int size; + void (*byref_keep)(struct Block_byref *dst, struct Block_byref *src); + void (*byref_destroy)(struct Block_byref *); + /* long shared[0]; */ +}; + + +struct Block_byref_header { + void *isa; + struct Block_byref *forwarding; + int flags; + int size; +}; + + +/* Runtime support functions used by compiler when generating copy/dispose helpers. */ + +enum { + /* See function implementation for a more complete description of these fields and combinations */ + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy helpers */ + BLOCK_BYREF_CALLER = 128 /* called from __block (byref) copy/dispose support routines. */ +}; + +/* Runtime entry point called by compiler when assigning objects inside copy helper routines */ +BLOCK_EXPORT void _Block_object_assign(void *destAddr, const void *object, const int flags); + /* BLOCK_FIELD_IS_BYREF is only used from within block copy helpers */ + + +/* runtime entry point called by the compiler when disposing of objects inside dispose helper routine */ +BLOCK_EXPORT void _Block_object_dispose(const void *object, const int flags); + + + +/* Other support functions */ + +/* Runtime entry to get total size of a closure */ +BLOCK_EXPORT unsigned long int Block_size(void *block_basic); + + + +/* the raw data space for runtime classes for blocks */ +/* class+meta used for stack, malloc, and collectable based blocks */ +BLOCK_EXPORT void * _NSConcreteStackBlock[32]; +BLOCK_EXPORT void * _NSConcreteMallocBlock[32]; +BLOCK_EXPORT void * _NSConcreteAutoBlock[32]; +BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]; +BLOCK_EXPORT void * _NSConcreteGlobalBlock[32]; +BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32]; + + +/* the intercept routines that must be used under GC */ +BLOCK_EXPORT void _Block_use_GC( void *(*alloc)(const unsigned long, const bool isOne, const bool isObject), + void (*setHasRefcount)(const void *, const bool), + void (*gc_assign_strong)(void *, void **), + void (*gc_assign_weak)(const void *, void *), + void (*gc_memmove)(void *, void *, unsigned long)); + +/* earlier version, now simply transitional */ +BLOCK_EXPORT void _Block_use_GC5( void *(*alloc)(const unsigned long, const bool isOne, const bool isObject), + void (*setHasRefcount)(const void *, const bool), + void (*gc_assign_strong)(void *, void **), + void (*gc_assign_weak)(const void *, void *)); + +BLOCK_EXPORT void _Block_use_RR( void (*retain)(const void *), + void (*release)(const void *)); + +/* make a collectable GC heap based Block. Not useful under non-GC. */ +BLOCK_EXPORT void *_Block_copy_collectable(const void *aBlock); + +/* thread-unsafe diagnostic */ +BLOCK_EXPORT const char *_Block_dump(const void *block); + + +/* Obsolete */ + +/* first layout */ +struct Block_basic { + void *isa; + int Block_flags; /* int32_t */ + int Block_size; /* XXX should be packed into Block_flags */ + void (*Block_invoke)(void *); + void (*Block_copy)(void *dst, void *src); /* iff BLOCK_HAS_COPY_DISPOSE */ + void (*Block_dispose)(void *); /* iff BLOCK_HAS_COPY_DISPOSE */ + /* long params[0]; // where const imports, __block storage references, etc. get laid down */ +}; + + +#if defined(__cplusplus) +} +#endif + + +#endif /* _BLOCK_PRIVATE_H_ */ diff --git a/projects/compiler-rt/BlocksRuntime/data.c b/projects/compiler-rt/BlocksRuntime/data.c new file mode 100644 index 00000000..b4eb02e0 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/data.c @@ -0,0 +1,41 @@ +/* + * data.c + * + * Copyright 2008-2010 Apple, Inc. Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/******************** +NSBlock support + +We allocate space and export a symbol to be used as the Class for the on-stack and malloc'ed copies until ObjC arrives on the scene. These data areas are set up by Foundation to link in as real classes post facto. + +We keep these in a separate file so that we can include the runtime code in test subprojects but not include the data so that compiled code that sees the data in libSystem doesn't get confused by a second copy. Somehow these don't get unified in a common block. +**********************/ + +void * _NSConcreteStackBlock[32] = { 0 }; +void * _NSConcreteMallocBlock[32] = { 0 }; +void * _NSConcreteAutoBlock[32] = { 0 }; +void * _NSConcreteFinalizingBlock[32] = { 0 }; +void * _NSConcreteGlobalBlock[32] = { 0 }; +void * _NSConcreteWeakBlockVariable[32] = { 0 }; + +void _Block_copy_error(void) { +} diff --git a/projects/compiler-rt/BlocksRuntime/runtime.c b/projects/compiler-rt/BlocksRuntime/runtime.c new file mode 100644 index 00000000..a059c223 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/runtime.c @@ -0,0 +1,700 @@ +/* + * runtime.c + * + * Copyright 2008-2010 Apple, Inc. Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "Block_private.h" +#include +#include +#include +#include + +#include "config.h" + +#ifdef HAVE_AVAILABILITY_MACROS_H +#include +#endif /* HAVE_AVAILABILITY_MACROS_H */ + +#ifdef HAVE_TARGET_CONDITIONALS_H +#include +#endif /* HAVE_TARGET_CONDITIONALS_H */ + +#if defined(HAVE_OSATOMIC_COMPARE_AND_SWAP_INT) && defined(HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG) + +#ifdef HAVE_LIBKERN_OSATOMIC_H +#include +#endif /* HAVE_LIBKERN_OSATOMIC_H */ + +#elif defined(__WIN32__) || defined(_WIN32) +#define _CRT_SECURE_NO_WARNINGS 1 +#include + +static __inline bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst) { + /* fixme barrier is overkill -- see objc-os.h */ + long original = InterlockedCompareExchange(dst, newl, oldl); + return (original == oldl); +} + +static __inline bool OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst) { + /* fixme barrier is overkill -- see objc-os.h */ + int original = InterlockedCompareExchange(dst, newi, oldi); + return (original == oldi); +} + +/* + * Check to see if the GCC atomic built-ins are available. If we're on + * a 64-bit system, make sure we have an 8-byte atomic function + * available. + * + */ + +#elif defined(HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT) && defined(HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG) + +static __inline bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst) { + return __sync_bool_compare_and_swap(dst, oldl, newl); +} + +static __inline bool OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst) { + return __sync_bool_compare_and_swap(dst, oldi, newi); +} + +#else +#error unknown atomic compare-and-swap primitive +#endif /* HAVE_OSATOMIC_COMPARE_AND_SWAP_INT && HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG */ + + +/* + * Globals: + */ + +static void *_Block_copy_class = _NSConcreteMallocBlock; +static void *_Block_copy_finalizing_class = _NSConcreteMallocBlock; +static int _Block_copy_flag = BLOCK_NEEDS_FREE; +static int _Byref_flag_initial_value = BLOCK_NEEDS_FREE | 2; + +static const int WANTS_ONE = (1 << 16); + +static bool isGC = false; + +/* + * Internal Utilities: + */ + +#if 0 +static unsigned long int latching_incr_long(unsigned long int *where) { + while (1) { + unsigned long int old_value = *(volatile unsigned long int *)where; + if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) { + return BLOCK_REFCOUNT_MASK; + } + if (OSAtomicCompareAndSwapLong(old_value, old_value+1, (volatile long int *)where)) { + return old_value+1; + } + } +} +#endif /* if 0 */ + +static int latching_incr_int(int *where) { + while (1) { + int old_value = *(volatile int *)where; + if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) { + return BLOCK_REFCOUNT_MASK; + } + if (OSAtomicCompareAndSwapInt(old_value, old_value+1, (volatile int *)where)) { + return old_value+1; + } + } +} + +#if 0 +static int latching_decr_long(unsigned long int *where) { + while (1) { + unsigned long int old_value = *(volatile int *)where; + if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) { + return BLOCK_REFCOUNT_MASK; + } + if ((old_value & BLOCK_REFCOUNT_MASK) == 0) { + return 0; + } + if (OSAtomicCompareAndSwapLong(old_value, old_value-1, (volatile long int *)where)) { + return old_value-1; + } + } +} +#endif /* if 0 */ + +static int latching_decr_int(int *where) { + while (1) { + int old_value = *(volatile int *)where; + if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) { + return BLOCK_REFCOUNT_MASK; + } + if ((old_value & BLOCK_REFCOUNT_MASK) == 0) { + return 0; + } + if (OSAtomicCompareAndSwapInt(old_value, old_value-1, (volatile int *)where)) { + return old_value-1; + } + } +} + + +/* + * GC support stub routines: + */ +#if 0 +#pragma mark GC Support Routines +#endif /* if 0 */ + + +static void *_Block_alloc_default(const unsigned long size, const bool initialCountIsOne, const bool isObject) { + return malloc(size); +} + +static void _Block_assign_default(void *value, void **destptr) { + *destptr = value; +} + +static void _Block_setHasRefcount_default(const void *ptr, const bool hasRefcount) { +} + +static void _Block_do_nothing(const void *aBlock) { } + +static void _Block_retain_object_default(const void *ptr) { + if (!ptr) return; +} + +static void _Block_release_object_default(const void *ptr) { + if (!ptr) return; +} + +static void _Block_assign_weak_default(const void *ptr, void *dest) { + *(void **)dest = (void *)ptr; +} + +static void _Block_memmove_default(void *dst, void *src, unsigned long size) { + memmove(dst, src, (size_t)size); +} + +static void _Block_memmove_gc_broken(void *dest, void *src, unsigned long size) { + void **destp = (void **)dest; + void **srcp = (void **)src; + while (size) { + _Block_assign_default(*srcp, destp); + destp++; + srcp++; + size -= sizeof(void *); + } +} + +/* + * GC support callout functions - initially set to stub routines: + */ + +static void *(*_Block_allocator)(const unsigned long, const bool isOne, const bool isObject) = _Block_alloc_default; +static void (*_Block_deallocator)(const void *) = (void (*)(const void *))free; +static void (*_Block_assign)(void *value, void **destptr) = _Block_assign_default; +static void (*_Block_setHasRefcount)(const void *ptr, const bool hasRefcount) = _Block_setHasRefcount_default; +static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default; +static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default; +static void (*_Block_assign_weak)(const void *dest, void *ptr) = _Block_assign_weak_default; +static void (*_Block_memmove)(void *dest, void *src, unsigned long size) = _Block_memmove_default; + + +/* + * GC support SPI functions - called from ObjC runtime and CoreFoundation: + */ + +/* Public SPI + * Called from objc-auto to turn on GC. + * version 3, 4 arg, but changed 1st arg + */ +void _Block_use_GC( void *(*alloc)(const unsigned long, const bool isOne, const bool isObject), + void (*setHasRefcount)(const void *, const bool), + void (*gc_assign)(void *, void **), + void (*gc_assign_weak)(const void *, void *), + void (*gc_memmove)(void *, void *, unsigned long)) { + + isGC = true; + _Block_allocator = alloc; + _Block_deallocator = _Block_do_nothing; + _Block_assign = gc_assign; + _Block_copy_flag = BLOCK_IS_GC; + _Block_copy_class = _NSConcreteAutoBlock; + /* blocks with ctors & dtors need to have the dtor run from a class with a finalizer */ + _Block_copy_finalizing_class = _NSConcreteFinalizingBlock; + _Block_setHasRefcount = setHasRefcount; + _Byref_flag_initial_value = BLOCK_IS_GC; // no refcount + _Block_retain_object = _Block_do_nothing; + _Block_release_object = _Block_do_nothing; + _Block_assign_weak = gc_assign_weak; + _Block_memmove = gc_memmove; +} + +/* transitional */ +void _Block_use_GC5( void *(*alloc)(const unsigned long, const bool isOne, const bool isObject), + void (*setHasRefcount)(const void *, const bool), + void (*gc_assign)(void *, void **), + void (*gc_assign_weak)(const void *, void *)) { + /* until objc calls _Block_use_GC it will call us; supply a broken internal memmove implementation until then */ + _Block_use_GC(alloc, setHasRefcount, gc_assign, gc_assign_weak, _Block_memmove_gc_broken); +} + + +/* + * Called from objc-auto to alternatively turn on retain/release. + * Prior to this the only "object" support we can provide is for those + * super special objects that live in libSystem, namely dispatch queues. + * Blocks and Block_byrefs have their own special entry points. + * + */ +void _Block_use_RR( void (*retain)(const void *), + void (*release)(const void *)) { + _Block_retain_object = retain; + _Block_release_object = release; +} + +/* + * Internal Support routines for copying: + */ + +#if 0 +#pragma mark Copy/Release support +#endif /* if 0 */ + +/* Copy, or bump refcount, of a block. If really copying, call the copy helper if present. */ +static void *_Block_copy_internal(const void *arg, const int flags) { + struct Block_layout *aBlock; + const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE; + + //printf("_Block_copy_internal(%p, %x)\n", arg, flags); + if (!arg) return NULL; + + + // The following would be better done as a switch statement + aBlock = (struct Block_layout *)arg; + if (aBlock->flags & BLOCK_NEEDS_FREE) { + // latches on high + latching_incr_int(&aBlock->flags); + return aBlock; + } + else if (aBlock->flags & BLOCK_IS_GC) { + // GC refcounting is expensive so do most refcounting here. + if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 1)) { + // Tell collector to hang on this - it will bump the GC refcount version + _Block_setHasRefcount(aBlock, true); + } + return aBlock; + } + else if (aBlock->flags & BLOCK_IS_GLOBAL) { + return aBlock; + } + + // Its a stack block. Make a copy. + if (!isGC) { + struct Block_layout *result = malloc(aBlock->descriptor->size); + if (!result) return (void *)0; + memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first + // reset refcount + result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed + result->flags |= BLOCK_NEEDS_FREE | 1; + result->isa = _NSConcreteMallocBlock; + if (result->flags & BLOCK_HAS_COPY_DISPOSE) { + //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock); + (*aBlock->descriptor->copy)(result, aBlock); // do fixup + } + return result; + } + else { + // Under GC want allocation with refcount 1 so we ask for "true" if wantsOne + // This allows the copy helper routines to make non-refcounted block copies under GC + unsigned long int flags = aBlock->flags; + bool hasCTOR = (flags & BLOCK_HAS_CTOR) != 0; + struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR); + if (!result) return (void *)0; + memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first + // reset refcount + // if we copy a malloc block to a GC block then we need to clear NEEDS_FREE. + flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK); // XXX not needed + if (wantsOne) + flags |= BLOCK_IS_GC | 1; + else + flags |= BLOCK_IS_GC; + result->flags = flags; + if (flags & BLOCK_HAS_COPY_DISPOSE) { + //printf("calling block copy helper...\n"); + (*aBlock->descriptor->copy)(result, aBlock); // do fixup + } + if (hasCTOR) { + result->isa = _NSConcreteFinalizingBlock; + } + else { + result->isa = _NSConcreteAutoBlock; + } + return result; + } +} + + +/* + * Runtime entry points for maintaining the sharing knowledge of byref data blocks. + * + * A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data + * Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr. + * We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment it. + * Otherwise we need to copy it and update the stack forwarding pointer + * XXX We need to account for weak/nonretained read-write barriers. + */ + +static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) { + struct Block_byref **destp = (struct Block_byref **)dest; + struct Block_byref *src = (struct Block_byref *)arg; + + //printf("_Block_byref_assign_copy called, byref destp %p, src %p, flags %x\n", destp, src, flags); + //printf("src dump: %s\n", _Block_byref_dump(src)); + if (src->forwarding->flags & BLOCK_IS_GC) { + ; // don't need to do any more work + } + else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) { + //printf("making copy\n"); + // src points to stack + bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)); + // if its weak ask for an object (only matters under GC) + struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak); + copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack + copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier) + src->forwarding = copy; // patch stack to point to heap copy + copy->size = src->size; + if (isWeak) { + copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning + } + if (src->flags & BLOCK_HAS_COPY_DISPOSE) { + // Trust copy helper to copy everything of interest + // If more than one field shows up in a byref block this is wrong XXX + copy->byref_keep = src->byref_keep; + copy->byref_destroy = src->byref_destroy; + (*src->byref_keep)(copy, src); + } + else { + // just bits. Blast 'em using _Block_memmove in case they're __strong + _Block_memmove( + (void *)©->byref_keep, + (void *)&src->byref_keep, + src->size - sizeof(struct Block_byref_header)); + } + } + // already copied to heap + else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) { + latching_incr_int(&src->forwarding->flags); + } + // assign byref data block pointer into new Block + _Block_assign(src->forwarding, (void **)destp); +} + +// Old compiler SPI +static void _Block_byref_release(const void *arg) { + struct Block_byref *shared_struct = (struct Block_byref *)arg; + int refcount; + + // dereference the forwarding pointer since the compiler isn't doing this anymore (ever?) + shared_struct = shared_struct->forwarding; + + //printf("_Block_byref_release %p called, flags are %x\n", shared_struct, shared_struct->flags); + // To support C++ destructors under GC we arrange for there to be a finalizer for this + // by using an isa that directs the code to a finalizer that calls the byref_destroy method. + if ((shared_struct->flags & BLOCK_NEEDS_FREE) == 0) { + return; // stack or GC or global + } + refcount = shared_struct->flags & BLOCK_REFCOUNT_MASK; + if (refcount <= 0) { + printf("_Block_byref_release: Block byref data structure at %p underflowed\n", arg); + } + else if ((latching_decr_int(&shared_struct->flags) & BLOCK_REFCOUNT_MASK) == 0) { + //printf("disposing of heap based byref block\n"); + if (shared_struct->flags & BLOCK_HAS_COPY_DISPOSE) { + //printf("calling out to helper\n"); + (*shared_struct->byref_destroy)(shared_struct); + } + _Block_deallocator((struct Block_layout *)shared_struct); + } +} + + +/* + * + * API supporting SPI + * _Block_copy, _Block_release, and (old) _Block_destroy + * + */ + +#if 0 +#pragma mark SPI/API +#endif /* if 0 */ + +void *_Block_copy(const void *arg) { + return _Block_copy_internal(arg, WANTS_ONE); +} + + +// API entry point to release a copied Block +void _Block_release(void *arg) { + struct Block_layout *aBlock = (struct Block_layout *)arg; + int32_t newCount; + if (!aBlock) return; + newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK; + if (newCount > 0) return; + // Hit zero + if (aBlock->flags & BLOCK_IS_GC) { + // Tell GC we no longer have our own refcounts. GC will decr its refcount + // and unless someone has done a CFRetain or marked it uncollectable it will + // now be subject to GC reclamation. + _Block_setHasRefcount(aBlock, false); + } + else if (aBlock->flags & BLOCK_NEEDS_FREE) { + if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock); + _Block_deallocator(aBlock); + } + else if (aBlock->flags & BLOCK_IS_GLOBAL) { + ; + } + else { + printf("Block_release called upon a stack Block: %p, ignored\n", (void *)aBlock); + } +} + + + +// Old Compiler SPI point to release a copied Block used by the compiler in dispose helpers +static void _Block_destroy(const void *arg) { + struct Block_layout *aBlock; + if (!arg) return; + aBlock = (struct Block_layout *)arg; + if (aBlock->flags & BLOCK_IS_GC) { + // assert(aBlock->Block_flags & BLOCK_HAS_CTOR); + return; // ignore, we are being called because of a DTOR + } + _Block_release(aBlock); +} + + + +/* + * + * SPI used by other layers + * + */ + +// SPI, also internal. Called from NSAutoBlock only under GC +void *_Block_copy_collectable(const void *aBlock) { + return _Block_copy_internal(aBlock, 0); +} + + +// SPI +unsigned long int Block_size(void *arg) { + return ((struct Block_layout *)arg)->descriptor->size; +} + + +#if 0 +#pragma mark Compiler SPI entry points +#endif /* if 0 */ + + +/******************************************************* + +Entry points used by the compiler - the real API! + + +A Block can reference four different kinds of things that require help when the Block is copied to the heap. +1) C++ stack based objects +2) References to Objective-C objects +3) Other Blocks +4) __block variables + +In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers. The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign. The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest. + +The flags parameter of _Block_object_assign and _Block_object_dispose is set to + * BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object, + * BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and + * BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable. +If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16). + +So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24. + +When a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions. Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor. And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied. + +So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities: + __block id 128+3 + __weak block id 128+3+16 + __block (^Block) 128+7 + __weak __block (^Block) 128+7+16 + +The implementation of the two routines would be improved by switch statements enumerating the eight cases. + +********************************************************/ + +/* + * When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point + * to do the assignment. + */ +void _Block_object_assign(void *destAddr, const void *object, const int flags) { + //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags); + if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) { + if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) { + _Block_assign_weak(object, destAddr); + } + else { + // do *not* retain or *copy* __block variables whatever they are + _Block_assign((void *)object, destAddr); + } + } + else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF) { + // copying a __block reference from the stack Block to the heap + // flags will indicate if it holds a __weak reference and needs a special isa + _Block_byref_assign_copy(destAddr, object, flags); + } + // (this test must be before next one) + else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) { + // copying a Block declared variable from the stack Block to the heap + _Block_assign(_Block_copy_internal(object, flags), destAddr); + } + // (this test must be after previous one) + else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) { + //printf("retaining object at %p\n", object); + _Block_retain_object(object); + //printf("done retaining object at %p\n", object); + _Block_assign((void *)object, destAddr); + } +} + +// When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point +// to help dispose of the contents +// Used initially only for __attribute__((NSObject)) marked pointers. +void _Block_object_dispose(const void *object, const int flags) { + //printf("_Block_object_dispose(%p, %x)\n", object, flags); + if (flags & BLOCK_FIELD_IS_BYREF) { + // get rid of the __block data structure held in a Block + _Block_byref_release(object); + } + else if ((flags & (BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_BLOCK) { + // get rid of a referenced Block held by this Block + // (ignore __block Block variables, compiler doesn't need to call us) + _Block_destroy(object); + } + else if ((flags & (BLOCK_FIELD_IS_WEAK|BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_OBJECT) { + // get rid of a referenced object held by this Block + // (ignore __block object variables, compiler doesn't need to call us) + _Block_release_object(object); + } +} + + +/* + * Debugging support: + */ +#if 0 +#pragma mark Debugging +#endif /* if 0 */ + + +const char *_Block_dump(const void *block) { + struct Block_layout *closure = (struct Block_layout *)block; + static char buffer[512]; + char *cp = buffer; + if (closure == NULL) { + sprintf(cp, "NULL passed to _Block_dump\n"); + return buffer; + } + if (! (closure->flags & BLOCK_HAS_DESCRIPTOR)) { + printf("Block compiled by obsolete compiler, please recompile source for this Block\n"); + exit(1); + } + cp += sprintf(cp, "^%p (new layout) =\n", (void *)closure); + if (closure->isa == NULL) { + cp += sprintf(cp, "isa: NULL\n"); + } + else if (closure->isa == _NSConcreteStackBlock) { + cp += sprintf(cp, "isa: stack Block\n"); + } + else if (closure->isa == _NSConcreteMallocBlock) { + cp += sprintf(cp, "isa: malloc heap Block\n"); + } + else if (closure->isa == _NSConcreteAutoBlock) { + cp += sprintf(cp, "isa: GC heap Block\n"); + } + else if (closure->isa == _NSConcreteGlobalBlock) { + cp += sprintf(cp, "isa: global Block\n"); + } + else if (closure->isa == _NSConcreteFinalizingBlock) { + cp += sprintf(cp, "isa: finalizing Block\n"); + } + else { + cp += sprintf(cp, "isa?: %p\n", (void *)closure->isa); + } + cp += sprintf(cp, "flags:"); + if (closure->flags & BLOCK_HAS_DESCRIPTOR) { + cp += sprintf(cp, " HASDESCRIPTOR"); + } + if (closure->flags & BLOCK_NEEDS_FREE) { + cp += sprintf(cp, " FREEME"); + } + if (closure->flags & BLOCK_IS_GC) { + cp += sprintf(cp, " ISGC"); + } + if (closure->flags & BLOCK_HAS_COPY_DISPOSE) { + cp += sprintf(cp, " HASHELP"); + } + if (closure->flags & BLOCK_HAS_CTOR) { + cp += sprintf(cp, " HASCTOR"); + } + cp += sprintf(cp, "\nrefcount: %u\n", closure->flags & BLOCK_REFCOUNT_MASK); + cp += sprintf(cp, "invoke: %p\n", (void *)(uintptr_t)closure->invoke); + { + struct Block_descriptor *dp = closure->descriptor; + cp += sprintf(cp, "descriptor: %p\n", (void *)dp); + cp += sprintf(cp, "descriptor->reserved: %lu\n", dp->reserved); + cp += sprintf(cp, "descriptor->size: %lu\n", dp->size); + + if (closure->flags & BLOCK_HAS_COPY_DISPOSE) { + cp += sprintf(cp, "descriptor->copy helper: %p\n", (void *)(uintptr_t)dp->copy); + cp += sprintf(cp, "descriptor->dispose helper: %p\n", (void *)(uintptr_t)dp->dispose); + } + } + return buffer; +} + + +const char *_Block_byref_dump(struct Block_byref *src) { + static char buffer[256]; + char *cp = buffer; + cp += sprintf(cp, "byref data block %p contents:\n", (void *)src); + cp += sprintf(cp, " forwarding: %p\n", (void *)src->forwarding); + cp += sprintf(cp, " flags: 0x%x\n", src->flags); + cp += sprintf(cp, " size: %d\n", src->size); + if (src->flags & BLOCK_HAS_COPY_DISPOSE) { + cp += sprintf(cp, " copy helper: %p\n", (void *)(uintptr_t)src->byref_keep); + cp += sprintf(cp, " dispose helper: %p\n", (void *)(uintptr_t)src->byref_destroy); + } + return buffer; +} + diff --git a/projects/compiler-rt/BlocksRuntime/tests/block-static.c b/projects/compiler-rt/BlocksRuntime/tests/block-static.c new file mode 100644 index 00000000..d38c816c --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/block-static.c @@ -0,0 +1,25 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// testfilerunner CONFIG + +#include + + +int main(int argc, char **argv) { + static int numberOfSquesals = 5; + + ^{ numberOfSquesals = 6; }(); + + if (numberOfSquesals == 6) { + printf("%s: success\n", argv[0]); + return 0; + } + printf("**** did not update static local, rdar://6177162\n"); + return 1; + +} + diff --git a/projects/compiler-rt/BlocksRuntime/tests/blockimport.c b/projects/compiler-rt/BlocksRuntime/tests/blockimport.c new file mode 100644 index 00000000..178fce43 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/blockimport.c @@ -0,0 +1,51 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * blockimport.c + * testObjects + * + * Created by Blaine Garst on 10/13/08. + * + */ + + +// +// pure C nothing more needed +// CONFIG rdar://6289344 + +#include +#include +#include + + + + +int main(int argc, char *argv[]) { + int i = 1; + int (^intblock)(void) = ^{ return i*10; }; + + void (^vv)(void) = ^{ + if (argc > 0) { + printf("intblock returns %d\n", intblock()); + } + }; + +#if 0 + //printf("Block dump %s\n", _Block_dump(vv)); + { + struct Block_layout *layout = (struct Block_layout *)(void *)vv; + printf("isa %p\n", layout->isa); + printf("flags %x\n", layout->flags); + printf("descriptor %p\n", layout->descriptor); + printf("descriptor->size %d\n", layout->descriptor->size); + } +#endif + void (^vvcopy)(void) = Block_copy(vv); + Block_release(vvcopy); + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefaccess.c b/projects/compiler-rt/BlocksRuntime/tests/byrefaccess.c new file mode 100644 index 00000000..45655533 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefaccess.c @@ -0,0 +1,34 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// byrefaccess.m +// test that byref access to locals is accurate +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// +// CONFIG + +#include + + +void callVoidVoid(void (^closure)(void)) { + closure(); +} + +int main(int argc, char *argv[]) { + __block int i = 10; + + callVoidVoid(^{ ++i; }); + + if (i != 11) { + printf("*** %s didn't update i\n", argv[0]); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefcopy.c b/projects/compiler-rt/BlocksRuntime/tests/byrefcopy.c new file mode 100644 index 00000000..513b63c2 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefcopy.c @@ -0,0 +1,41 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// byrefcopy.m +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// + +#include +#include +#include + +// CONFIG + +void callVoidVoid(void (^closure)(void)) { + closure(); +} + +int main(int argc, char *argv[]) { + int __block i = 10; + + void (^block)(void) = ^{ ++i; }; + //printf("original (old style) is %s\n", _Block_dump_old(block)); + //printf("original (new style) is %s\n", _Block_dump(block)); + void (^blockcopy)(void) = Block_copy(block); + //printf("copy is %s\n", _Block_dump(blockcopy)); + // use a copy & see that it updates i + callVoidVoid(block); + + if (i != 11) { + printf("*** %s didn't update i\n", argv[0]); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefcopycopy.c b/projects/compiler-rt/BlocksRuntime/tests/byrefcopycopy.c new file mode 100644 index 00000000..d6fafc15 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefcopycopy.c @@ -0,0 +1,46 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6255170 + +#include +#include +#include +#include +#include +#include + + +int +main(int argc, char *argv[]) +{ + __block int var = 0; + int shouldbe = 0; + void (^b)(void) = ^{ var++; /*printf("var is at %p with value %d\n", &var, var);*/ }; + __typeof(b) _b; + //printf("before copy...\n"); + b(); ++shouldbe; + size_t i; + + for (i = 0; i < 10; i++) { + _b = Block_copy(b); // make a new copy each time + assert(_b); + ++shouldbe; + _b(); // should still update the stack + Block_release(_b); + } + + //printf("after...\n"); + b(); ++shouldbe; + + if (var != shouldbe) { + printf("Hmm, var is %d but should be %d\n", var, shouldbe); + return 1; + } + printf("%s: Success!!\n", argv[0]); + + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefcopyinner.c b/projects/compiler-rt/BlocksRuntime/tests/byrefcopyinner.c new file mode 100644 index 00000000..07770933 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefcopyinner.c @@ -0,0 +1,32 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include +#include + +// CONFIG rdar://6225809 +// fixed in 5623 + +int main(int argc, char *argv[]) { + __block int a = 42; + int* ap = &a; // just to keep the address on the stack. + + void (^b)(void) = ^{ + //a; // workaround, a should be implicitly imported + Block_copy(^{ + a = 2; + }); + }; + + Block_copy(b); + + if(&a == ap) { + printf("**** __block heap storage should have been created at this point\n"); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefcopyint.c b/projects/compiler-rt/BlocksRuntime/tests/byrefcopyint.c new file mode 100644 index 00000000..d632f88a --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefcopyint.c @@ -0,0 +1,69 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * byrefcopyint.c + * testObjects + * + * Created by Blaine Garst on 12/1/08. + * + */ + +// +// byrefcopyid.m +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// + +// Tests copying of blocks with byref ints +// CONFIG rdar://6414583 -C99 + +#include +#include +#include +#include + + + + +typedef void (^voidVoid)(void); + +voidVoid dummy; + +void callVoidVoid(voidVoid closure) { + closure(); +} + + +voidVoid testRoutine(const char *whoami) { + __block int dumbo = strlen(whoami); + dummy = ^{ + //printf("incring dumbo from %d\n", dumbo); + ++dumbo; + }; + + + voidVoid copy = Block_copy(dummy); + + + return copy; +} + +int main(int argc, char *argv[]) { + voidVoid array[100]; + for (int i = 0; i < 100; ++i) { + array[i] = testRoutine(argv[0]); + array[i](); + } + for (int i = 0; i < 100; ++i) { + Block_release(array[i]); + } + + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefcopystack.c b/projects/compiler-rt/BlocksRuntime/tests/byrefcopystack.c new file mode 100644 index 00000000..d119afa3 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefcopystack.c @@ -0,0 +1,41 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// byrefcopystack.m +// testObjects +// +// Created by Blaine Garst on 5/13/08. +// + + + +#include +#include + +// CONFIG rdar://6255170 + +void (^bumpi)(void); +int (^geti)(void); + +void setClosures() { + int __block i = 10; + bumpi = Block_copy(^{ ++i; }); + geti = Block_copy(^{ return i; }); +} + +int main(int argc, char *argv[]) { + setClosures(); + bumpi(); + int i = geti(); + + if (i != 11) { + printf("*** %s didn't update i\n", argv[0]); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefsanity.c b/projects/compiler-rt/BlocksRuntime/tests/byrefsanity.c new file mode 100644 index 00000000..dfa16b0d --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefsanity.c @@ -0,0 +1,73 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG + + +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + __block int var = 0; + void (^b)(void) = ^{ var++; }; + + //sanity(b); + b(); + printf("%s: success!\n", argv[0]); + return 0; +} + + +#if 1 +/* replicated internal data structures: BEWARE, MAY CHANGE!!! */ + +enum { + BLOCK_REFCOUNT_MASK = (0xffff), + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_NO_COPY = (1 << 26), // interim byref: no copies allowed + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), +}; + +struct byref_id { + struct byref_id *forwarding; + int flags;//refcount; + int size; + void (*byref_keep)(struct byref_id *dst, struct byref_id *src); + void (*byref_destroy)(struct byref_id *); + int var; +}; +struct Block_basic2 { + void *isa; + int Block_flags; // int32_t + int Block_size; // XXX should be packed into Block_flags + void (*Block_invoke)(void *); + void (*Block_copy)(void *dst, void *src); + void (*Block_dispose)(void *); + struct byref_id *ref; +}; + +void sanity(void *arg) { + struct Block_basic2 *bb = (struct Block_basic2 *)arg; + if ( ! (bb->Block_flags & BLOCK_HAS_COPY_DISPOSE)) { + printf("missing copy/dispose helpers for byref data\n"); + exit(1); + } + struct byref_id *ref = bb->ref; + if (ref->forwarding != ref) { + printf("forwarding pointer should be %p but is %p\n", ref, ref->forwarding); + exit(1); + } +} +#endif + + + diff --git a/projects/compiler-rt/BlocksRuntime/tests/byrefstruct.c b/projects/compiler-rt/BlocksRuntime/tests/byrefstruct.c new file mode 100644 index 00000000..a3dc44e2 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/byrefstruct.c @@ -0,0 +1,57 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import +#import +#import + +typedef struct { + unsigned long ps[30]; + int qs[30]; +} BobTheStruct; + +int main (int argc, const char * argv[]) { + __block BobTheStruct fiddly; + BobTheStruct copy; + + void (^incrementFiddly)() = ^{ + int i; + for(i=0; i<30; i++) { + fiddly.ps[i]++; + fiddly.qs[i]++; + } + }; + + memset(&fiddly, 0xA5, sizeof(fiddly)); + memset(©, 0x2A, sizeof(copy)); + + int i; + for(i=0; i<30; i++) { + fiddly.ps[i] = i * i * i; + fiddly.qs[i] = -i * i * i; + } + + copy = fiddly; + incrementFiddly(); + + if ( © == &fiddly ) { + printf("%s: struct wasn't copied.", argv[0]); + exit(1); + } + for(i=0; i<30; i++) { + //printf("[%d]: fiddly.ps: %lu, copy.ps: %lu, fiddly.qs: %d, copy.qs: %d\n", i, fiddly.ps[i], copy.ps[i], fiddly.qs[i], copy.qs[i]); + if ( (fiddly.ps[i] != copy.ps[i] + 1) || (fiddly.qs[i] != copy.qs[i] + 1) ) { + printf("%s: struct contents were not incremented.", argv[0]); + exit(1); + } + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/c99.c b/projects/compiler-rt/BlocksRuntime/tests/c99.c new file mode 100644 index 00000000..8f31ab3f --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/c99.c @@ -0,0 +1,20 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// c99.m +// +// CONFIG C99 rdar://problem/6399225 + +#import +#import + +int main(int argc, char *argv[]) { + void (^blockA)(void) = ^ { ; }; + blockA(); + printf("%s: success\n", argv[0]); + exit(0); +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/cast.c b/projects/compiler-rt/BlocksRuntime/tests/cast.c new file mode 100644 index 00000000..5bef2c19 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/cast.c @@ -0,0 +1,37 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * cast.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +// PURPOSE should allow casting of a Block reference to an arbitrary pointer and back +// CONFIG open + +#include + + + +int main(int argc, char *argv[]) { + + void (^aBlock)(void); + int *ip; + char *cp; + double *dp; + + ip = (int *)aBlock; + cp = (char *)aBlock; + dp = (double *)aBlock; + aBlock = (void (^)(void))ip; + aBlock = (void (^)(void))cp; + aBlock = (void (^)(void))dp; + printf("%s: success", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/constassign.c b/projects/compiler-rt/BlocksRuntime/tests/constassign.c new file mode 100644 index 00000000..537cb2df --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/constassign.c @@ -0,0 +1,28 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// constassign.c +// bocktest +// +// Created by Blaine Garst on 3/21/08. +// +// shouldn't be able to assign to a const pointer +// CONFIG error: assignment of read-only + +#import + +void foo(void) { printf("I'm in foo\n"); } +void bar(void) { printf("I'm in bar\n"); } + +int main(int argc, char *argv[]) { + void (*const fptr)(void) = foo; + void (^const blockA)(void) = ^ { printf("hello\n"); }; + blockA = ^ { printf("world\n"); } ; + fptr = bar; + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/copy-block-literal-rdar6439600.c b/projects/compiler-rt/BlocksRuntime/tests/copy-block-literal-rdar6439600.c new file mode 100644 index 00000000..6fa488ee --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/copy-block-literal-rdar6439600.c @@ -0,0 +1,29 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG open rdar://6439600 + +#import +#import + +#define NUMBER_OF_BLOCKS 100 +int main (int argc, const char * argv[]) { + int (^x[NUMBER_OF_BLOCKS])(); + int i; + + for(i=0; i +#include + +// CONFIG C++ rdar://6243400,rdar://6289367 + + +int constructors = 0; +int destructors = 0; + + +#define CONST const + +class TestObject +{ +public: + TestObject(CONST TestObject& inObj); + TestObject(); + ~TestObject(); + + TestObject& operator=(CONST TestObject& inObj); + + int version() CONST { return _version; } +private: + mutable int _version; +}; + +TestObject::TestObject(CONST TestObject& inObj) + +{ + ++constructors; + _version = inObj._version; + //printf("%p (%d) -- TestObject(const TestObject&) called\n", this, _version); +} + + +TestObject::TestObject() +{ + _version = ++constructors; + //printf("%p (%d) -- TestObject() called\n", this, _version); +} + + +TestObject::~TestObject() +{ + //printf("%p -- ~TestObject() called\n", this); + ++destructors; +} + + +TestObject& TestObject::operator=(CONST TestObject& inObj) +{ + //printf("%p -- operator= called\n", this); + _version = inObj._version; + return *this; +} + + + +void testRoutine() { + TestObject one; + + void (^b)(void) = ^{ printf("my const copy of one is %d\n", one.version()); }; +} + + + +int main(int argc, char *argv[]) { + testRoutine(); + if (constructors == 0) { + printf("No copy constructors!!!\n"); + return 1; + } + if (constructors != destructors) { + printf("%d constructors but only %d destructors\n", constructors, destructors); + return 1; + } + printf("%s:success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/copynull.c b/projects/compiler-rt/BlocksRuntime/tests/copynull.c new file mode 100644 index 00000000..c49e499f --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/copynull.c @@ -0,0 +1,37 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * copynull.c + * testObjects + * + * Created by Blaine Garst on 10/15/08. + * + */ + +#import +#import +#import + +// CONFIG rdar://6295848 + +int main(int argc, char *argv[]) { + + void (^block)(void) = (void (^)(void))0; + void (^blockcopy)(void) = Block_copy(block); + + if (blockcopy != (void (^)(void))0) { + printf("whoops, somehow we copied NULL!\n"); + return 1; + } + // make sure we can also + Block_release(blockcopy); + // and more secretly + //_Block_destroy(blockcopy); + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/dispatch_async.c b/projects/compiler-rt/BlocksRuntime/tests/dispatch_async.c new file mode 100644 index 00000000..e3e517c5 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/dispatch_async.c @@ -0,0 +1,57 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include + +#include +#include +//#import +#include + +// CONFIG rdar://problem/6371811 + +const char *whoami = "nobody"; + +void EnqueueStuff(dispatch_queue_t q) +{ + __block CFIndex counter; + + // above call has a side effect: it works around: + // __block variables not implicitly imported into intermediate scopes + dispatch_async(q, ^{ + counter = 0; + }); + + + dispatch_async(q, ^{ + //printf("outer block.\n"); + counter++; + dispatch_async(q, ^{ + //printf("inner block.\n"); + counter--; + if(counter == 0) { + printf("%s: success\n", whoami); + exit(0); + } + }); + if(counter == 0) { + printf("already done? inconceivable!\n"); + exit(1); + } + }); +} + +int main (int argc, const char * argv[]) { + dispatch_queue_t q = dispatch_queue_create("queue", NULL); + + whoami = argv[0]; + + EnqueueStuff(q); + + dispatch_main(); + printf("shouldn't get here\n"); + return 1; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/dispatch_call_Block_with_release.c b/projects/compiler-rt/BlocksRuntime/tests/dispatch_call_Block_with_release.c new file mode 100644 index 00000000..9e06f69b --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/dispatch_call_Block_with_release.c @@ -0,0 +1,31 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include +#include + +// CONFIG + +void callsomething(const char *format, int argument) { +} + +void +dispatch_call_Block_with_release2(void *block) +{ + void (^b)(void) = (void (^)(void))block; + b(); + Block_release(b); +} + +int main(int argc, char *argv[]) { + void (^b1)(void) = ^{ callsomething("argc is %d\n", argc); }; + void (^b2)(void) = ^{ callsomething("hellow world\n", 0); }; // global block now + + dispatch_call_Block_with_release2(Block_copy(b1)); + dispatch_call_Block_with_release2(Block_copy(b2)); + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/fail.c b/projects/compiler-rt/BlocksRuntime/tests/fail.c new file mode 100644 index 00000000..28dbc2d1 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/fail.c @@ -0,0 +1,107 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * fail.c + * testObjects + * + * Created by Blaine Garst on 9/16/08. + * + */ +#include +#include +#include +#include +#include +#include + + +bool readfile(char *buffer, const char *from) { + int fd = open(from, 0); + if (fd < 0) return false; + int count = read(fd, buffer, 512); + if (count < 0) return false; + buffer[count] = 0; // zap newline + return true; +} + +// basic idea, take compiler args, run compiler, and verify that expected failure matches any existing one + +int main(int argc, char *argv[]) { + if (argc == 1) return 0; + char *copy[argc+1]; // make a copy + // find and strip off -e "errorfile" + char *errorfile = NULL; + int counter = 0, i = 0; + for (i = 1; i < argc; ++i) { // skip 0 arg which is "fail" + if (!strncmp(argv[i], "-e", 2)) { + errorfile = argv[++i]; + } + else { + copy[counter++] = argv[i]; + } + } + copy[counter] = NULL; + pid_t child = fork(); + char buffer[512]; + if (child == 0) { + // in child + sprintf(buffer, "/tmp/errorfile_%d", getpid()); + close(1); + int fd = creat(buffer, 0777); + if (fd != 1) { + fprintf(stderr, "didn't open custom error file %s as 1, got %d\n", buffer, fd); + exit(1); + } + close(2); + dup(1); + int result = execv(copy[0], copy); + exit(10); + } + if (child < 0) { + printf("fork failed\n"); + exit(1); + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(1); + } + if (WEXITSTATUS(status) == 0) { + printf("compiler exited normally, not good under these circumstances\n"); + exit(1); + } + //printf("exit status of child %d was %d\n", child, WEXITSTATUS(status)); + sprintf(buffer, "/tmp/errorfile_%d", child); + if (errorfile) { + //printf("ignoring error file: %s\n", errorfile); + char desired[512]; + char got[512]; + bool gotErrorFile = readfile(desired, errorfile); + bool gotOutput = readfile(got, buffer); + if (!gotErrorFile && gotOutput) { + printf("didn't read errorfile %s, it should have something from\n*****\n%s\n*****\nin it.\n", + errorfile, got); + exit(1); + } + else if (gotErrorFile && gotOutput) { + char *where = strstr(got, desired); + if (!where) { + printf("didn't find contents of %s in %s\n", errorfile, buffer); + exit(1); + } + } + else { + printf("errorfile %s and output %s inconsistent\n", errorfile, buffer); + exit(1); + } + } + unlink(buffer); + printf("success\n"); + exit(0); +} + diff --git a/projects/compiler-rt/BlocksRuntime/tests/flagsisa.c b/projects/compiler-rt/BlocksRuntime/tests/flagsisa.c new file mode 100644 index 00000000..5d4b2dcb --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/flagsisa.c @@ -0,0 +1,21 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include + +/* CONFIG rdar://6310599 + */ + +int main(int argc, char *argv[]) +{ + __block int flags; + __block void *isa; + + ^{ flags=1; isa = (void *)isa; }; + printf("%s: success\n", argv[0]); + return 0; +} + diff --git a/projects/compiler-rt/BlocksRuntime/tests/globalexpression.c b/projects/compiler-rt/BlocksRuntime/tests/globalexpression.c new file mode 100644 index 00000000..eeedd75e --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/globalexpression.c @@ -0,0 +1,42 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// testfilerunner CONFIG + +#import +#import + +int global; + +void (^gblock)(int) = ^(int x){ global = x; }; + +int main(int argc, char *argv[]) { + gblock(1); + if (global != 1) { + printf("%s: *** did not set global to 1\n", argv[0]); + return 1; + } + void (^gblockcopy)(int) = Block_copy(gblock); + if (gblockcopy != gblock) { + printf("global copy %p not a no-op %p\n", (void *)gblockcopy, (void *)gblock); + return 1; + } + Block_release(gblockcopy); + gblock(3); + if (global != 3) { + printf("%s: *** did not set global to 3\n", argv[0]); + return 1; + } + gblockcopy = Block_copy(gblock); + gblockcopy(5); + if (global != 5) { + printf("%s: *** did not set global to 5\n", argv[0]); + return 1; + } + printf("%s: Success!\n", argv[0]); + return 0; +} + diff --git a/projects/compiler-rt/BlocksRuntime/tests/goto.c b/projects/compiler-rt/BlocksRuntime/tests/goto.c new file mode 100644 index 00000000..7e5b08ad --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/goto.c @@ -0,0 +1,34 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * goto.c + * testObjects + * + * Created by Blaine Garst on 10/17/08. + * + */ + +// CONFIG rdar://6289031 + +#include + +int main(int argc, char *argv[]) +{ + __block int val = 0; + + ^{ val = 1; }(); + + if (val == 0) { + goto out_bad; // error: local byref variable val is in the scope of this goto + } + + printf("%s: Success!\n", argv[0]); + return 0; +out_bad: + printf("%s: val not updated!\n", argv[0]); + return 1; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/hasdescriptor.c b/projects/compiler-rt/BlocksRuntime/tests/hasdescriptor.c new file mode 100644 index 00000000..429adb9b --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/hasdescriptor.c @@ -0,0 +1,29 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + + + +// CONFIG C + +#include +#include + + +int main(int argc, char *argv[]) { + void (^inner)(void) = ^ { printf("argc was %d\n", argc); }; + void (^outer)(void) = ^{ + inner(); + inner(); + }; + //printf("size of inner is %ld\n", Block_size(inner)); + //printf("size of outer is %ld\n", Block_size(outer)); + if (Block_size(inner) != Block_size(outer)) { + printf("not the same size, using old compiler??\n"); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/josh.C b/projects/compiler-rt/BlocksRuntime/tests/josh.C new file mode 100644 index 00000000..dbc7369e --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/josh.C @@ -0,0 +1,32 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG C++ GC RR open rdar://6347910 + + + +struct MyStruct { + int something; +}; + +struct TestObject { + + void test(void){ + { + MyStruct first; // works + } + void (^b)(void) = ^{ + MyStruct inner; // fails to compile! + }; + } +}; + + + +int main(int argc, char *argv[]) { + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/k-and-r.c b/projects/compiler-rt/BlocksRuntime/tests/k-and-r.c new file mode 100644 index 00000000..16b9cc64 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/k-and-r.c @@ -0,0 +1,33 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG error: incompatible block pointer types assigning + +#import +#import + +int main(int argc, char *argv[]) { +#ifndef __cplusplus + char (^rot13)(); + rot13 = ^(char c) { return (char)(((c - 'a' + 13) % 26) + 'a'); }; + char n = rot13('a'); + char c = rot13('p'); + if ( n != 'n' || c != 'c' ) { + printf("%s: rot13('a') returned %c, rot13('p') returns %c\n", argv[0], n, c); + exit(1); + } +#else +// yield characteristic error message for C++ +#error incompatible block pointer types assigning +#endif +#ifndef __clang__ +// yield characteristic error message for C++ +#error incompatible block pointer types assigning +#endif + printf("%s: success\n", argv[0]); + exit(0); +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/large-struct.c b/projects/compiler-rt/BlocksRuntime/tests/large-struct.c new file mode 100644 index 00000000..1867bd02 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/large-struct.c @@ -0,0 +1,51 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import +#import +#import + +typedef struct { + unsigned long ps[30]; + int qs[30]; +} BobTheStruct; + +int main (int argc, const char * argv[]) { + BobTheStruct inny; + BobTheStruct outty; + BobTheStruct (^copyStruct)(BobTheStruct); + int i; + + memset(&inny, 0xA5, sizeof(inny)); + memset(&outty, 0x2A, sizeof(outty)); + + for(i=0; i<30; i++) { + inny.ps[i] = i * i * i; + inny.qs[i] = -i * i * i; + } + + copyStruct = ^(BobTheStruct aBigStruct){ return aBigStruct; }; // pass-by-value intrinsically copies the argument + + outty = copyStruct(inny); + + if ( &inny == &outty ) { + printf("%s: struct wasn't copied.", argv[0]); + exit(1); + } + for(i=0; i<30; i++) { + if ( (inny.ps[i] != outty.ps[i]) || (inny.qs[i] != outty.qs[i]) ) { + printf("%s: struct contents did not match.", argv[0]); + exit(1); + } + } + + printf("%s: success\n", argv[0]); + + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/localisglobal.c b/projects/compiler-rt/BlocksRuntime/tests/localisglobal.c new file mode 100644 index 00000000..75a79dff --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/localisglobal.c @@ -0,0 +1,42 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * localisglobal.c + * testObjects + * + * Created by Blaine Garst on 9/29/08. + * + * works in all configurations + * CONFIG rdar://6230297 + */ + +#include + +void (^global)(void) = ^{ printf("hello world\n"); }; + +int aresame(void *first, void *second) { + long *f = (long *)first; + long *s = (long *)second; + return *f == *s; +} +int main(int argc, char *argv[]) { + int i = 10; + void (^local)(void) = ^ { printf("hi %d\n", i); }; + void (^localisglobal)(void) = ^ { printf("hi\n"); }; + + if (aresame(local, localisglobal)) { + printf("local block could be global, but isn't\n"); + return 1; + } + if (!aresame(global, localisglobal)) { + printf("local block is not global, not stack, what is it??\n"); + return 1; + } + printf("%s: success\n", argv[0]); + return 0; + +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/macro.c b/projects/compiler-rt/BlocksRuntime/tests/macro.c new file mode 100644 index 00000000..988c0689 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/macro.c @@ -0,0 +1,14 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG open rdar://6718399 +#include + +void foo() { + void (^bbb)(void) = Block_copy(^ { + int j, cnt; + }); +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/makefile b/projects/compiler-rt/BlocksRuntime/tests/makefile new file mode 100644 index 00000000..2734bcae --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/makefile @@ -0,0 +1,70 @@ +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +CCDIR=/usr/bin +#CCDIR=/Volumes/Keep/gcc/usr/bin + +all: std + +clean: + rm -fr *.dSYM *.o *-bin testfilerunner + +TFR = ~public/bin/testfilerunner + +testfilerunner: testfilerunner.h testfilerunner.m + gcc -fobjc-gc-only -g -arch x86_64 -arch i386 -std=gnu99 testfilerunner.m -o testfilerunner -framework Foundation + +tests: + grep CONFIG *.[cmCM] | $(TFR) $(CCDIR) -- + +open: + grep CONFIG *.[cmCM] | $(TFR) $(CCDIR) -open -- + +fast: + grep CONFIG *.[cmCM] | $(TFR) -fast $(CCDIR) -- + +std: + grep CONFIG *.[cmCM] | $(TFR) -- + +clang: + grep CONFIG *.[cmCM] | $(TFR) -clang -fast -- + +fastd: + grep CONFIG *.[cmCM] | $(TFR) -fast -- + + +# Hack Alert: arguably most of the following belongs in libclosure's Makefile; sticking it here until I get around to grokking what goes on in that file. +sudid: + @echo Enabling sudo: # Hack Alert: enable sudo first thing so we don't hang at the password prompt later + @sudo echo Thanks + + +RootsDirectory ?= /tmp/ +# Note: the libsystem project (built by the libsystemroot target below) uses the ALTUSRLOCALLIBSYSTEM variable, so we use it here to maintain parity +ALTUSRLOCALLIBSYSTEM ?= $(RootsDirectory)/alt-usr-local-lib-system/ +altusrlocallibsystem: + ditto /usr/local/lib/system $(ALTUSRLOCALLIBSYSTEM) # FIXME: conditionalize this copy + + +# ER: option to not require extra privileges (-nosudo or somesuch) +Buildit ?= ~rc/bin/buildit -rootsDirectory $(RootsDirectory) -arch i386 -arch ppc -arch x86_64 +blocksroot: sudid clean altusrlocallibsystem + sudo $(Buildit) .. + ditto $(RootsDirectory)/libclosure.roots/libclosure~dst/usr/local/lib/system $(ALTUSRLOCALLIBSYSTEM) + + +LibsystemVersion ?= 121 +LibsystemPath ?= ~rc/Software/SnowLeopard/Projects/Libsystem/Libsystem-$(LibsystemVersion) +LibsystemTmpPath ?= $(RootsDirectory)/Libsystem-$(LibsystemVersion) +libsystemroot: blocksroot + ditto $(LibsystemPath) $(LibsystemTmpPath) # FIXME: conditionalize this copy + sudo ALTUSRLOCALLIBSYSTEM=$(ALTUSRLOCALLIBSYSTEM) $(Buildit) $(LibsystemTmpPath) + + +# Defaults to product of the libsystemroot target but does not automatically rebuild that, make both targets if you want a fresh root +LibsystemRootPath ?= $(RootsDirectory)/Libsystem-$(LibsystemVersion).roots/Libsystem-$(LibsystemVersion)~dst/usr/lib/ +roottests: + grep CONFIG *.[cmCM] | $(TFR) -dyld $(LibsystemRootPath) -- # FIXME: figure out if I can "call" the std target instead of duplicating it + diff --git a/projects/compiler-rt/BlocksRuntime/tests/modglobal.c b/projects/compiler-rt/BlocksRuntime/tests/modglobal.c new file mode 100644 index 00000000..562d5a5c --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/modglobal.c @@ -0,0 +1,18 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +#include + +// CONFIG + +int AGlobal; + +int main(int argc, char *argv[]) { + void (^f)(void) = ^ { AGlobal++; }; + + printf("%s: success\n", argv[0]); + return 0; + +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/nestedimport.c b/projects/compiler-rt/BlocksRuntime/tests/nestedimport.c new file mode 100644 index 00000000..e8066922 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/nestedimport.c @@ -0,0 +1,44 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// nestedimport.m +// testObjects +// +// Created by Blaine Garst on 6/24/08. +// +// pure C nothing more needed +// CONFIG + + +#include +#include + + +int Global = 0; + +void callVoidVoid(void (^closure)(void)) { + closure(); +} + +int main(int argc, char *argv[]) { + int i = 1; + + void (^vv)(void) = ^{ + if (argc > 0) { + callVoidVoid(^{ Global = i; }); + } + }; + + i = 2; + vv(); + if (Global != 1) { + printf("%s: error, Global not set to captured value\n", argv[0]); + exit(1); + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/nullblockisa.c b/projects/compiler-rt/BlocksRuntime/tests/nullblockisa.c new file mode 100644 index 00000000..ba0282e8 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/nullblockisa.c @@ -0,0 +1,43 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// nullblockisa.m +// testObjects +// +// Created by Blaine Garst on 9/24/08. +// +// CONFIG rdar://6244520 + + + +#include +#include +#include + + +void check(void (^b)(void)) { + struct _custom { + struct Block_layout layout; + struct Block_byref *innerp; + } *custom = (struct _custom *)(void *)(b); + //printf("block is at %p, size is %lx, inner is %p\n", (void *)b, Block_size(b), innerp); + if (custom->innerp->isa != (void *)NULL) { + printf("not a NULL __block isa\n"); + exit(1); + } + return; +} + +int main(int argc, char *argv[]) { + + __block int i; + + check(^{ printf("%d\n", ++i); }); + printf("%s: success\n", argv[0]); + return 0; +} + diff --git a/projects/compiler-rt/BlocksRuntime/tests/objectRRGC.c b/projects/compiler-rt/BlocksRuntime/tests/objectRRGC.c new file mode 100644 index 00000000..2cefea2a --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/objectRRGC.c @@ -0,0 +1,77 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * objectRRGC.c + * testObjects + * + * Created by Blaine Garst on 10/31/08. + * + * Test that the runtime honors the new callouts properly for retain/release and GC + * CON FIG C rdar://6175959 + */ + + + +#include +#include + + +int AssignCalled = 0; +int DisposeCalled = 0; + +// local copy instead of libSystem.B.dylib copy +void _Block_object_assign(void *destAddr, const void *object, const int isWeak) { + //printf("_Block_object_assign(%p, %p, %d) called\n", destAddr, object, isWeak); + AssignCalled = 1; +} + +void _Block_object_dispose(const void *object, const int isWeak) { + //printf("_Block_object_dispose(%p, %d) called\n", object, isWeak); + DisposeCalled = 1; +} + +struct MyStruct { + long isa; + long field; +}; + +typedef struct MyStruct *__attribute__((NSObject)) MyStruct_t; + +int main(int argc, char *argv[]) { + // create a block + struct MyStruct X; + MyStruct_t xp = (MyStruct_t)&X; + xp->field = 10; + void (^myBlock)(void) = ^{ printf("field is %ld\n", xp->field); }; + // should be a copy helper generated with a calls to above routines + // Lets find out! + struct Block_layout *bl = (struct Block_layout *)(void *)myBlock; + if ((bl->flags & BLOCK_HAS_DESCRIPTOR) != BLOCK_HAS_DESCRIPTOR) { + printf("using old runtime layout!\n"); + return 1; + } + if ((bl->flags & BLOCK_HAS_COPY_DISPOSE) != BLOCK_HAS_COPY_DISPOSE) { + printf("no copy dispose!!!!\n"); + return 1; + } + // call helper routines directly. These will, in turn, we hope, call the stubs above + long destBuffer[256]; + //printf("destbuffer is at %p, block at %p\n", destBuffer, (void *)bl); + //printf("dump is %s\n", _Block_dump(myBlock)); + bl->descriptor->copy(destBuffer, bl); + bl->descriptor->dispose(bl); + if (AssignCalled == 0) { + printf("did not call assign helper!\n"); + return 1; + } + if (DisposeCalled == 0) { + printf("did not call dispose helper\n"); + return 1; + } + printf("%s: Success!\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/objectassign.c b/projects/compiler-rt/BlocksRuntime/tests/objectassign.c new file mode 100644 index 00000000..1c4f4841 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/objectassign.c @@ -0,0 +1,76 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * objectassign.c + * testObjects + * + * Created by Blaine Garst on 10/28/08. + * + * This just tests that the compiler is issuing the proper helper routines + * CONFIG C rdar://6175959 + */ + + +#include +#include + + +int AssignCalled = 0; +int DisposeCalled = 0; + +// local copy instead of libSystem.B.dylib copy +void _Block_object_assign(void *destAddr, const void *object, const int isWeak) { + //printf("_Block_object_assign(%p, %p, %d) called\n", destAddr, object, isWeak); + AssignCalled = 1; +} + +void _Block_object_dispose(const void *object, const int isWeak) { + //printf("_Block_object_dispose(%p, %d) called\n", object, isWeak); + DisposeCalled = 1; +} + +struct MyStruct { + long isa; + long field; +}; + +typedef struct MyStruct *__attribute__((NSObject)) MyStruct_t; + +int main(int argc, char *argv[]) { + if (__APPLE_CC__ < 5627) { + printf("need compiler version %d, have %d\n", 5627, __APPLE_CC__); + return 0; + } + // create a block + struct MyStruct X; + MyStruct_t xp = (MyStruct_t)&X; + xp->field = 10; + void (^myBlock)(void) = ^{ printf("field is %ld\n", xp->field); }; + // should be a copy helper generated with a calls to above routines + // Lets find out! + struct Block_layout *bl = (struct Block_layout *)(void *)myBlock; + if ((bl->flags & BLOCK_HAS_COPY_DISPOSE) != BLOCK_HAS_COPY_DISPOSE) { + printf("no copy dispose!!!!\n"); + return 1; + } + // call helper routines directly. These will, in turn, we hope, call the stubs above + long destBuffer[256]; + //printf("destbuffer is at %p, block at %p\n", destBuffer, (void *)bl); + //printf("dump is %s\n", _Block_dump(myBlock)); + bl->descriptor->copy(destBuffer, bl); + bl->descriptor->dispose(bl); + if (AssignCalled == 0) { + printf("did not call assign helper!\n"); + return 1; + } + if (DisposeCalled == 0) { + printf("did not call dispose helper\n"); + return 1; + } + printf("%s: Success!\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/orbars.c b/projects/compiler-rt/BlocksRuntime/tests/orbars.c new file mode 100644 index 00000000..18a92444 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/orbars.c @@ -0,0 +1,23 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * orbars.c + * testObjects + * + * Created by Blaine Garst on 9/17/08. + * + * CONFIG rdar://6276695 error: before ‘|’ token + */ + +#include + +int main(int argc, char *argv[]) { + int i = 10; + void (^b)(void) = ^(void){ | i | printf("hello world, %d\n", ++i); }; + printf("%s: success :-(\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/rdar6396238.c b/projects/compiler-rt/BlocksRuntime/tests/rdar6396238.c new file mode 100644 index 00000000..28041564 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/rdar6396238.c @@ -0,0 +1,32 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6396238 + +#include +#include + +static int count = 0; + +void (^mkblock(void))(void) +{ + count++; + return ^{ + count++; + }; +} + +int main (int argc, const char * argv[]) { + mkblock()(); + if (count != 2) { + printf("%s: failure, 2 != %d\n", argv[0], count); + exit(1); + } else { + printf("%s: success\n", argv[0]); + exit(0); + } + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/rdar6405500.c b/projects/compiler-rt/BlocksRuntime/tests/rdar6405500.c new file mode 100644 index 00000000..1ab4624b --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/rdar6405500.c @@ -0,0 +1,29 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6405500 + +#include +#include +#import +#import + +int main (int argc, const char * argv[]) { + __block void (^blockFu)(size_t t); + blockFu = ^(size_t t){ + if (t == 20) { + printf("%s: success\n", argv[0]); + exit(0); + } else + dispatch_async(dispatch_get_main_queue(), ^{ blockFu(20); }); + }; + + dispatch_apply(10, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), blockFu); + + dispatch_main(); + printf("shouldn't get here\n"); + return 1; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/rdar6414583.c b/projects/compiler-rt/BlocksRuntime/tests/rdar6414583.c new file mode 100644 index 00000000..2ada04d3 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/rdar6414583.c @@ -0,0 +1,31 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG rdar://6414583 + +// a smaller case of byrefcopyint + +#include +#include +#include + +int main(int argc, char *argv[]) { + __block int c = 1; + + //printf("&c = %p - c = %i\n", &c, c); + + int i; + for(i =0; i < 2; i++) { + dispatch_block_t block = Block_copy(^{ c = i; }); + + block(); +// printf("%i: &c = %p - c = %i\n", i, &c, c); + + Block_release(block); + } + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/recursive-block.c b/projects/compiler-rt/BlocksRuntime/tests/recursive-block.c new file mode 100644 index 00000000..454ad482 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/recursive-block.c @@ -0,0 +1,55 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#include +#include +#include +#include + +// CONFIG + + +int cumulation = 0; + +int doSomething(int i) { + cumulation += i; + return cumulation; +} + +void dirtyStack() { + int i = random(); + int j = doSomething(i); + int k = doSomething(j); + doSomething(i + j + k); +} + +typedef void (^voidVoid)(void); + +voidVoid testFunction() { + int i = random(); + __block voidVoid inner = ^{ doSomething(i); }; + //printf("inner, on stack, is %p\n", (void*)inner); + /*__block*/ voidVoid outer = ^{ + //printf("will call inner block %p\n", (void *)inner); + inner(); + }; + //printf("outer looks like: %s\n", _Block_dump(outer)); + voidVoid result = Block_copy(outer); + //Block_release(inner); + return result; +} + + +int main(int argc, char **argv) { + voidVoid block = testFunction(); + dirtyStack(); + block(); + Block_release(block); + + printf("%s: success\n", argv[0]); + + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/recursive-test.c b/projects/compiler-rt/BlocksRuntime/tests/recursive-test.c new file mode 100644 index 00000000..f7991486 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/recursive-test.c @@ -0,0 +1,74 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// CONFIG open rdar://6416474 +// was rdar://5847976 +// was rdar://6348320 + +#include +#include + +int verbose = 0; + +int main(int argc, char* argv[]) { + + if (argc > 1) verbose = 1; + + __block void (^recursive_local_block)(int); + + if (verbose) printf("recursive_local_block is a local recursive block\n"); + recursive_local_block = ^(int i) { + if (verbose) printf("%d\n", i); + if (i > 0) { + recursive_local_block(i - 1); + } + }; + + if (verbose) printf("recursive_local_block's address is %p, running it:\n", (void*)recursive_local_block); + recursive_local_block(5); + + if (verbose) printf("Creating other_local_block: a local block that calls recursive_local_block\n"); + + void (^other_local_block)(int) = ^(int i) { + if (verbose) printf("other_local_block running\n"); + recursive_local_block(i); + }; + + if (verbose) printf("other_local_block's address is %p, running it:\n", (void*)other_local_block); + + other_local_block(5); + +#if __APPLE_CC__ >= 5627 + if (verbose) printf("Creating other_copied_block: a Block_copy of a block that will call recursive_local_block\n"); + + void (^other_copied_block)(int) = Block_copy(^(int i) { + if (verbose) printf("other_copied_block running\n"); + recursive_local_block(i); + }); + + if (verbose) printf("other_copied_block's address is %p, running it:\n", (void*)other_copied_block); + + other_copied_block(5); +#endif + + __block void (^recursive_copy_block)(int); + + if (verbose) printf("Creating recursive_copy_block: a Block_copy of a block that will call recursive_copy_block recursively\n"); + + recursive_copy_block = Block_copy(^(int i) { + if (verbose) printf("%d\n", i); + if (i > 0) { + recursive_copy_block(i - 1); + } + }); + + if (verbose) printf("recursive_copy_block's address is %p, running it:\n", (void*)recursive_copy_block); + + recursive_copy_block(5); + + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/recursiveassign.c b/projects/compiler-rt/BlocksRuntime/tests/recursiveassign.c new file mode 100644 index 00000000..f0070cbe --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/recursiveassign.c @@ -0,0 +1,44 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * recursiveassign.c + * testObjects + * + * Created by Blaine Garst on 12/3/08. + * + */ + +// CONFIG rdar://6639533 + +// The compiler is prefetching x->forwarding before evaluting code that recomputes forwarding and so the value goes to a place that is never seen again. + +#include +#include +#include + + +int main(int argc, char* argv[]) { + + __block void (^recursive_copy_block)(int) = ^(int arg) { printf("got wrong Block\n"); exit(1); }; + + + recursive_copy_block = Block_copy(^(int i) { + if (i > 0) { + recursive_copy_block(i - 1); + } + else { + printf("done!\n"); + } + }); + + + recursive_copy_block(5); + + printf("%s: Success\n", argv[0]); + return 0; +} + diff --git a/projects/compiler-rt/BlocksRuntime/tests/reference.C b/projects/compiler-rt/BlocksRuntime/tests/reference.C new file mode 100644 index 00000000..f86f11e8 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/reference.C @@ -0,0 +1,95 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +#import +#import +#import + +// CONFIG C++ + +int recovered = 0; + + + +int constructors = 0; +int destructors = 0; + +#define CONST const + +class TestObject +{ +public: + TestObject(CONST TestObject& inObj); + TestObject(); + ~TestObject(); + + TestObject& operator=(CONST TestObject& inObj); + + void test(void); + + int version() CONST { return _version; } +private: + mutable int _version; +}; + +TestObject::TestObject(CONST TestObject& inObj) + +{ + ++constructors; + _version = inObj._version; + //printf("%p (%d) -- TestObject(const TestObject&) called", this, _version); +} + + +TestObject::TestObject() +{ + _version = ++constructors; + //printf("%p (%d) -- TestObject() called\n", this, _version); +} + + +TestObject::~TestObject() +{ + //printf("%p -- ~TestObject() called\n", this); + ++destructors; +} + +#if 1 +TestObject& TestObject::operator=(CONST TestObject& inObj) +{ + //printf("%p -- operator= called", this); + _version = inObj._version; + return *this; +} +#endif + +void TestObject::test(void) { + void (^b)(void) = ^{ recovered = _version; }; + void (^b2)(void) = Block_copy(b); + b2(); + Block_release(b2); +} + + + +void testRoutine() { + TestObject one; + + + one.test(); +} + + + +int main(int argc, char *argv[]) { + testRoutine(); + if (recovered == 1) { + printf("%s: success\n", argv[0]); + exit(0); + } + printf("%s: *** didn't recover byref block variable\n", argv[0]); + exit(1); +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/rettypepromotion.c b/projects/compiler-rt/BlocksRuntime/tests/rettypepromotion.c new file mode 100644 index 00000000..597eafe8 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/rettypepromotion.c @@ -0,0 +1,36 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * rettypepromotion.c + * testObjects + * + * Created by Blaine Garst on 11/3/08. + * + */ + +// CONFIG error: +// C++ and C give different errors so we don't check for an exact match. +// The error is that enum's are defined to be ints, always, even if defined with explicit long values + + +#include +#include + +enum { LESS = -1, EQUAL, GREATER }; + +void sortWithBlock(long (^comp)(void *arg1, void *arg2)) { +} + +int main(int argc, char *argv[]) { + sortWithBlock(^(void *arg1, void *arg2) { + if (random()) return LESS; + if (random()) return EQUAL; + if (random()) return GREATER; + }); + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/returnfunctionptr.c b/projects/compiler-rt/BlocksRuntime/tests/returnfunctionptr.c new file mode 100644 index 00000000..6c7df631 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/returnfunctionptr.c @@ -0,0 +1,23 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + + +// CONFIG rdar://6339747 but wasn't + +#include + +int (*funcptr)(long); + +int (*(^b)(char))(long); + +int main(int argc, char *argv[]) { + // implicit is fine + b = ^(char x) { return funcptr; }; + // explicit never parses + b = ^int (*(char x))(long) { return funcptr; }; + printf("%s: Success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/shorthandexpression.c b/projects/compiler-rt/BlocksRuntime/tests/shorthandexpression.c new file mode 100644 index 00000000..bf458207 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/shorthandexpression.c @@ -0,0 +1,24 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * shorthandexpression.c + * testObjects + * + * Created by Blaine Garst on 9/16/08. + * + * CONFIG error: + */ + + +void foo() { + void (^b)(void) = ^(void)printf("hello world\n"); +} + +int main(int argc, char *argv[]) { + printf("%s: this shouldn't compile\n", argv[0]); + return 1; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/sizeof.c b/projects/compiler-rt/BlocksRuntime/tests/sizeof.c new file mode 100644 index 00000000..1f84fc16 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/sizeof.c @@ -0,0 +1,26 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * sizeof.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +#include + +// CONFIG error: + +int main(int argc, char *argv[]) { + + void (^aBlock)(void) = ^{ printf("hellow world\n"); }; + + printf("the size of a block is %ld\n", sizeof(*aBlock)); + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/small-struct.c b/projects/compiler-rt/BlocksRuntime/tests/small-struct.c new file mode 100644 index 00000000..434f3c17 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/small-struct.c @@ -0,0 +1,45 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import +#import +#import + +typedef struct { + int a; + int b; +} MiniStruct; + +int main (int argc, const char * argv[]) { + MiniStruct inny; + MiniStruct outty; + MiniStruct (^copyStruct)(MiniStruct); + + memset(&inny, 0xA5, sizeof(inny)); + memset(&outty, 0x2A, sizeof(outty)); + + inny.a = 12; + inny.b = 42; + + copyStruct = ^(MiniStruct aTinyStruct){ return aTinyStruct; }; // pass-by-value intrinsically copies the argument + + outty = copyStruct(inny); + + if ( &inny == &outty ) { + printf("%s: struct wasn't copied.", argv[0]); + exit(1); + } + if ( (inny.a != outty.a) || (inny.b != outty.b) ) { + printf("%s: struct contents did not match.", argv[0]); + exit(1); + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/structmember.c b/projects/compiler-rt/BlocksRuntime/tests/structmember.c new file mode 100644 index 00000000..c451d3f3 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/structmember.c @@ -0,0 +1,45 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * structmember.c + * testObjects + * + * Created by Blaine Garst on 9/30/08. + * CONFIG + */ +#include +#include +#include + +// CONFIG + +int main(int argc, char *argv[]) { + struct stuff { + long int a; + long int b; + long int c; + } localStuff = { 10, 20, 30 }; + int d; + + void (^a)(void) = ^ { printf("d is %d", d); }; + void (^b)(void) = ^ { printf("d is %d, localStuff.a is %lu", d, localStuff.a); }; + + unsigned nominalsize = Block_size(b) - Block_size(a); +#if __cplusplus__ + // need copy+dispose helper for C++ structures + nominalsize += 2*sizeof(void*); +#endif + if ((Block_size(b) - Block_size(a)) != nominalsize) { + printf("sizeof a is %ld, sizeof b is %ld, expected %d\n", Block_size(a), Block_size(b), nominalsize); + printf("dump of b is %s\n", _Block_dump(b)); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} + + diff --git a/projects/compiler-rt/BlocksRuntime/tests/testfilerunner.h b/projects/compiler-rt/BlocksRuntime/tests/testfilerunner.h new file mode 100644 index 00000000..d4e54f02 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/testfilerunner.h @@ -0,0 +1,110 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// testfilerunner.h +// testObjects +// +// Created by Blaine Garst on 9/24/08. +// + +#import + +/* + variations: + four source types: C, ObjC, C++, ObjC++, + and for ObjC or ObjC++ we have + RR and GC capabilities + we assume C++ friendly includes for C/ObjC even if C++ isn't used + + + four compilers: C, ObjC, C++, ObjC++ + and for ObjC or ObjC++ we can compile + RR, RR+GC, GC+RR, GC + although to test RR+GC we need to build a shell "main" in both modes + and/or run with GC disabled if possible. + + To maximize coverage we mark files with capabilities and then ask them to be + compiled with each variation of compiler and option. + If the file doesn't have the capability it politely refuses. +*/ + +enum options { + Do64 = (1 << 0), + DoCPP = (1 << 1), + DoOBJC = (1 << 3), + DoGC = (1 << 4), + DoRR = (1 << 5), + DoRRGC = (1 << 6), // -fobjc-gc but main w/o so it runs in RR mode + DoGCRR = (1 << 7), // -fobjc-gc & run GC mode + + //DoDashG = (1 << 8), + DoDashO = (1 << 9), + DoDashOs = (1 << 10), + DoDashO2 = (1 << 11), + + DoC99 = (1 << 12), // -std=c99 +}; + + +@class TestFileExeGenerator; + +// this class will actually compile and/or run a target binary +// XXX we don't track which dynamic libraries requested/used nor set them up +@interface TestFileExe : NSObject { + NSPointerArray *compileLine; + int options; + bool shouldFail; + TestFileExeGenerator *generator; + __strong char *binaryName; + __strong char *sourceName; + __strong char *libraryPath; + __strong char *frameworkPath; +} +@property int options; +@property(assign) NSPointerArray *compileLine; +@property(assign) TestFileExeGenerator *generator; +@property bool shouldFail; +@property __strong char *binaryName; +@property __strong char *sourceName; +@property __strong char *libraryPath; +@property __strong char *frameworkPath; +- (bool) compileUnlessExists:(bool)skip; +- (bool) run; +@property(readonly) __strong char *radar; +@end + +// this class generates an appropriate set of configurations to compile +// we don't track which gcc we use but we should XXX +@interface TestFileExeGenerator : NSObject { + bool hasObjC; + bool hasRR; + bool hasGC; + bool hasCPlusPlus; + bool wantsC99; + bool wants64; + bool wants32; + bool supposedToNotCompile; + bool open; // this problem is still open - e.g. unresolved + __strong char *radar; // for things already known to go wrong + __strong char *filename; + __strong char *compilerPath; + __strong char *errorString; + __strong char *warningString; + NSPointerArray *extraLibraries; +} +@property bool hasObjC, hasRR, hasGC, hasCPlusPlus, wantsC99, supposedToNotCompile, open, wants32, wants64; +@property(assign) __strong char *radar; +@property __strong char *filename; +@property __strong char *compilerPath; +@property __strong char *errorString; +@property __strong char *warningString; +- (TestFileExe *)lineForOptions:(int)options; // nil if no can do ++ (NSArray *)generatorsFromFILE:(FILE *)fd; ++ (NSArray *)generatorsFromPath:(NSString *)path; +@end + + diff --git a/projects/compiler-rt/BlocksRuntime/tests/testfilerunner.m b/projects/compiler-rt/BlocksRuntime/tests/testfilerunner.m new file mode 100644 index 00000000..459adf88 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/testfilerunner.m @@ -0,0 +1,805 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// +// testfilerunner.m +// testObjects +// +// Created by Blaine Garst on 9/24/08. +// + +#import "testfilerunner.h" +#import +#include +#include +#include +#include +#include +#include + +bool Everything = false; // do it also with 3 levels of optimization +bool DoClang = false; + +static bool isDirectory(char *path); +static bool isExecutable(char *path); +static bool isYounger(char *source, char *binary); +static bool readErrorFile(char *buffer, const char *from); + +__strong char *gcstrcpy2(__strong const char *arg, char *endp) { + unsigned size = endp - arg + 1; + __strong char *result = NSAllocateCollectable(size, 0); + strncpy(result, arg, size); + result[size-1] = 0; + return result; +} +__strong char *gcstrcpy1(__strong char *arg) { + unsigned size = strlen(arg) + 1; + __strong char *result = NSAllocateCollectable(size, 0); + strncpy(result, arg, size); + result[size-1] = 0; + return result; +} + +@implementation TestFileExe + +@synthesize options, compileLine, shouldFail, binaryName, sourceName; +@synthesize generator; +@synthesize libraryPath, frameworkPath; + +- (NSString *)description { + NSMutableString *result = [NSMutableString new]; + if (shouldFail) [result appendString:@"fail"]; + for (id x in compileLine) { + [result appendString:[NSString stringWithFormat:@" %s", (char *)x]]; + } + return result; +} + +- (__strong char *)radar { + return generator.radar; +} + +- (bool) compileUnlessExists:(bool)skip { + if (shouldFail) { + printf("don't use this to compile anymore!\n"); + return false; + } + if (skip && isExecutable(binaryName) && !isYounger(sourceName, binaryName)) return true; + int argc = [compileLine count]; + char *argv[argc+1]; + for (int i = 0; i < argc; ++i) + argv[i] = (char *)[compileLine pointerAtIndex:i]; + argv[argc] = NULL; + pid_t child = fork(); + if (child == 0) { + execv(argv[0], argv); + exit(10); // shouldn't happen + } + if (child < 0) { + printf("fork failed\n"); + return false; + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(1); + } + if (WEXITSTATUS(status) == 0) { + return true; + } + printf("run failed\n"); + return false; +} + +bool lookforIn(char *lookfor, const char *format, pid_t child) { + char buffer[512]; + char got[512]; + sprintf(buffer, format, child); + bool gotOutput = readErrorFile(got, buffer); + if (!gotOutput) { + printf("**** didn't get an output file %s to analyze!!??\n", buffer); + return false; + } + char *where = strstr(got, lookfor); + if (!where) { + printf("didn't find '%s' in output file %s\n", lookfor, buffer); + return false; + } + unlink(buffer); + return true; +} + +- (bool) compileWithExpectedFailure { + if (!shouldFail) { + printf("Why am I being called?\n"); + return false; + } + int argc = [compileLine count]; + char *argv[argc+1]; + for (int i = 0; i < argc; ++i) + argv[i] = (char *)[compileLine pointerAtIndex:i]; + argv[argc] = NULL; + pid_t child = fork(); + char buffer[512]; + if (child == 0) { + // in child + sprintf(buffer, "/tmp/errorfile_%d", getpid()); + close(1); + int fd = creat(buffer, 0777); + if (fd != 1) { + fprintf(stderr, "didn't open custom error file %s as 1, got %d\n", buffer, fd); + exit(1); + } + close(2); + dup(1); + int result = execv(argv[0], argv); + exit(10); + } + if (child < 0) { + printf("fork failed\n"); + return false; + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(11); + } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) { + return false; + } + } + else { + printf("***** compiler borked/ICEd/died unexpectedly (status %x)\n", status); + return false; + } + char *error = generator.errorString; + + if (!error) return true; +#if 0 + char got[512]; + sprintf(buffer, "/tmp/errorfile_%d", child); + bool gotOutput = readErrorFile(got, buffer); + if (!gotOutput) { + printf("**** didn't get an error file %s to analyze!!??\n", buffer); + return false; + } + char *where = strstr(got, error); + if (!where) { + printf("didn't find '%s' in error file %s\n", error, buffer); + return false; + } + unlink(buffer); +#else + if (!lookforIn(error, "/tmp/errorfile_%d", child)) return false; +#endif + return true; +} + +- (bool) run { + if (shouldFail) return true; + if (sizeof(long) == 4 && options & Do64) { + return true; // skip 64-bit tests + } + int argc = 1; + char *argv[argc+1]; + argv[0] = binaryName; + argv[argc] = NULL; + pid_t child = fork(); + if (child == 0) { + // set up environment + char lpath[1024]; + char fpath[1024]; + char *myenv[3]; + int counter = 0; + if (libraryPath) { + sprintf(lpath, "DYLD_LIBRARY_PATH=%s", libraryPath); + myenv[counter++] = lpath; + } + if (frameworkPath) { + sprintf(fpath, "DYLD_FRAMEWORK_PATH=%s", frameworkPath); + myenv[counter++] = fpath; + } + myenv[counter] = NULL; + if (generator.warningString) { + // set up stdout/stderr + char outfile[1024]; + sprintf(outfile, "/tmp/stdout_%d", getpid()); + close(2); + close(1); + creat(outfile, 0700); + dup(1); + } + execve(argv[0], argv, myenv); + exit(10); // shouldn't happen + } + if (child < 0) { + printf("fork failed\n"); + return false; + } + int status = 0; + pid_t deadchild = wait(&status); + if (deadchild != child) { + printf("wait got %d instead of %d\n", deadchild, child); + exit(1); + } + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + if (generator.warningString) { + if (!lookforIn(generator.warningString, "/tmp/stdout_%d", child)) return false; + } + return true; + } + printf("**** run failed for %s\n", binaryName); + return false; +} + +@end + +@implementation TestFileExeGenerator +@synthesize filename, compilerPath, errorString; +@synthesize hasObjC, hasRR, hasGC, hasCPlusPlus, wantsC99, supposedToNotCompile, open, wants32, wants64; +@synthesize radar; +@synthesize warningString; + +- (void)setFilename:(__strong char *)name { + filename = gcstrcpy1(name); +} +- (void)setCompilerPath:(__strong char *)name { + compilerPath = gcstrcpy1(name); +} + +- (void)forMostThings:(NSMutableArray *)lines options:(int)options { + TestFileExe *item = nil; + item = [self lineForOptions:options]; + if (item) [lines addObject:item]; + item = [self lineForOptions:options|Do64]; + if (item) [lines addObject:item]; + item = [self lineForOptions:options|DoCPP]; + if (item) [lines addObject:item]; + item = [self lineForOptions:options|Do64|DoCPP]; + if (item) [lines addObject:item]; +} + +/* + DoDashG = (1 << 8), + DoDashO = (1 << 9), + DoDashOs = (1 << 10), + DoDashO2 = (1 << 11), +*/ + +- (void)forAllThings:(NSMutableArray *)lines options:(int)options { + [self forMostThings:lines options:options]; + if (!Everything) { + return; + } + // now do it with three explicit optimization flags + [self forMostThings:lines options:options | DoDashO]; + [self forMostThings:lines options:options | DoDashOs]; + [self forMostThings:lines options:options | DoDashO2]; +} + +- (NSArray *)allLines { + NSMutableArray *result = [NSMutableArray new]; + TestFileExe *item = nil; + + int options = 0; + [self forAllThings:result options:0]; + [self forAllThings:result options:DoOBJC | DoRR]; + [self forAllThings:result options:DoOBJC | DoGC]; + [self forAllThings:result options:DoOBJC | DoGCRR]; + //[self forAllThings:result options:DoOBJC | DoRRGC]; + + return result; +} + +- (void)addLibrary:(const char *)dashLSomething { + if (!extraLibraries) { + extraLibraries = [NSPointerArray pointerArrayWithOptions: + NSPointerFunctionsStrongMemory | + NSPointerFunctionsCStringPersonality]; + } + [extraLibraries addPointer:(void *)dashLSomething]; +} + +- (TestFileExe *)lineForOptions:(int)options { // nil if no can do + if (hasObjC && !(options & DoOBJC)) return nil; + if (hasCPlusPlus && !(options & DoCPP)) return nil; + if (hasObjC) { + if (!hasGC && (options & (DoGC|DoGCRR))) return nil; // not smart enough + if (!hasRR && (options & (DoRR|DoRRGC))) return nil; + } + NSPointerArray *pa = [NSPointerArray pointerArrayWithOptions: + NSPointerFunctionsStrongMemory | + NSPointerFunctionsCStringPersonality]; + // construct path + char path[512]; + path[0] = 0; + if (!compilerPath) compilerPath = "/usr/bin"; + if (compilerPath) { + strcat(path, compilerPath); + strcat(path, "/"); + } + if (options & DoCPP) { + strcat(path, DoClang ? "clang++" : "g++-4.2"); + } + else { + strcat(path, DoClang ? "clang" : "gcc-4.2"); + } + [pa addPointer:gcstrcpy1(path)]; + if (options & DoOBJC) { + if (options & DoCPP) { + [pa addPointer:"-ObjC++"]; + } + else { + [pa addPointer:"-ObjC"]; + } + } + [pa addPointer:"-g"]; + if (options & DoDashO) [pa addPointer:"-O"]; + else if (options & DoDashO2) [pa addPointer:"-O2"]; + else if (options & DoDashOs) [pa addPointer:"-Os"]; + if (wantsC99 && (! (options & DoCPP))) { + [pa addPointer:"-std=c99"]; + [pa addPointer:"-fblocks"]; + } + [pa addPointer:"-arch"]; + [pa addPointer: (options & Do64) ? "x86_64" : "i386"]; + + if (options & DoOBJC) { + switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { + case DoRR: + break; + case DoGC: + [pa addPointer:"-fobjc-gc-only"]; + break; + case DoGCRR: + [pa addPointer:"-fobjc-gc"]; + break; + case DoRRGC: + printf("DoRRGC unsupported right now\n"); + [pa addPointer:"-c"]; + return nil; + } + [pa addPointer:"-framework"]; + [pa addPointer:"Foundation"]; + } + [pa addPointer:gcstrcpy1(filename)]; + [pa addPointer:"-o"]; + + path[0] = 0; + strcat(path, filename); + strcat(path, "."); + strcat(path, (options & Do64) ? "64" : "32"); + if (options & DoOBJC) { + switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { + case DoRR: strcat(path, "-rr"); break; + case DoGC: strcat(path, "-gconly"); break; + case DoGCRR: strcat(path, "-gcrr"); break; + case DoRRGC: strcat(path, "-rrgc"); break; + } + } + if (options & DoCPP) strcat(path, "++"); + if (options & DoDashO) strcat(path, "-O"); + else if (options & DoDashO2) strcat(path, "-O2"); + else if (options & DoDashOs) strcat(path, "-Os"); + if (wantsC99) strcat(path, "-C99"); + strcat(path, DoClang ? "-clang" : "-gcc"); + strcat(path, "-bin"); + TestFileExe *result = [TestFileExe new]; + result.binaryName = gcstrcpy1(path); // could snarf copy in pa + [pa addPointer:result.binaryName]; + for (id cString in extraLibraries) { + [pa addPointer:cString]; + } + + result.sourceName = gcstrcpy1(filename); // could snarf copy in pa + result.compileLine = pa; + result.options = options; + result.shouldFail = supposedToNotCompile; + result.generator = self; + return result; +} + ++ (NSArray *)generatorsFromPath:(NSString *)path { + FILE *fp = fopen([path fileSystemRepresentation], "r"); + if (fp == NULL) return nil; + NSArray *result = [self generatorsFromFILE:fp]; + fclose(fp); + return result; +} + +#define LOOKFOR "CON" "FIG" + +char *__strong parseRadar(char *line) { + line = strstr(line, "rdar:"); // returns beginning + char *endp = line + strlen("rdar:"); + while (*endp && *endp != ' ' && *endp != '\n') + ++endp; + return gcstrcpy2(line, endp); +} + +- (void)parseLibraries:(const char *)line { + start: + line = strstr(line, "-l"); + char *endp = (char *)line + 2; + while (*endp && *endp != ' ' && *endp != '\n') + ++endp; + [self addLibrary:gcstrcpy2(line, endp)]; + if (strstr(endp, "-l")) { + line = endp; + goto start; + } +} + ++ (TestFileExeGenerator *)generatorFromLine:(char *)line filename:(char *)filename { + TestFileExeGenerator *item = [TestFileExeGenerator new]; + item.filename = gcstrcpy1(filename); + if (strstr(line, "GC")) item.hasGC = true; + if (strstr(line, "RR")) item.hasRR = true; + if (strstr(line, "C++")) item.hasCPlusPlus = true; + if (strstr(line, "-C99")) { + item.wantsC99 = true; + } + if (strstr(line, "64")) item.wants64 = true; + if (strstr(line, "32")) item.wants32 = true; + if (strstr(line, "-l")) [item parseLibraries:line]; + if (strstr(line, "open")) item.open = true; + if (strstr(line, "FAIL")) item.supposedToNotCompile = true; // old + // compile time error + if (strstr(line, "error:")) { + item.supposedToNotCompile = true; + // zap newline + char *error = strstr(line, "error:") + strlen("error:"); + // make sure we have something before the newline + char *newline = strstr(error, "\n"); + if (newline && ((newline-error) > 1)) { + *newline = 0; + item.errorString = gcstrcpy1(strstr(line, "error:") + strlen("error: ")); + } + } + // run time warning + if (strstr(line, "runtime:")) { + // zap newline + char *error = strstr(line, "runtime:") + strlen("runtime:"); + // make sure we have something before the newline + char *newline = strstr(error, "\n"); + if (newline && ((newline-error) > 1)) { + *newline = 0; + item.warningString = gcstrcpy1(strstr(line, "runtime:") + strlen("runtime:")); + } + } + if (strstr(line, "rdar:")) item.radar = parseRadar(line); + if (item.hasGC || item.hasRR) item.hasObjC = true; + if (!item.wants32 && !item.wants64) { // give them both if they ask for neither + item.wants32 = item.wants64 = true; + } + return item; +} + ++ (NSArray *)generatorsFromFILE:(FILE *)fp { + NSMutableArray *result = [NSMutableArray new]; + // pretend this is a grep LOOKFOR *.[cmCM][cmCM] input + // look for + // filename: ... LOOKFOR [GC] [RR] [C++] [FAIL ...] + char buf[512]; + while (fgets(buf, 512, fp)) { + char *config = strstr(buf, LOOKFOR); + if (!config) continue; + char *filename = buf; + char *end = strchr(buf, ':'); + *end = 0; + [result addObject:[self generatorFromLine:config filename:filename]]; + } + return result; +} + ++ (TestFileExeGenerator *)generatorFromFilename:(char *)filename { + FILE *fp = fopen(filename, "r"); + if (!fp) { + printf("didn't open %s!!\n", filename); + return nil; + } + char buf[512]; + while (fgets(buf, 512, fp)) { + char *config = strstr(buf, LOOKFOR); + if (!config) continue; + fclose(fp); + return [self generatorFromLine:config filename:filename]; + } + fclose(fp); + // guess from filename + char *ext = strrchr(filename, '.'); + if (!ext) return nil; + TestFileExeGenerator *result = [TestFileExeGenerator new]; + result.filename = gcstrcpy1(filename); + if (!strncmp(ext, ".m", 2)) { + result.hasObjC = true; + result.hasRR = true; + result.hasGC = true; + } + else if (!strcmp(ext, ".c")) { + ; + } + else if (!strcmp(ext, ".M") || !strcmp(ext, ".mm")) { + result.hasObjC = true; + result.hasRR = true; + result.hasGC = true; + result.hasCPlusPlus = true; + } + else if (!strcmp(ext, ".cc") + || !strcmp(ext, ".cp") + || !strcmp(ext, ".cxx") + || !strcmp(ext, ".cpp") + || !strcmp(ext, ".CPP") + || !strcmp(ext, ".c++") + || !strcmp(ext, ".C")) { + result.hasCPlusPlus = true; + } + else { + printf("unknown extension, file %s ignored\n", filename); + result = nil; + } + return result; + +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%s: %s%s%s%s%s%s", + filename, + LOOKFOR, + hasGC ? " GC" : "", + hasRR ? " RR" : "", + hasCPlusPlus ? " C++" : "", + wantsC99 ? "C99" : "", + supposedToNotCompile ? " FAIL" : ""]; +} + +@end + +void printDetails(NSArray *failures, const char *whatAreThey) { + if ([failures count]) { + NSMutableString *output = [NSMutableString new]; + printf("%s:\n", whatAreThey); + for (TestFileExe *line in failures) { + printf("%s", line.binaryName); + char *radar = line.generator.radar; + if (radar) + printf(" (due to %s?),", radar); + printf(" recompile via:\n%s\n\n", line.description.UTF8String); + } + printf("\n"); + } +} + +void help(const char *whoami) { + printf("Usage: %s [-fast] [-e] [-dyld librarypath] [gcc4.2dir] [-- | source1 ...]\n", whoami); + printf(" -fast don't recompile if binary younger than source\n"); + printf(" -open only run tests that are thought to still be unresolved\n"); + printf(" -clang use the clang and clang++ compilers\n"); + printf(" -e compile all variations also with -Os, -O2, -O3\n"); + printf(" -dyld p override DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH to p when running tests\n"); + printf(" directory containing gcc-4.2 (or clang) that you wish to use to compile the tests\n"); + printf(" -- assume stdin is a grep CON" "FIG across the test sources\n"); + printf(" otherwise treat each remaining argument as a single test file source\n"); + printf("%s will compile and run individual test files under a variety of compilers, c, obj-c, c++, and objc++\n", whoami); + printf(" .c files are compiled with all four compilers\n"); + printf(" .m files are compiled with objc and objc++ compilers\n"); + printf(" .C files are compiled with c++ and objc++ compilers\n"); + printf(" .M files are compiled only with the objc++ compiler\n"); + printf("(actually all forms of extensions recognized by the compilers are honored, .cc, .c++ etc.)\n"); + printf("\nTest files should run to completion with no output and exit (return) 0 on success.\n"); + printf("Further they should be able to be compiled and run with GC on or off and by the C++ compilers\n"); + printf("A line containing the string CON" "FIG within the source enables restrictions to the above assumptions\n"); + printf("and other options.\n"); + printf("Following CON" "FIG the string\n"); + printf(" C++ restricts the test to only be run by c++ and objc++ compilers\n"); + printf(" GC restricts the test to only be compiled and run with GC on\n"); + printf(" RR (retain/release) restricts the test to only be compiled and run with GC off\n"); + printf("Additionally,\n"); + printf(" -C99 restricts the C versions of the test to -fstd=c99 -fblocks\n"); + printf(" -O adds the -O optimization level\n"); + printf(" -O2 adds the -O2 optimization level\n"); + printf(" -Os adds the -Os optimization level\n"); + printf("Files that are known to exhibit unresolved problems can provide the term \"open\" and this can"); + printf("in turn allow highlighting of fixes that have regressed as well as identify that fixes are now available.\n"); + printf("Files that exhibit known bugs may provide\n"); + printf(" rdar://whatever such that if they fail the rdar will get cited\n"); + printf("Files that are expected to fail to compile should provide, as their last token sequence,\n"); + printf(" error:\n"); + printf(" or error: substring to match.\n"); + printf("Files that are expected to produce a runtime error message should provide, as their last token sequence,\n"); + printf(" warning: string to match\n"); + printf("\n%s will compile and run all configurations of the test files and report a summary at the end. Good luck.\n", whoami); + printf(" Blaine Garst blaine@apple.com\n"); +} + +int main(int argc, char *argv[]) { + printf("running on %s-bit architecture\n", sizeof(long) == 4 ? "32" : "64"); + char *compilerDir = "/usr/bin"; + NSMutableArray *generators = [NSMutableArray new]; + bool doFast = false; + bool doStdin = false; + bool onlyOpen = false; + char *libraryPath = getenv("DYLD_LIBRARY_PATH"); + char *frameworkPath = getenv("DYLD_FRAMEWORK_PATH"); + // process options + while (argc > 1) { + if (!strcmp(argv[1], "-fast")) { + doFast = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-dyld")) { + doFast = true; + --argc; + ++argv; + frameworkPath = argv[1]; + libraryPath = argv[1]; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-open")) { + onlyOpen = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-clang")) { + DoClang = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-e")) { + Everything = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "--")) { + doStdin = true; + --argc; + ++argv; + } + else if (!strcmp(argv[1], "-")) { + help(argv[0]); + return 1; + } + else if (argc > 1 && isDirectory(argv[1])) { + compilerDir = argv[1]; + ++argv; + --argc; + } + else + break; + } + // process remaining arguments, or stdin + if (argc == 1) { + if (doStdin) + generators = (NSMutableArray *)[TestFileExeGenerator generatorsFromFILE:stdin]; + else { + help(argv[0]); + return 1; + } + } + else while (argc > 1) { + TestFileExeGenerator *generator = [TestFileExeGenerator generatorFromFilename:argv[1]]; + if (generator) [generators addObject:generator]; + ++argv; + --argc; + } + // see if we can generate all possibilities + NSMutableArray *failureToCompile = [NSMutableArray new]; + NSMutableArray *failureToFailToCompile = [NSMutableArray new]; + NSMutableArray *failureToRun = [NSMutableArray new]; + NSMutableArray *successes = [NSMutableArray new]; + for (TestFileExeGenerator *generator in generators) { + //NSLog(@"got %@", generator); + if (onlyOpen && !generator.open) { + //printf("skipping resolved test %s\n", generator.filename); + continue; // skip closed if onlyOpen + } + if (!onlyOpen && generator.open) { + //printf("skipping open test %s\n", generator.filename); + continue; // skip open if not asked for onlyOpen + } + generator.compilerPath = compilerDir; + NSArray *tests = [generator allLines]; + for (TestFileExe *line in tests) { + line.frameworkPath = frameworkPath; // tell generators about it instead XXX + line.libraryPath = libraryPath; // tell generators about it instead XXX + if ([line shouldFail]) { + if (doFast) continue; // don't recompile & don't count as success + if ([line compileWithExpectedFailure]) { + [successes addObject:line]; + } + else + [failureToFailToCompile addObject:line]; + } + else if ([line compileUnlessExists:doFast]) { + if ([line run]) { + printf("%s ran successfully\n", line.binaryName); + [successes addObject:line]; + } + else { + [failureToRun addObject:line]; + } + } + else { + [failureToCompile addObject:line]; + } + } + } + printf("\n--- results ---\n\n%lu successes\n%lu unexpected compile failures\n%lu failure to fail to compile errors\n%lu run failures\n", + [successes count], [failureToCompile count], [failureToFailToCompile count], [failureToRun count]); + printDetails(failureToCompile, "unexpected compile failures"); + printDetails(failureToFailToCompile, "should have failed to compile but didn't failures"); + printDetails(failureToRun, "run failures"); + + if (onlyOpen && [successes count]) { + NSMutableSet *radars = [NSMutableSet new]; + printf("The following tests ran successfully suggesting that they are now resolved:\n"); + for (TestFileExe *line in successes) { + printf("%s\n", line.binaryName); + if (line.radar) [radars addObject:line.generator]; + } + if ([radars count]) { + printf("The following radars may be resolved:\n"); + for (TestFileExeGenerator *line in radars) { + printf("%s\n", line.radar); + } + } + } + + return [failureToCompile count] + [failureToRun count]; +} + +#include + +static bool isDirectory(char *path) { + struct stat statb; + int retval = stat(path, &statb); + if (retval != 0) return false; + if (statb.st_mode & S_IFDIR) return true; + return false; +} + +static bool isExecutable(char *path) { + struct stat statb; + int retval = stat(path, &statb); + if (retval != 0) return false; + if (!(statb.st_mode & S_IFREG)) return false; + if (statb.st_mode & S_IXUSR) return true; + return false; +} + +static bool isYounger(char *source, char *binary) { + struct stat statb; + int retval = stat(binary, &statb); + if (retval != 0) return true; // if doesn't exit, lie + + struct stat stata; + retval = stat(source, &stata); + if (retval != 0) return true; // we're hosed + // the greater the timeval the younger it is + if (stata.st_mtimespec.tv_sec > statb.st_mtimespec.tv_sec) return true; + if (stata.st_mtimespec.tv_nsec > statb.st_mtimespec.tv_nsec) return true; + return false; +} + +static bool readErrorFile(char *buffer, const char *from) { + int fd = open(from, 0); + if (fd < 0) { + printf("didn't open %s, (might not have been created?)\n", buffer); + return false; + } + int count = read(fd, buffer, 512); + if (count < 1) { + printf("read error on %s\n", buffer); + return false; + } + buffer[count-1] = 0; // zap newline + return true; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/varargs-bad-assign.c b/projects/compiler-rt/BlocksRuntime/tests/varargs-bad-assign.c new file mode 100644 index 00000000..b978668b --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/varargs-bad-assign.c @@ -0,0 +1,44 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// HACK ALERT: gcc and g++ give different errors, referencing the line number to ensure that it checks for the right error; MUST KEEP IN SYNC WITH THE TEST +// CONFIG 27: error: + +#import +#import +#import +#import + + +int main (int argc, const char * argv[]) { + int (^sumn)(int n, ...); + int six = 0; + + sumn = ^(int a, int b, int n, ...){ + int result = 0; + va_list numbers; + int i; + + va_start(numbers, n); + for (i = 0 ; i < n ; i++) { + result += va_arg(numbers, int); + } + va_end(numbers); + + return result; + }; + + six = sumn(3, 1, 2, 3); + + if ( six != 6 ) { + printf("%s: Expected 6 but got %d\n", argv[0], six); + exit(1); + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/varargs.c b/projects/compiler-rt/BlocksRuntime/tests/varargs.c new file mode 100644 index 00000000..01affc76 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/varargs.c @@ -0,0 +1,39 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// -*- mode:C; c-basic-offset:4; tab-width:4; intent-tabs-mode:nil; -*- +// CONFIG + +#import +#import +#import +#import + + +int main (int argc, const char * argv[]) { + int (^sumn)(int n, ...) = ^(int n, ...){ + int result = 0; + va_list numbers; + int i; + + va_start(numbers, n); + for (i = 0 ; i < n ; i++) { + result += va_arg(numbers, int); + } + va_end(numbers); + + return result; + }; + int six = sumn(3, 1, 2, 3); + + if ( six != 6 ) { + printf("%s: Expected 6 but got %d\n", argv[0], six); + exit(1); + } + + printf("%s: success\n", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/BlocksRuntime/tests/variadic.c b/projects/compiler-rt/BlocksRuntime/tests/variadic.c new file mode 100644 index 00000000..1d80657e --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/variadic.c @@ -0,0 +1,66 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * variadic.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +// PURPOSE Test that variadic arguments compile and work for Blocks +// CONFIG + +#include +#include + +int main(int argc, char *argv[]) { + + long (^addthem)(const char *, ...) = ^long (const char *format, ...){ + va_list argp; + const char *p; + int i; + char c; + double d; + long result = 0; + va_start(argp, format); + //printf("starting...\n"); + for (p = format; *p; p++) switch (*p) { + case 'i': + i = va_arg(argp, int); + //printf("i: %d\n", i); + result += i; + break; + case 'd': + d = va_arg(argp, double); + //printf("d: %g\n", d); + result += (int)d; + break; + case 'c': + c = va_arg(argp, int); + //printf("c: '%c'\n", c); + result += c; + break; + } + //printf("...done\n\n"); + return result; + }; + long testresult = addthem("ii", 10, 20); + if (testresult != 30) { + printf("got wrong result: %ld\n", testresult); + return 1; + } + testresult = addthem("idc", 30, 40.0, 'a'); + if (testresult != (70+'a')) { + printf("got different wrong result: %ld\n", testresult); + return 1; + } + printf("%s: Success\n", argv[0]); + return 0; +} + + diff --git a/projects/compiler-rt/BlocksRuntime/tests/voidarg.c b/projects/compiler-rt/BlocksRuntime/tests/voidarg.c new file mode 100644 index 00000000..a8f034b4 --- /dev/null +++ b/projects/compiler-rt/BlocksRuntime/tests/voidarg.c @@ -0,0 +1,27 @@ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +/* + * voidarg.c + * testObjects + * + * Created by Blaine Garst on 2/17/09. + * + */ + +// PURPOSE should complain about missing 'void' but both GCC and clang are supporting K&R instead +// CONFIG open error: + +#include + +int Global; + +void (^globalBlock)() = ^{ ++Global; }; // should be void (^gb)(void) = ... + +int main(int argc, char *argv[]) { + printf("%s: success", argv[0]); + return 0; +} diff --git a/projects/compiler-rt/CMakeLists.txt b/projects/compiler-rt/CMakeLists.txt new file mode 100644 index 00000000..a4424086 --- /dev/null +++ b/projects/compiler-rt/CMakeLists.txt @@ -0,0 +1,232 @@ +# CMake build for CompilerRT. +# +# This build assumes that CompilerRT is checked out into the +# 'projects/compiler-rt' inside of an LLVM tree, it is not a stand-alone build +# system. +# +# An important constraint of the build is that it only produces libraries +# based on the ability of the host toolchain to target various platforms. + +include(LLVMParseArguments) + +# The CompilerRT build system requires CMake version 2.8.8 or higher in order +# to use its support for building convenience "libraries" as a collection of +# .o files. This is particularly useful in producing larger, more complex +# runtime libraries. +cmake_minimum_required(VERSION 2.8.8) + +# Top level target used to build all compiler-rt libraries. +add_custom_target(compiler-rt) + +# Compute the Clang version from the LLVM version. +# FIXME: We should be able to reuse CLANG_VERSION variable calculated +# in Clang cmake files, instead of copying the rules here. +string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION + ${PACKAGE_VERSION}) +# Setup the paths where compiler-rt runtimes and headers should be stored. +set(LIBCLANG_INSTALL_PATH lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION}) +string(TOLOWER ${CMAKE_SYSTEM_NAME} LIBCLANG_OS_DIR) +set(CLANG_RESOURCE_DIR ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}) +set(COMPILER_RT_LIBRARY_OUTPUT_DIR ${CLANG_RESOURCE_DIR}/lib/${LIBCLANG_OS_DIR}) +set(COMPILER_RT_LIBRARY_INSTALL_DIR + ${LIBCLANG_INSTALL_PATH}/lib/${LIBCLANG_OS_DIR}) + +# Add path for custom modules +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + ) +include(AddCompilerRT) + +set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +# Setup custom SDK sysroots. +set(COMPILER_RT_DARWIN_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/darwin) +set(COMPILER_RT_LINUX_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/linux) +include(SanitizerUtils) + +# Detect whether the current target platform is 32-bit or 64-bit, and setup +# the correct commandline flags needed to attempt to target 32-bit and 64-bit. +if (NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND + NOT CMAKE_SIZEOF_VOID_P EQUAL 8) + message(FATAL_ERROR "Please use architecture with 4 or 8 byte pointers.") +endif() +if (NOT MSVC) + set(TARGET_64_BIT_CFLAGS "-m64") + set(TARGET_32_BIT_CFLAGS "-m32") +else() + set(TARGET_64_BIT_CFLAGS "") + set(TARGET_32_BIT_CFLAGS "") +endif() + +# List of architectures we can target. +set(COMPILER_RT_SUPPORTED_ARCH) + +function(get_target_flags_for_arch arch out_var) + list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX) + if(ARCH_INDEX EQUAL -1) + message(FATAL_ERROR "Unsupported architecture: ${arch}") + else() + set(${out_var} ${TARGET_${arch}_CFLAGS} PARENT_SCOPE) + endif() +endfunction() + +# Try to compile a very simple source file to ensure we can target the given +# platform. We use the results of these tests to build only the various target +# runtime libraries supported by our current compilers cross-compiling +# abilities. +set(SIMPLE_SOURCE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple.c) +file(WRITE ${SIMPLE_SOURCE} "#include \nint main() {}") + +# test_target_arch( ) +# Sets the target flags for a given architecture and determines if this +# architecture is supported by trying to build a simple file. +macro(test_target_arch arch) + set(TARGET_${arch}_CFLAGS ${ARGN}) + try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE} + COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}" + CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_${arch}_CFLAGS}") + if(${CAN_TARGET_${arch}}) + list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch}) + endif() +endmacro() + +if("${LLVM_NATIVE_ARCH}" STREQUAL "X86") + if (NOT MSVC) + test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS}) + endif() + test_target_arch(i386 ${TARGET_32_BIT_CFLAGS}) +elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC") + test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS}) +endif() + +# We only support running instrumented tests when we're not cross compiling +# and target a unix-like system. On Android we define the rules for building +# unit tests, but don't execute them. +if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX AND NOT ANDROID) + option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" ON) +else() + option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" OFF) +endif() + +# Check if compiler-rt is built with libc++. +find_flag_in_string("${CMAKE_CXX_FLAGS}" "-stdlib=libc++" + COMPILER_RT_USES_LIBCXX) + +function(filter_available_targets out_var) + set(archs) + foreach(arch ${ARGN}) + list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX) + if(NOT (ARCH_INDEX EQUAL -1) AND CAN_TARGET_${arch}) + list(APPEND archs ${arch}) + endif() + endforeach() + set(${out_var} ${archs} PARENT_SCOPE) +endfunction() + +option(COMPILER_RT_DEBUG "Build runtimes with full debug info" OFF) + +# COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in. +pythonize_bool(COMPILER_RT_DEBUG) + +# Provide some common commmandline flags for Sanitizer runtimes. +if (NOT MSVC) + set(SANITIZER_COMMON_CFLAGS + -fPIC + -fno-builtin + -fno-exceptions + -fomit-frame-pointer + -funwind-tables + -fno-stack-protector + -Wno-gnu # Variadic macros with 0 arguments for ... + -fvisibility=hidden + ) + if (NOT COMPILER_RT_DEBUG) + list(APPEND SANITIZER_COMMON_CFLAGS -O3) + endif() +else() + set(SANITIZER_COMMON_CFLAGS + /MT + /Zi + /Oy- + /GS- + /wd4722 + ) +endif() +# Build sanitizer runtimes with debug info. (MSVC gets /Zi above) +if (NOT MSVC) + check_cxx_compiler_flag(-gline-tables-only SUPPORTS_GLINE_TABLES_ONLY_FLAG) + if(SUPPORTS_GLINE_TABLES_ONLY_FLAG AND NOT COMPILER_RT_DEBUG) + list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only) + else() + list(APPEND SANITIZER_COMMON_CFLAGS -g) + endif() +endif() +# Warnings suppressions. +check_cxx_compiler_flag(-Wno-variadic-macros SUPPORTS_NO_VARIADIC_MACROS_FLAG) +if(SUPPORTS_NO_VARIADIC_MACROS_FLAG) + list(APPEND SANITIZER_COMMON_CFLAGS -Wno-variadic-macros) +endif() +check_cxx_compiler_flag(-Wno-c99-extensions SUPPORTS_NO_C99_EXTENSIONS_FLAG) +if(SUPPORTS_NO_C99_EXTENSIONS_FLAG) + list(APPEND SANITIZER_COMMON_CFLAGS -Wno-c99-extensions) +endif() +# Sanitizer may not have libstdc++, so we can have problems with virtual +# destructors. +check_cxx_compiler_flag(-Wno-non-virtual-dtor SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG) +if (SUPPORTS_NO_NON_VIRTUAL_DTOR_FLAG) + list(APPEND SANITIZER_COMMON_CFLAGS -Wno-non-virtual-dtor) +endif() +check_cxx_compiler_flag(-Wglobal-constructors SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG) +# Not all sanitizers forbid global constructors. + +if(APPLE) + # Obtain the iOS Simulator SDK path from xcodebuild. + execute_process( + COMMAND xcodebuild -version -sdk iphonesimulator Path + OUTPUT_VARIABLE IOSSIM_SDK_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(SANITIZER_COMMON_SUPPORTED_DARWIN_OS osx) + if (IOSSIM_SDK_DIR) + list(APPEND SANITIZER_COMMON_SUPPORTED_DARWIN_OS iossim) + endif() + + if(COMPILER_RT_USES_LIBCXX) + set(SANITIZER_MIN_OSX_VERSION 10.7) + else() + set(SANITIZER_MIN_OSX_VERSION 10.6) + endif() + set(DARWIN_osx_CFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION}) + set(DARWIN_iossim_CFLAGS + -mios-simulator-version-min=7.0 -isysroot ${IOSSIM_SDK_DIR}) + set(DARWIN_osx_LINKFLAGS) + set(DARWIN_iossim_LINKFLAGS + -Wl,-ios_simulator_version_min,7.0.0 + -mios-simulator-version-min=7.0 + -isysroot ${IOSSIM_SDK_DIR}) +endif() + +# Architectures supported by Sanitizer runtimes. Specific sanitizers may +# support only subset of these (e.g. TSan works on x86_64 only). +filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH + x86_64 i386 powerpc64) + +add_subdirectory(include) + +set(SANITIZER_COMMON_LIT_TEST_DEPS + clang clang-headers FileCheck count not llvm-nm llvm-symbolizer + compiler-rt-headers) +# Check code style when running lit tests for sanitizers. +if(UNIX) + list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck) +endif() + +add_subdirectory(lib) + +if(LLVM_INCLUDE_TESTS) + # Currently the tests have not been ported to CMake, so disable this + # directory. + # + #add_subdirectory(test) +endif() diff --git a/projects/compiler-rt/CREDITS.TXT b/projects/compiler-rt/CREDITS.TXT new file mode 100644 index 00000000..818f4fa1 --- /dev/null +++ b/projects/compiler-rt/CREDITS.TXT @@ -0,0 +1,24 @@ +This file is a partial list of people who have contributed to the LLVM/CompilerRT +project. If you have contributed a patch or made some other contribution to +LLVM/CompilerRT, please submit a patch to this file to add yourself, and it will be +done! + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address +(S). + +N: Craig van Vliet +E: cvanvliet@auroraux.org +W: http://www.auroraux.org +D: Code style and Readability fixes. + +N: Edward O'Callaghan +E: eocallaghan@auroraux.org +W: http://www.auroraux.org +D: CMake'ify Compiler-RT build system +D: Maintain Solaris & AuroraUX ports of Compiler-RT + +N: Howard Hinnant +E: hhinnant@apple.com +D: Architect and primary author of compiler-rt diff --git a/projects/compiler-rt/LICENSE.TXT b/projects/compiler-rt/LICENSE.TXT new file mode 100644 index 00000000..6aab1f69 --- /dev/null +++ b/projects/compiler-rt/LICENSE.TXT @@ -0,0 +1,97 @@ +============================================================================== +compiler_rt License +============================================================================== + +The compiler_rt library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +============================================================================== +Copyrights and Licenses for Third Party Software Distributed with LLVM: +============================================================================== +The LLVM software contains code written by third parties. Such software will +have its own individual LICENSE.TXT file in the directory in which it appears. +This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License +applies to all code in the LLVM Distribution, and nothing in any of the +other licenses gives permission to use the names of the LLVM Team or the +University of Illinois to endorse or promote products derived from this +Software. + +The following pieces of software have additional or alternate copyrights, +licenses, and/or restrictions: + +Program Directory +------- --------- +mach_override lib/interception/mach_override diff --git a/projects/compiler-rt/Makefile b/projects/compiler-rt/Makefile new file mode 100644 index 00000000..6747ea4d --- /dev/null +++ b/projects/compiler-rt/Makefile @@ -0,0 +1,275 @@ +SubDirs := lib + +# Set default rule before anything else. +all: help + +include make/config.mk +include make/util.mk +# If SRCROOT is defined, assume we are doing an Apple style build. We should be +# able to use RC_XBS for this but that is unused during "make installsrc". +ifdef SRCROOT + include make/AppleBI.mk +endif + +# Make sure we don't build with a missing ProjObjRoot. +ifeq ($(ProjObjRoot),) +$(error Refusing to build with empty ProjObjRoot variable) +endif + +############## + +### +# Rules + +### +# Top level targets + +# FIXME: Document the available subtargets. +help: + @echo "usage: make [{VARIABLE=VALUE}*] target" + @echo + @echo "User variables:" + @echo " VERBOSE=1: Use to show all commands [default=0]" + @echo + @echo "Available targets:" + @echo " : build the libraries for 'platform'" + @echo " clean: clean all configurations" + @echo " test: run unit tests" + @echo + @echo " info-platforms: list available platforms" + @echo " help-devel: print additional help for developers" + @echo + +help-devel: help + @echo "Development targets:" + @echo " -:" + @echo " build the libraries for a single platform config" + @echo " --:" + @echo " build the libraries for a single config and arch" + @echo " info-functions: list available compiler-rt functions" + @echo " help-hidden: print help for Makefile debugging" + @echo + +help-hidden: help-devel + @echo "Debugging variables:" + @echo " DEBUGMAKE=1: enable some Makefile logging [default=]" + @echo " =2: enable more Makefile logging" + @echo + @echo "Debugging targets:" + @echo " make-print-FOO: print information on the variable 'FOO'" + @echo + +info-functions: + @echo "compiler-rt Available Functions" + @echo + @echo "All Functions: $(AvailableFunctions)" + @$(foreach fn,$(AvailableFunctions),\ + printf " %-20s - available in (%s)\n" $(fn)\ + "$(foreach key,$(AvailableIn.$(fn)),$($(key).Dir))";) + +info-platforms: + @echo "compiler-rt Available Platforms" + @echo + @echo "Platforms:" + @$(foreach key,$(PlatformKeys),\ + printf " %s - from '%s'\n" $($(key).Name) $($(key).Path);\ + printf " %s\n" "$($(key).Description)";\ + printf " Configurations: %s\n\n" "$($(key).Configs)";) + +# Provide default clean target which is extended by other templates. +.PHONY: clean +clean:: + +# Test +.PHONY: test +test: + cd test/Unit && ./test + +### +# Directory handling magic. + +# Create directories as needed, and timestamp their creation. +%/.dir: + $(Summary) " MKDIR: $*" + $(Verb) $(MKDIR) $* > /dev/null + $(Verb) echo 'Created.' > $@ + +# Remove directories +%/.remove: + $(Verb) $(RM) -r $* + +### +# Include child makefile fragments + +Dir := . +include make/subdir.mk +include make/lib_info.mk +include make/lib_util.mk +include make/lib_platforms.mk + +### +# Define Platform Rules + +define PerPlatform_template +$(call Set,Tmp.Key,$(1)) +$(call Set,Tmp.Name,$($(Tmp.Key).Name)) +$(call Set,Tmp.Configs,$($(Tmp.Key).Configs)) +$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)) + +# Top-Level Platform Target +$(Tmp.Name):: $(Tmp.Configs:%=$(Tmp.Name)-%) +.PHONY: $(Tmp.Name) + +clean:: + $(Verb) rm -rf $(Tmp.ObjPath) + +# Per-Config Libraries +$(foreach config,$(Tmp.Configs),\ + $(call PerPlatformConfig_template,$(config))) +endef + +define PerPlatformConfig_template +$(call Set,Tmp.Config,$(1)) +$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)) +$(call Set,Tmp.SHARED_LIBRARY,$(strip \ + $(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.SHARED_LIBRARY_SUFFIX,$(strip \ + $(call GetCNAVar,SHARED_LIBRARY_SUFFIX,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) + +# Compute the library suffix. +$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)), + $(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)), + $(call Set,Tmp.LibrarySuffix,a)) + +# Compute the archs to build, depending on whether this is a universal build or +# not. +$(call Set,Tmp.ArchsToBuild,\ + $(if $(call IsDefined,$(Tmp.Key).UniversalArchs),\ + $(strip \ + $(or $($(Tmp.Key).UniversalArchs.$(Tmp.Config)),\ + $($(Tmp.Key).UniversalArchs))),\ + $(call VarOrDefault,$(Tmp.Key).Arch.$(Tmp.Config),$($(Tmp.Key).Arch)))) + +# Copy or lipo to create the per-config library. +$(call Set,Tmp.Inputs,$(Tmp.ArchsToBuild:%=$(Tmp.ObjPath)/%/libcompiler_rt.$(Tmp.LibrarySuffix))) +$(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix): $(Tmp.Inputs) $(Tmp.ObjPath)/.dir + $(Summary) " FINAL-ARCHIVE: $(Tmp.Name)/$(Tmp.Config): $$@" + -$(Verb) $(RM) $$@ + $(if $(call streq,1,$(words $(Tmp.ArchsToBuild))), \ + $(Verb) $(CP) $(Tmp.Inputs) $$@, \ + $(Verb) $(LIPO) -create -output $$@ $(Tmp.Inputs)) +.PRECIOUS: $(Tmp.ObjPath)/.dir + +# Per-Config Targets +$(Tmp.Name)-$(Tmp.Config):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix) +.PHONY: $(Tmp.Name)-$(Tmp.Config) + +# Per-Config-Arch Libraries +$(foreach arch,$(Tmp.ArchsToBuild),\ + $(call PerPlatformConfigArch_template,$(arch))) +endef + +define PerPlatformConfigArch_template +$(call Set,Tmp.Arch,$(1)) +$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch)) +$(call Set,Tmp.Functions,$(strip \ + $(AlwaysRequiredModules) \ + $(call GetCNAVar,FUNCTIONS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.Optimized,$(strip \ + $(call GetCNAVar,OPTIMIZED,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.AR,$(strip \ + $(call GetCNAVar,AR,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.ARFLAGS,$(strip \ + $(call GetCNAVar,ARFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.CC,$(strip \ + $(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.LDFLAGS,$(strip \ + $(call GetCNAVar,LDFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.RANLIB,$(strip \ + $(call GetCNAVar,RANLIB,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.RANLIBFLAGS,$(strip \ + $(call GetCNAVar,RANLIBFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.SHARED_LIBRARY,$(strip \ + $(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) + +# Compute the library suffix. +$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)), + $(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)), + $(call Set,Tmp.LibrarySuffix,a)) + +# Compute the object inputs for this library. +$(call Set,Tmp.Inputs,\ + $(foreach fn,$(sort $(Tmp.Functions)),\ + $(call Set,Tmp.FnDir,\ + $(call SelectFunctionDir,$(Tmp.Config),$(Tmp.Arch),$(fn),$(Tmp.Optimized)))\ + $(Tmp.ObjPath)/$(Tmp.FnDir)/$(fn).o)) +$(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir + $(Summary) " ARCHIVE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@" + -$(Verb) $(RM) $$@ + $(Verb) $(Tmp.AR) $(Tmp.ARFLAGS) $$@ $(Tmp.Inputs) + $(Verb) $(Tmp.RANLIB) $(Tmp.RANLIBFLAGS) $$@ +$(Tmp.ObjPath)/libcompiler_rt.dylib: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir + $(Summary) " DYLIB: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@" + $(Verb) $(Tmp.CC) -arch $(Tmp.Arch) -dynamiclib -o $$@ \ + $(Tmp.Inputs) $(Tmp.LDFLAGS) +$(Tmp.ObjPath)/libcompiler_rt.so: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir + $(Summary) " SO: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@" + $(Verb) $(Tmp.CC) -shared -o $$@ \ + $(Tmp.Inputs) $(Tmp.LDFLAGS) +.PRECIOUS: $(Tmp.ObjPath)/.dir + +# Per-Config-Arch Targets +$(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix) +.PHONY: $(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch) + +# Per-Config-Arch-SubDir Objects +$(foreach key,$(SubDirKeys),\ + $(call PerPlatformConfigArchSubDir_template,$(key))) +endef + +define PerPlatformConfigArchSubDir_template +$(call Set,Tmp.SubDirKey,$(1)) +$(call Set,Tmp.SubDir,$($(Tmp.SubDirKey).Dir)) +$(call Set,Tmp.SrcPath,$(ProjSrcRoot)/$(Tmp.SubDir)) +$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch)/$(Tmp.SubDirKey)) +$(call Set,Tmp.Dependencies,$($(Tmp.SubDirKey).Dependencies)) +$(call Set,Tmp.CC,$(strip \ + $(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.KERNEL_USE,$(strip \ + $(call GetCNAVar,KERNEL_USE,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.VISIBILITY_HIDDEN,$(strip \ + $(call GetCNAVar,VISIBILITY_HIDDEN,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) +$(call Set,Tmp.CFLAGS,$(strip \ + $(if $(call IsDefined,$(Tmp.Key).UniversalArchs),-arch $(Tmp.Arch),)\ + $(if $(call streq,$(Tmp.VISIBILITY_HIDDEN),1),\ + -fvisibility=hidden -DVISIBILITY_HIDDEN,)\ + $(if $(call streq,$(Tmp.KERNEL_USE),1),\ + -mkernel -DKERNEL_USE,)\ + $(call GetCNAVar,CFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch)))) + +$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.s $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir + $(Summary) " ASSEMBLE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" + $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c -o $$@ $$< +$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.S $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir + $(Summary) " ASSEMBLE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" + $(Verb) $(Tmp.CC) $(Tmp.CFLAGS) -c -o $$@ $$< +$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.c $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir + $(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" + $(Verb) $(Tmp.CC) $(COMMON_CFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$< +$(Tmp.ObjPath)/%.o: $(Tmp.SrcPath)/%.cc $(Tmp.Dependencies) $(Tmp.ObjPath)/.dir + $(Summary) " COMPILE: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$<" + $(Verb) $(Tmp.CC) $(COMMON_CXXFLAGS) $(Tmp.CFLAGS) -c -o $$@ $$< +.PRECIOUS: $(Tmp.ObjPath)/.dir + +endef + +# Run templates. +$(foreach key,$(PlatformKeys),\ + $(eval $(call PerPlatform_template,$(key)))) + +### + +ifneq ($(DEBUGMAKE),) + $(info MAKE: Done processing Makefile) + $(info ) +endif diff --git a/projects/compiler-rt/README.txt b/projects/compiler-rt/README.txt new file mode 100644 index 00000000..b37c0aec --- /dev/null +++ b/projects/compiler-rt/README.txt @@ -0,0 +1,343 @@ +Compiler-RT +================================ + +This directory and its subdirectories contain source code for the compiler +support routines. + +Compiler-RT is open source software. You may freely distribute it under the +terms of the license agreement found in LICENSE.txt. + +================================ + +This is a replacement library for libgcc. Each function is contained +in its own file. Each function has a corresponding unit test under +test/Unit. + +A rudimentary script to test each file is in the file called +test/Unit/test. + +Here is the specification for this library: + +http://gcc.gnu.org/onlinedocs/gccint/Libgcc.html#Libgcc + +Here is a synopsis of the contents of this library: + +typedef int si_int; +typedef unsigned su_int; + +typedef long long di_int; +typedef unsigned long long du_int; + +// Integral bit manipulation + +di_int __ashldi3(di_int a, si_int b); // a << b +ti_int __ashlti3(ti_int a, si_int b); // a << b + +di_int __ashrdi3(di_int a, si_int b); // a >> b arithmetic (sign fill) +ti_int __ashrti3(ti_int a, si_int b); // a >> b arithmetic (sign fill) +di_int __lshrdi3(di_int a, si_int b); // a >> b logical (zero fill) +ti_int __lshrti3(ti_int a, si_int b); // a >> b logical (zero fill) + +si_int __clzsi2(si_int a); // count leading zeros +si_int __clzdi2(di_int a); // count leading zeros +si_int __clzti2(ti_int a); // count leading zeros +si_int __ctzsi2(si_int a); // count trailing zeros +si_int __ctzdi2(di_int a); // count trailing zeros +si_int __ctzti2(ti_int a); // count trailing zeros + +si_int __ffsdi2(di_int a); // find least significant 1 bit +si_int __ffsti2(ti_int a); // find least significant 1 bit + +si_int __paritysi2(si_int a); // bit parity +si_int __paritydi2(di_int a); // bit parity +si_int __parityti2(ti_int a); // bit parity + +si_int __popcountsi2(si_int a); // bit population +si_int __popcountdi2(di_int a); // bit population +si_int __popcountti2(ti_int a); // bit population + +uint32_t __bswapsi2(uint32_t a); // a byteswapped, arm only +uint64_t __bswapdi2(uint64_t a); // a byteswapped, arm only + +// Integral arithmetic + +di_int __negdi2 (di_int a); // -a +ti_int __negti2 (ti_int a); // -a +di_int __muldi3 (di_int a, di_int b); // a * b +ti_int __multi3 (ti_int a, ti_int b); // a * b +si_int __divsi3 (si_int a, si_int b); // a / b signed +di_int __divdi3 (di_int a, di_int b); // a / b signed +ti_int __divti3 (ti_int a, ti_int b); // a / b signed +su_int __udivsi3 (su_int n, su_int d); // a / b unsigned +du_int __udivdi3 (du_int a, du_int b); // a / b unsigned +tu_int __udivti3 (tu_int a, tu_int b); // a / b unsigned +si_int __modsi3 (si_int a, si_int b); // a % b signed +di_int __moddi3 (di_int a, di_int b); // a % b signed +ti_int __modti3 (ti_int a, ti_int b); // a % b signed +su_int __umodsi3 (su_int a, su_int b); // a % b unsigned +du_int __umoddi3 (du_int a, du_int b); // a % b unsigned +tu_int __umodti3 (tu_int a, tu_int b); // a % b unsigned +du_int __udivmoddi4(du_int a, du_int b, du_int* rem); // a / b, *rem = a % b unsigned +tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); // a / b, *rem = a % b unsigned +su_int __udivmodsi4(su_int a, su_int b, su_int* rem); // a / b, *rem = a % b unsigned +si_int __divmodsi4(si_int a, si_int b, si_int* rem); // a / b, *rem = a % b signed + + + +// Integral arithmetic with trapping overflow + +si_int __absvsi2(si_int a); // abs(a) +di_int __absvdi2(di_int a); // abs(a) +ti_int __absvti2(ti_int a); // abs(a) + +si_int __negvsi2(si_int a); // -a +di_int __negvdi2(di_int a); // -a +ti_int __negvti2(ti_int a); // -a + +si_int __addvsi3(si_int a, si_int b); // a + b +di_int __addvdi3(di_int a, di_int b); // a + b +ti_int __addvti3(ti_int a, ti_int b); // a + b + +si_int __subvsi3(si_int a, si_int b); // a - b +di_int __subvdi3(di_int a, di_int b); // a - b +ti_int __subvti3(ti_int a, ti_int b); // a - b + +si_int __mulvsi3(si_int a, si_int b); // a * b +di_int __mulvdi3(di_int a, di_int b); // a * b +ti_int __mulvti3(ti_int a, ti_int b); // a * b + + +// Integral arithmetic which returns if overflow + +si_int __mulosi4(si_int a, si_int b, int* overflow); // a * b, overflow set to one if result not in signed range +di_int __mulodi4(di_int a, di_int b, int* overflow); // a * b, overflow set to one if result not in signed range +ti_int __muloti4(ti_int a, ti_int b, int* overflow); // a * b, overflow set to + one if result not in signed range + + +// Integral comparison: a < b -> 0 +// a == b -> 1 +// a > b -> 2 + +si_int __cmpdi2 (di_int a, di_int b); +si_int __cmpti2 (ti_int a, ti_int b); +si_int __ucmpdi2(du_int a, du_int b); +si_int __ucmpti2(tu_int a, tu_int b); + +// Integral / floating point conversion + +di_int __fixsfdi( float a); +di_int __fixdfdi( double a); +di_int __fixxfdi(long double a); + +ti_int __fixsfti( float a); +ti_int __fixdfti( double a); +ti_int __fixxfti(long double a); +uint64_t __fixtfdi(long double input); // ppc only, doesn't match documentation + +su_int __fixunssfsi( float a); +su_int __fixunsdfsi( double a); +su_int __fixunsxfsi(long double a); + +du_int __fixunssfdi( float a); +du_int __fixunsdfdi( double a); +du_int __fixunsxfdi(long double a); + +tu_int __fixunssfti( float a); +tu_int __fixunsdfti( double a); +tu_int __fixunsxfti(long double a); +uint64_t __fixunstfdi(long double input); // ppc only + +float __floatdisf(di_int a); +double __floatdidf(di_int a); +long double __floatdixf(di_int a); +long double __floatditf(int64_t a); // ppc only + +float __floattisf(ti_int a); +double __floattidf(ti_int a); +long double __floattixf(ti_int a); + +float __floatundisf(du_int a); +double __floatundidf(du_int a); +long double __floatundixf(du_int a); +long double __floatunditf(uint64_t a); // ppc only + +float __floatuntisf(tu_int a); +double __floatuntidf(tu_int a); +long double __floatuntixf(tu_int a); + +// Floating point raised to integer power + +float __powisf2( float a, si_int b); // a ^ b +double __powidf2( double a, si_int b); // a ^ b +long double __powixf2(long double a, si_int b); // a ^ b +long double __powitf2(long double a, si_int b); // ppc only, a ^ b + +// Complex arithmetic + +// (a + ib) * (c + id) + + float _Complex __mulsc3( float a, float b, float c, float d); + double _Complex __muldc3(double a, double b, double c, double d); +long double _Complex __mulxc3(long double a, long double b, + long double c, long double d); +long double _Complex __multc3(long double a, long double b, + long double c, long double d); // ppc only + +// (a + ib) / (c + id) + + float _Complex __divsc3( float a, float b, float c, float d); + double _Complex __divdc3(double a, double b, double c, double d); +long double _Complex __divxc3(long double a, long double b, + long double c, long double d); +long double _Complex __divtc3(long double a, long double b, + long double c, long double d); // ppc only + + +// Runtime support + +// __clear_cache() is used to tell process that new instructions have been +// written to an address range. Necessary on processors that do not have +// a unified instuction and data cache. +void __clear_cache(void* start, void* end); + +// __enable_execute_stack() is used with nested functions when a trampoline +// function is written onto the stack and that page range needs to be made +// executable. +void __enable_execute_stack(void* addr); + +// __gcc_personality_v0() is normally only called by the system unwinder. +// C code (as opposed to C++) normally does not need a personality function +// because there are no catch clauses or destructors to be run. But there +// is a C language extension __attribute__((cleanup(func))) which marks local +// variables as needing the cleanup function "func" to be run when the +// variable goes out of scope. That includes when an exception is thrown, +// so a personality handler is needed. +_Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions, + uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, + _Unwind_Context_t context); + +// for use with some implementations of assert() in +void __eprintf(const char* format, const char* assertion_expression, + const char* line, const char* file); + + + +// Power PC specific functions + +// There is no C interface to the saveFP/restFP functions. They are helper +// functions called by the prolog and epilog of functions that need to save +// a number of non-volatile float point registers. +saveFP +restFP + +// PowerPC has a standard template for trampoline functions. This function +// generates a custom trampoline function with the specific realFunc +// and localsPtr values. +void __trampoline_setup(uint32_t* trampOnStack, int trampSizeAllocated, + const void* realFunc, void* localsPtr); + +// adds two 128-bit double-double precision values ( x + y ) +long double __gcc_qadd(long double x, long double y); + +// subtracts two 128-bit double-double precision values ( x - y ) +long double __gcc_qsub(long double x, long double y); + +// multiples two 128-bit double-double precision values ( x * y ) +long double __gcc_qmul(long double x, long double y); + +// divides two 128-bit double-double precision values ( x / y ) +long double __gcc_qdiv(long double a, long double b); + + +// ARM specific functions + +// There is no C interface to the switch* functions. These helper functions +// are only needed by Thumb1 code for efficient switch table generation. +switch16 +switch32 +switch8 +switchu8 + +// There is no C interface to the *_vfp_d8_d15_regs functions. There are +// called in the prolog and epilog of Thumb1 functions. When the C++ ABI use +// SJLJ for exceptions, each function with a catch clause or destuctors needs +// to save and restore all registers in it prolog and epliog. But there is +// no way to access vector and high float registers from thumb1 code, so the +// compiler must add call outs to these helper functions in the prolog and +// epilog. +restore_vfp_d8_d15_regs +save_vfp_d8_d15_regs + + +// Note: long ago ARM processors did not have floating point hardware support. +// Floating point was done in software and floating point parameters were +// passed in integer registers. When hardware support was added for floating +// point, new *vfp functions were added to do the same operations but with +// floating point parameters in floating point registers. + +// Undocumented functions + +float __addsf3vfp(float a, float b); // Appears to return a + b +double __adddf3vfp(double a, double b); // Appears to return a + b +float __divsf3vfp(float a, float b); // Appears to return a / b +double __divdf3vfp(double a, double b); // Appears to return a / b +int __eqsf2vfp(float a, float b); // Appears to return one + // iff a == b and neither is NaN. +int __eqdf2vfp(double a, double b); // Appears to return one + // iff a == b and neither is NaN. +double __extendsfdf2vfp(float a); // Appears to convert from + // float to double. +int __fixdfsivfp(double a); // Appears to convert from + // double to int. +int __fixsfsivfp(float a); // Appears to convert from + // float to int. +unsigned int __fixunssfsivfp(float a); // Appears to convert from + // float to unsigned int. +unsigned int __fixunsdfsivfp(double a); // Appears to convert from + // double to unsigned int. +double __floatsidfvfp(int a); // Appears to convert from + // int to double. +float __floatsisfvfp(int a); // Appears to convert from + // int to float. +double __floatunssidfvfp(unsigned int a); // Appears to convert from + // unisgned int to double. +float __floatunssisfvfp(unsigned int a); // Appears to convert from + // unisgned int to float. +int __gedf2vfp(double a, double b); // Appears to return __gedf2 + // (a >= b) +int __gesf2vfp(float a, float b); // Appears to return __gesf2 + // (a >= b) +int __gtdf2vfp(double a, double b); // Appears to return __gtdf2 + // (a > b) +int __gtsf2vfp(float a, float b); // Appears to return __gtsf2 + // (a > b) +int __ledf2vfp(double a, double b); // Appears to return __ledf2 + // (a <= b) +int __lesf2vfp(float a, float b); // Appears to return __lesf2 + // (a <= b) +int __ltdf2vfp(double a, double b); // Appears to return __ltdf2 + // (a < b) +int __ltsf2vfp(float a, float b); // Appears to return __ltsf2 + // (a < b) +double __muldf3vfp(double a, double b); // Appears to return a * b +float __mulsf3vfp(float a, float b); // Appears to return a * b +int __nedf2vfp(double a, double b); // Appears to return __nedf2 + // (a != b) +double __negdf2vfp(double a); // Appears to return -a +float __negsf2vfp(float a); // Appears to return -a +float __negsf2vfp(float a); // Appears to return -a +double __subdf3vfp(double a, double b); // Appears to return a - b +float __subsf3vfp(float a, float b); // Appears to return a - b +float __truncdfsf2vfp(double a); // Appears to convert from + // double to float. +int __unorddf2vfp(double a, double b); // Appears to return __unorddf2 +int __unordsf2vfp(float a, float b); // Appears to return __unordsf2 + + +Preconditions are listed for each function at the definition when there are any. +Any preconditions reflect the specification at +http://gcc.gnu.org/onlinedocs/gccint/Libgcc.html#Libgcc. + +Assumptions are listed in "int_lib.h", and in individual files. Where possible +assumptions are checked at compile time. diff --git a/projects/compiler-rt/SDKs/README.txt b/projects/compiler-rt/SDKs/README.txt new file mode 100644 index 00000000..b95575e8 --- /dev/null +++ b/projects/compiler-rt/SDKs/README.txt @@ -0,0 +1,9 @@ +It is often convenient to be able to build compiler-rt libraries for a certain +platform without having a full SDK or development environment installed. + +This makes it easy for users to build a compiler which can target a number of +different platforms, without having to actively maintain full development +environments for those platforms. + +Since compiler-rt's libraries typically have minimal interaction with the +system, we achieve this by stubbing out the SDKs of certain platforms. diff --git a/projects/compiler-rt/SDKs/darwin/README.txt b/projects/compiler-rt/SDKs/darwin/README.txt new file mode 100644 index 00000000..ea30af35 --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/README.txt @@ -0,0 +1,3 @@ +The Darwin platforms are all similar enough we roll them into one SDK, and use +preprocessor tricks to get the right definitions for the few things which +diverge between OS X and iOS. diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/errno.h b/projects/compiler-rt/SDKs/darwin/usr/include/errno.h new file mode 100644 index 00000000..f06e5371 --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/errno.h @@ -0,0 +1,17 @@ +/* ===-- errno.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#include diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/fcntl.h b/projects/compiler-rt/SDKs/darwin/usr/include/fcntl.h new file mode 100644 index 00000000..a5f91e3a --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/fcntl.h @@ -0,0 +1,17 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#include diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/limits.h b/projects/compiler-rt/SDKs/darwin/usr/include/limits.h new file mode 100644 index 00000000..5495a784 --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/limits.h @@ -0,0 +1,23 @@ +/* ===-- limits.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __LIMITS_H__ +#define __LIMITS_H__ + +/* This is only here as a landing pad for the include_next from the compiler's + built-in limits.h. */ + +#endif /* __LIMITS_H__ */ diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/stdio.h b/projects/compiler-rt/SDKs/darwin/usr/include/stdio.h new file mode 100644 index 00000000..006652fb --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/stdio.h @@ -0,0 +1,89 @@ +/* ===-- stdio.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDIO_H__ +#define __STDIO_H__ + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct __sFILE FILE; +typedef __SIZE_TYPE__ size_t; + +/* Determine the appropriate fdopen, fopen(), and fwrite() functions. */ +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +# if defined(__i386) +# define __FDOPEN_NAME "_fdopen$UNIX2003" +# define __FOPEN_NAME "_fopen$UNIX2003" +# define __FWRITE_NAME "_fwrite$UNIX2003" +# elif defined(__x86_64__) +# define __FDOPEN_NAME "_fdopen" +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# elif defined(__arm) +# define __FDOPEN_NAME "_fdopen" +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# else +# error "unrecognized architecture for targetting OS X" +# endif +#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# if defined(__i386) || defined (__x86_64) +# define __FDOPEN_NAME "_fdopen" +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# elif defined(__arm) +# define __FDOPEN_NAME "_fdopen" +# define __FOPEN_NAME "_fopen" +# define __FWRITE_NAME "_fwrite" +# else +# error "unrecognized architecture for targetting iOS" +# endif +#else +# error "unrecognized architecture for targetting Darwin" +#endif + +# define stderr __stderrp +extern FILE *__stderrp; + +#ifndef SEEK_SET +#define SEEK_SET 0 /* set file offset to offset */ +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#endif +#ifndef SEEK_END +#define SEEK_END 2 /* set file offset to EOF plus offset */ +#endif + +int fclose(FILE *); +int fflush(FILE *); +FILE *fopen(const char * __restrict, const char * __restrict) __asm(__FOPEN_NAME); +FILE *fdopen(int, const char *) __asm(__FDOPEN_NAME); +int fprintf(FILE * __restrict, const char * __restrict, ...); +size_t fwrite(const void * __restrict, size_t, size_t, FILE * __restrict) + __asm(__FWRITE_NAME); +size_t fread(void * __restrict, size_t, size_t, FILE * __restrict); +long ftell(FILE *); +int fseek(FILE *, long, int); +int snprintf(char * __restrict, size_t, const char * __restrict, ...); + +#if defined(__cplusplus) +} +#endif + +#endif /* __STDIO_H__ */ diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/stdlib.h b/projects/compiler-rt/SDKs/darwin/usr/include/stdlib.h new file mode 100644 index 00000000..b6d3171c --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/stdlib.h @@ -0,0 +1,32 @@ +/* ===-- stdlib.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDLIB_H__ +#define __STDLIB_H__ + +#define NULL ((void *)0) + +typedef __SIZE_TYPE__ size_t; + +void abort(void) __attribute__((__noreturn__)); +int atexit(void (*)(void)); +int atoi(const char *); +void free(void *); +char *getenv(const char *); +void *malloc(size_t); +void *realloc(void *, size_t); + +#endif /* __STDLIB_H__ */ diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/string.h b/projects/compiler-rt/SDKs/darwin/usr/include/string.h new file mode 100644 index 00000000..c6ab5d8e --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/string.h @@ -0,0 +1,52 @@ +/* ===-- string.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STRING_H__ +#define __STRING_H__ + +typedef __SIZE_TYPE__ size_t; + +int memcmp(const void *, const void *, size_t); +void *memcpy(void *, const void *, size_t); +void *memset(void *, int, size_t); +char *strcat(char *, const char *); +char *strcpy(char *, const char *); +char *strdup(const char *); +size_t strlen(const char *); +char *strncpy(char *, const char *, size_t); + +/* Determine the appropriate strerror() function. */ +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +# if defined(__i386) +# define __STRERROR_NAME "_strerror$UNIX2003" +# elif defined(__x86_64__) || defined(__arm) +# define __STRERROR_NAME "_strerror" +# else +# error "unrecognized architecture for targetting OS X" +# endif +#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# if defined(__i386) || defined (__x86_64) || defined(__arm) +# define __STRERROR_NAME "_strerror" +# else +# error "unrecognized architecture for targetting iOS" +# endif +#else +# error "unrecognized architecture for targetting Darwin" +#endif + +char *strerror(int) __asm(__STRERROR_NAME); + +#endif /* __STRING_H__ */ diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/sys/errno.h b/projects/compiler-rt/SDKs/darwin/usr/include/sys/errno.h new file mode 100644 index 00000000..4befe385 --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/sys/errno.h @@ -0,0 +1,31 @@ +/* ===-- errno.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef _SYS_ERRNO_H_ +#define _SYS_ERRNO_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +extern int *__error(void); +#define errno (*__error()) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/sys/fcntl.h b/projects/compiler-rt/SDKs/darwin/usr/include/sys/fcntl.h new file mode 100644 index 00000000..b71706bf --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/sys/fcntl.h @@ -0,0 +1,52 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef _SYS_FCNTL_H_ +#define _SYS_FCNTL_H_ + +/* Determine the appropriate open function. */ +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) +# if defined(__i386) +# define __OPEN_NAME "_open$UNIX2003" +# elif defined(__x86_64__) +# define __OPEN_NAME "_open" +# elif defined(__arm) +# define __OPEN_NAME "_open" +# else +# error "unrecognized architecture for targetting OS X" +# endif +#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) +# if defined(__i386) || defined (__x86_64) +# define __OPEN_NAME "_open" +# elif defined(__arm) +# define __OPEN_NAME "_open" +# else +# error "unrecognized architecture for targetting iOS" +# endif +#else +# error "unrecognized architecture for targetting Darwin" +#endif + +#define O_RDONLY 0x0000 /* open for reading only */ +#define O_WRONLY 0x0001 /* open for writing only */ +#define O_RDWR 0x0002 /* open for reading and writing */ +#define O_ACCMODE 0x0003 /* mask for above modes */ + +#define O_CREAT 0x0200 /* create if nonexistant */ + +int open(const char *, int, ...) __asm(__OPEN_NAME); + +#endif /* !_SYS_FCNTL_H_ */ diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/sys/mman.h b/projects/compiler-rt/SDKs/darwin/usr/include/sys/mman.h new file mode 100644 index 00000000..84561f1b --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/sys/mman.h @@ -0,0 +1,42 @@ +/* ===-- mman.h - stub SDK header for compiler-rt ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_MMAN_H__ +#define __SYS_MMAN_H__ + +typedef __SIZE_TYPE__ size_t; + +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 + +#define MAP_SHARED 0x0001 +#define MAP_PRIVATE 0x0002 + +#define MAP_FILE 0x0000 +#define MAP_ANON 0x1000 + +#define MS_ASYNC 0x0001 +#define MS_INVALIDATE 0x0002 +#define MS_SYNC 0x0010 + +void *mmap(void *addr, size_t len, int prot, int flags, int fd, + long long offset); +int munmap(void *addr, size_t len); +int msync(void *addr, size_t len, int flags); + +#endif /* __SYS_MMAN_H__ */ diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/sys/stat.h b/projects/compiler-rt/SDKs/darwin/usr/include/sys/stat.h new file mode 100644 index 00000000..6225f908 --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/sys/stat.h @@ -0,0 +1,25 @@ +/* ===-- stat.h - stub SDK header for compiler-rt ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_STAT_H__ +#define __SYS_STAT_H__ + +typedef unsigned short uint16_t; +typedef uint16_t mode_t; + +int mkdir(const char *, mode_t); + +#endif /* __SYS_STAT_H__ */ diff --git a/projects/compiler-rt/SDKs/darwin/usr/include/sys/types.h b/projects/compiler-rt/SDKs/darwin/usr/include/sys/types.h new file mode 100644 index 00000000..b425767b --- /dev/null +++ b/projects/compiler-rt/SDKs/darwin/usr/include/sys/types.h @@ -0,0 +1,20 @@ +/* ===-- types.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_TYPES_H__ +#define __SYS_TYPES_H__ + +#endif /* __SYS_TYPES_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/README.txt b/projects/compiler-rt/SDKs/linux/README.txt new file mode 100644 index 00000000..aa0604af --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/README.txt @@ -0,0 +1,2 @@ +This is a stub SDK for Linux. Currently, this has only been tested on i386 and +x86_64 using the Clang compiler. diff --git a/projects/compiler-rt/SDKs/linux/usr/include/endian.h b/projects/compiler-rt/SDKs/linux/usr/include/endian.h new file mode 100644 index 00000000..95528db1 --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/endian.h @@ -0,0 +1,29 @@ +/* ===-- endian.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __ENDIAN_H__ +#define __ENDIAN_H__ + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 + +#if defined(__LITTLE_ENDIAN__) || defined(__ORDER_LITTLE_ENDIAN__) +#define __BYTE_ORDER __LITTLE_ENDIAN +#else +#define __BYTE_ORDER __BIG_ENDIAN +#endif + +#endif /* __ENDIAN_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/fcntl.h b/projects/compiler-rt/SDKs/linux/usr/include/fcntl.h new file mode 100644 index 00000000..a5f91e3a --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/fcntl.h @@ -0,0 +1,17 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#include diff --git a/projects/compiler-rt/SDKs/linux/usr/include/limits.h b/projects/compiler-rt/SDKs/linux/usr/include/limits.h new file mode 100644 index 00000000..5495a784 --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/limits.h @@ -0,0 +1,23 @@ +/* ===-- limits.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __LIMITS_H__ +#define __LIMITS_H__ + +/* This is only here as a landing pad for the include_next from the compiler's + built-in limits.h. */ + +#endif /* __LIMITS_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/stdio.h b/projects/compiler-rt/SDKs/linux/usr/include/stdio.h new file mode 100644 index 00000000..fba59364 --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/stdio.h @@ -0,0 +1,43 @@ +/* ===-- stdio.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDIO_H__ +#define __STDIO_H__ + +typedef __SIZE_TYPE__ size_t; + +struct _IO_FILE; +typedef struct _IO_FILE FILE; + +extern struct _IO_FILE *stdin; +extern struct _IO_FILE *stdout; +extern struct _IO_FILE *stderr; + +#define SEEK_SET 0 /* set file offset to offset */ +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#define SEEK_END 2 /* set file offset to EOF plus offset */ + +extern int fclose(FILE *); +extern int fflush(FILE *); +extern FILE *fopen(const char * restrict, const char * restrict); +extern FILE *fdopen(int, const char * restrict); +extern int fprintf(FILE * restrict, const char * restrict, ...); +extern size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict); +extern size_t fread(void * restrict, size_t, size_t, FILE * restrict); +extern long ftell(FILE *); +extern int fseek(FILE *, long, int); + +#endif /* __STDIO_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/stdlib.h b/projects/compiler-rt/SDKs/linux/usr/include/stdlib.h new file mode 100644 index 00000000..966b29db --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/stdlib.h @@ -0,0 +1,36 @@ +/* ===-- stdlib.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STDLIB_H__ +#define __STDLIB_H__ + +#define NULL ((void *)0) + +typedef __SIZE_TYPE__ size_t; + +void abort(void) __attribute__((__nothrow__)) __attribute__((__noreturn__)); +int atexit(void (*)(void)) __attribute__((__nothrow__)); +int atoi(const char *) __attribute__((__nothrow__)); +void free(void *) __attribute__((__nothrow__)); +char *getenv(const char *) __attribute__((__nothrow__)) + __attribute__((__nonnull__(1))); + __attribute__((__warn_unused_result__)); +void *malloc(size_t) __attribute__((__nothrow__)) __attribute((__malloc__)) + __attribute__((__warn_unused_result__)); +void *realloc(void *, size_t) __attribute__((__nothrow__)) __attribute((__malloc__)) + __attribute__((__warn_unused_result__)); + +#endif /* __STDLIB_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/string.h b/projects/compiler-rt/SDKs/linux/usr/include/string.h new file mode 100644 index 00000000..c7da1f57 --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/string.h @@ -0,0 +1,31 @@ +/* ===-- string.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __STRING_H__ +#define __STRING_H__ + +typedef __SIZE_TYPE__ size_t; + +int memcmp(const void *, const void *, size_t); +void *memcpy(void *, const void *, size_t); +void *memset(void *, int, size_t); +char *strcat(char *, const char *); +char *strcpy(char *, const char *); +char *strdup(const char *); +size_t strlen(const char *); +char *strncpy(char *, const char *, size_t); + +#endif /* __STRING_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/sys/fcntl.h b/projects/compiler-rt/SDKs/linux/usr/include/sys/fcntl.h new file mode 100644 index 00000000..1512bf9b --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/sys/fcntl.h @@ -0,0 +1,29 @@ +/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef _SYS_FCNTL_H_ +#define _SYS_FCNTL_H_ + +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +#define O_ACCMODE 0x0003 + +#define O_CREAT 0x0200 + +int open(const char *, int, ...); + +#endif /* _SYS_FCNTL_H_ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/sys/mman.h b/projects/compiler-rt/SDKs/linux/usr/include/sys/mman.h new file mode 100644 index 00000000..bfb7f8bb --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/sys/mman.h @@ -0,0 +1,47 @@ +/* ===-- limits.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_MMAN_H__ +#define __SYS_MMAN_H__ + +typedef __SIZE_TYPE__ size_t; + +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 + +#define MAP_SHARED 0x0001 +#define MAP_PRIVATE 0x0002 + +#define MAP_FILE 0x0000 +#define MAP_ANON 0x1000 + +#define MS_ASYNC 0x0001 +#define MS_INVALIDATE 0x0002 +#define MS_SYNC 0x0010 + +extern void *mmap(void *addr, size_t len, int prot, int flags, int fd, + long long offset) + __attribute__((__nothrow__)); +extern int munmap(void *addr, size_t len) + __attribute__((__nothrow__)); +extern int msync(void *addr, size_t len, int flags) + __attribute__((__nothrow__)); +extern int mprotect (void *__addr, size_t __len, int __prot) + __attribute__((__nothrow__)); + +#endif /* __SYS_MMAN_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/sys/stat.h b/projects/compiler-rt/SDKs/linux/usr/include/sys/stat.h new file mode 100644 index 00000000..0449fddb --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/sys/stat.h @@ -0,0 +1,24 @@ +/* ===-- stat.h - stub SDK header for compiler-rt ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_STAT_H__ +#define __SYS_STAT_H__ + +typedef unsigned int mode_t; + +int mkdir(const char *, mode_t); + +#endif /* __SYS_STAT_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/sys/types.h b/projects/compiler-rt/SDKs/linux/usr/include/sys/types.h new file mode 100644 index 00000000..10e74bbd --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/sys/types.h @@ -0,0 +1,20 @@ +/* ===-- stat.h - stub SDK header for compiler-rt ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __SYS_TYPES_H__ +#define __SYS_TYPES_H__ + +#endif /* __SYS_TYPES_H__ */ diff --git a/projects/compiler-rt/SDKs/linux/usr/include/unistd.h b/projects/compiler-rt/SDKs/linux/usr/include/unistd.h new file mode 100644 index 00000000..773b081d --- /dev/null +++ b/projects/compiler-rt/SDKs/linux/usr/include/unistd.h @@ -0,0 +1,26 @@ +/* ===-- unistd.h - stub SDK header for compiler-rt -------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This is a stub SDK header file. This file is not part of the interface of + * this library nor an official version of the appropriate SDK header. It is + * intended only to stub the features of this header required by compiler-rt. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef __UNISTD_H__ +#define __UNISTD_H__ + +enum { + _SC_PAGESIZE = 30 +}; + +extern long int sysconf (int __name) __attribute__ ((__nothrow__)); + +#endif /* __UNISTD_H__ */ diff --git a/projects/compiler-rt/cmake/Modules/AddCompilerRT.cmake b/projects/compiler-rt/cmake/Modules/AddCompilerRT.cmake new file mode 100644 index 00000000..fd117ac5 --- /dev/null +++ b/projects/compiler-rt/cmake/Modules/AddCompilerRT.cmake @@ -0,0 +1,145 @@ +include(AddLLVM) +include(LLVMParseArguments) +include(CompilerRTUtils) + +# Tries to add "object library" target for a given architecture +# with name "." if architecture can be targeted. +# add_compiler_rt_object_library( +# SOURCES +# CFLAGS +# DEFS ) +macro(add_compiler_rt_object_library name arch) + if(CAN_TARGET_${arch}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) + add_library(${name}.${arch} OBJECT ${LIB_SOURCES}) + set_target_compile_flags(${name}.${arch} + ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + set_property(TARGET ${name}.${arch} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + else() + message(FATAL_ERROR "Archtecture ${arch} can't be targeted") + endif() +endmacro() + +# Same as above, but adds universal osx library for either OSX or iOS simulator +# with name "." targeting multiple architectures. +# add_compiler_rt_darwin_object_library( ARCH +# SOURCES +# CFLAGS +# DEFS ) +macro(add_compiler_rt_darwin_object_library name os) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN}) + set(libname "${name}.${os}") + add_library(${libname} OBJECT ${LIB_SOURCES}) + set_target_compile_flags(${libname} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS}) + set_target_properties(${libname} PROPERTIES OSX_ARCHITECTURES "${LIB_ARCH}") + set_property(TARGET ${libname} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) +endmacro() + +# Adds static runtime for a given architecture and puts it in the proper +# directory in the build and install trees. +# add_compiler_rt_static_runtime( +# SOURCES +# CFLAGS +# DEFS ) +macro(add_compiler_rt_static_runtime name arch) + if(CAN_TARGET_${arch}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) + add_library(${name} STATIC ${LIB_SOURCES}) + # Setup compile flags and definitions. + set_target_compile_flags(${name} + ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + set_property(TARGET ${name} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + # Setup correct output directory in the build tree. + set_target_properties(${name} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + # Add installation command. + install(TARGETS ${name} + ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + add_dependencies(compiler-rt ${name}) + else() + message(FATAL_ERROR "Archtecture ${arch} can't be targeted") + endif() +endmacro() + +# Same as add_compiler_rt_static_runtime, but creates a universal library +# for several architectures. +# add_compiler_rt_osx_static_runtime( ARCH +# SOURCES +# CFLAGS +# DEFS ) +macro(add_compiler_rt_osx_static_runtime name) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN}) + add_library(${name} STATIC ${LIB_SOURCES}) + set_target_compile_flags(${name} ${LIB_CFLAGS}) + set_property(TARGET ${name} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + set_target_properties(${name} PROPERTIES + OSX_ARCHITECTURES "${LIB_ARCH}" + ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + install(TARGETS ${name} + ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + add_dependencies(compiler-rt ${name}) +endmacro() + +# Adds dynamic runtime library on osx/iossim, which supports multiple +# architectures. +# add_compiler_rt_darwin_dynamic_runtime( +# ARCH +# SOURCES +# CFLAGS +# DEFS +# LINKFLAGS ) +macro(add_compiler_rt_darwin_dynamic_runtime name os) + parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS;LINKFLAGS" "" ${ARGN}) + add_library(${name} SHARED ${LIB_SOURCES}) + set_target_compile_flags(${name} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS}) + set_target_link_flags(${name} ${LIB_LINKFLAGS} ${DARWIN_${os}_LINKFLAGS}) + set_property(TARGET ${name} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + set_target_properties(${name} PROPERTIES + OSX_ARCHITECTURES "${LIB_ARCH}" + LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + install(TARGETS ${name} + LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + add_dependencies(compiler-rt ${name}) +endmacro() + +# Unittests support. +set(COMPILER_RT_GTEST_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest) +set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/src/gtest-all.cc) +set(COMPILER_RT_GTEST_INCLUDE_CFLAGS + -DGTEST_NO_LLVM_RAW_OSTREAM=1 + -I${COMPILER_RT_GTEST_PATH}/include + -I${COMPILER_RT_GTEST_PATH} +) + +# Use Clang to link objects into a single executable with just-built +# Clang, using specific link flags. Make executable a part of provided +# test_suite. +# add_compiler_rt_test( +# OBJECTS +# DEPS +# LINK_FLAGS ) +macro(add_compiler_rt_test test_suite test_name) + parse_arguments(TEST "OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) + set(output_bin "${CMAKE_CURRENT_BINARY_DIR}/${test_name}") + add_custom_target(${test_name} + COMMAND clang ${TEST_OBJECTS} -o "${output_bin}" + ${TEST_LINK_FLAGS} + DEPENDS clang ${TEST_DEPS}) + # Make the test suite depend on the binary. + add_dependencies(${test_suite} ${test_name}) +endmacro() + +macro(add_compiler_rt_resource_file target_name file_name) + set(src_file "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}") + set(dst_file "${CLANG_RESOURCE_DIR}/${file_name}") + add_custom_target(${target_name} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src_file} ${dst_file} + DEPENDS ${file_name}) + # Install in Clang resource directory. + install(FILES ${file_name} DESTINATION ${LIBCLANG_INSTALL_PATH}) +endmacro() diff --git a/projects/compiler-rt/cmake/Modules/CompilerRTCompile.cmake b/projects/compiler-rt/cmake/Modules/CompilerRTCompile.cmake new file mode 100644 index 00000000..2794cabe --- /dev/null +++ b/projects/compiler-rt/cmake/Modules/CompilerRTCompile.cmake @@ -0,0 +1,16 @@ +include(LLVMParseArguments) + +# Compile a source into an object file with just-built Clang using +# a provided compile flags and dependenices. +# clang_compile( +# CFLAGS +# DEPS ) +macro(clang_compile object_file source) + parse_arguments(SOURCE "CFLAGS;DEPS" "" ${ARGN}) + get_filename_component(source_rpath ${source} REALPATH) + add_custom_command( + OUTPUT ${object_file} + COMMAND clang ${SOURCE_CFLAGS} -c -o "${object_file}" ${source_rpath} + MAIN_DEPENDENCY ${source} + DEPENDS clang ${SOURCE_DEPS}) +endmacro() diff --git a/projects/compiler-rt/cmake/Modules/CompilerRTLink.cmake b/projects/compiler-rt/cmake/Modules/CompilerRTLink.cmake new file mode 100644 index 00000000..85030a72 --- /dev/null +++ b/projects/compiler-rt/cmake/Modules/CompilerRTLink.cmake @@ -0,0 +1,14 @@ +include(LLVMParseArguments) + +# Link a shared library with just-built Clang. +# clang_link_shared( +# OBJECTS +# LINKFLAGS +# DEPS ) +macro(clang_link_shared so_file) + parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN}) + add_custom_command( + OUTPUT ${so_file} + COMMAND clang -o "${so_file}" -shared ${SOURCE_LINKFLAGS} ${SOURCE_OBJECTS} + DEPENDS clang ${SOURCE_DEPS}) +endmacro() diff --git a/projects/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/projects/compiler-rt/cmake/Modules/CompilerRTUtils.cmake new file mode 100644 index 00000000..fce37e3e --- /dev/null +++ b/projects/compiler-rt/cmake/Modules/CompilerRTUtils.cmake @@ -0,0 +1,38 @@ +# Because compiler-rt spends a lot of time setting up custom compile flags, +# define a handy helper function for it. The compile flags setting in CMake +# has serious issues that make its syntax challenging at best. +function(set_target_compile_flags target) + foreach(arg ${ARGN}) + set(argstring "${argstring} ${arg}") + endforeach() + set_property(TARGET ${target} PROPERTY COMPILE_FLAGS "${argstring}") +endfunction() + +function(set_target_link_flags target) + foreach(arg ${ARGN}) + set(argstring "${argstring} ${arg}") + endforeach() + set_property(TARGET ${target} PROPERTY LINK_FLAGS "${argstring}") +endfunction() + +# Check if a given flag is present in a space-separated flag_string. +# Store the result in out_var. +function(find_flag_in_string flag_string flag out_var) + string(REPLACE " " ";" flag_list ${flag_string}) + list(FIND flag_list ${flag} flag_pos) + if(NOT flag_pos EQUAL -1) + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Set the variable var_PYBOOL to True if var holds a true-ish string, +# otherwise set it to False. +macro(pythonize_bool var) + if (${var}) + set(${var}_PYBOOL True) + else() + set(${var}_PYBOOL False) + endif() +endmacro() diff --git a/projects/compiler-rt/cmake/Modules/SanitizerUtils.cmake b/projects/compiler-rt/cmake/Modules/SanitizerUtils.cmake new file mode 100644 index 00000000..0836edee --- /dev/null +++ b/projects/compiler-rt/cmake/Modules/SanitizerUtils.cmake @@ -0,0 +1,42 @@ +include(LLVMParseArguments) + +set(SANITIZER_GEN_DYNAMIC_LIST + ${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/scripts/gen_dynamic_list.py) + +set(SANITIZER_LINT_SCRIPT + ${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/scripts/check_lint.sh) + +# Create a target "-symbols" that would generate the list of symbols +# that need to be exported from sanitizer runtime "". Function +# interceptors are exported automatically, user can also provide files with +# symbol names that should be exported as well. +# add_sanitizer_rt_symbols( ) +macro(add_sanitizer_rt_symbols name) + get_target_property(libfile ${name} LOCATION) + set(symsfile "${libfile}.syms") + add_custom_command(OUTPUT ${symsfile} + COMMAND ${PYTHON_EXECUTABLE} + ${SANITIZER_GEN_DYNAMIC_LIST} ${libfile} ${ARGN} + > ${symsfile} + DEPENDS ${name} ${SANITIZER_GEN_DYNAMIC_LIST} ${ARGN} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating exported symbols for ${name}" + VERBATIM) + add_custom_target(${name}-symbols ALL + DEPENDS ${symsfile} + SOURCES ${SANITIZER_GEN_DYNAMIC_LIST} ${ARGN}) + install(FILES ${symsfile} DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + add_dependencies(compiler-rt ${name}-symbols) +endmacro() + +# Add target to check code style for sanitizer runtimes. +if(UNIX) + add_custom_target(SanitizerLintCheck + COMMAND LLVM_CHECKOUT=${LLVM_MAIN_SRC_DIR} SILENT=1 TMPDIR= + PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} + ${SANITIZER_LINT_SCRIPT} + DEPENDS ${SANITIZER_LINT_SCRIPT} + COMMENT "Running lint check for sanitizer sources..." + VERBATIM) +endif() + diff --git a/projects/compiler-rt/include/CMakeLists.txt b/projects/compiler-rt/include/CMakeLists.txt new file mode 100644 index 00000000..d8a73872 --- /dev/null +++ b/projects/compiler-rt/include/CMakeLists.txt @@ -0,0 +1,42 @@ +set(SANITIZER_HEADERS + sanitizer/asan_interface.h + sanitizer/common_interface_defs.h + sanitizer/dfsan_interface.h + sanitizer/linux_syscall_hooks.h + sanitizer/lsan_interface.h + sanitizer/msan_interface.h) + +set(output_dir ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/include) + +if(MSVC_IDE OR XCODE) + set(other_output_dir ${LLVM_BINARY_DIR}/bin/lib/clang/${CLANG_VERSION}/include) +endif() + +# Copy compiler-rt headers to the build tree. +set(out_files) +foreach( f ${SANITIZER_HEADERS} ) + set( src ${CMAKE_CURRENT_SOURCE_DIR}/${f} ) + set( dst ${output_dir}/${f} ) + add_custom_command(OUTPUT ${dst} + DEPENDS ${src} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst} + COMMENT "Copying compiler-rt's ${f}...") + list(APPEND out_files ${dst}) + + if(other_output_dir) + set(other_dst ${other_output_dir}/${f}) + add_custom_command(OUTPUT ${other_dst} + DEPENDS ${src} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${other_dst} + COMMENT "Copying compiler-rt's ${f}...") + list(APPEND out_files ${other_dst}) + endif() +endforeach( f ) + +add_custom_target(compiler-rt-headers ALL DEPENDS ${out_files}) +add_dependencies(compiler-rt compiler-rt-headers) + +# Install sanitizer headers. +install(FILES ${SANITIZER_HEADERS} + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DESTINATION ${LIBCLANG_INSTALL_PATH}/include/sanitizer) diff --git a/projects/compiler-rt/include/sanitizer/asan_interface.h b/projects/compiler-rt/include/sanitizer/asan_interface.h new file mode 100644 index 00000000..8adf3f17 --- /dev/null +++ b/projects/compiler-rt/include/sanitizer/asan_interface.h @@ -0,0 +1,137 @@ +//===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_ASAN_INTERFACE_H +#define SANITIZER_ASAN_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + // Marks memory region [addr, addr+size) as unaddressable. + // This memory must be previously allocated by the user program. Accessing + // addresses in this region from instrumented code is forbidden until + // this region is unpoisoned. This function is not guaranteed to poison + // the whole region - it may poison only subregion of [addr, addr+size) due + // to ASan alignment restrictions. + // Method is NOT thread-safe in the sense that no two threads can + // (un)poison memory in the same memory region simultaneously. + void __asan_poison_memory_region(void const volatile *addr, size_t size); + // Marks memory region [addr, addr+size) as addressable. + // This memory must be previously allocated by the user program. Accessing + // addresses in this region is allowed until this region is poisoned again. + // This function may unpoison a superregion of [addr, addr+size) due to + // ASan alignment restrictions. + // Method is NOT thread-safe in the sense that no two threads can + // (un)poison memory in the same memory region simultaneously. + void __asan_unpoison_memory_region(void const volatile *addr, size_t size); + +// User code should use macros instead of functions. +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +#define ASAN_POISON_MEMORY_REGION(addr, size) \ + __asan_poison_memory_region((addr), (size)) +#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ + __asan_unpoison_memory_region((addr), (size)) +#else +#define ASAN_POISON_MEMORY_REGION(addr, size) \ + ((void)(addr), (void)(size)) +#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ + ((void)(addr), (void)(size)) +#endif + + // Returns true iff addr is poisoned (i.e. 1-byte read/write access to this + // address will result in error report from AddressSanitizer). + bool __asan_address_is_poisoned(void const volatile *addr); + + // If at least on byte in [beg, beg+size) is poisoned, return the address + // of the first such byte. Otherwise return 0. + void *__asan_region_is_poisoned(void *beg, size_t size); + + // Print the description of addr (useful when debugging in gdb). + void __asan_describe_address(void *addr); + + // This is an internal function that is called to report an error. + // However it is still a part of the interface because users may want to + // set a breakpoint on this function in a debugger. + void __asan_report_error(void *pc, void *bp, void *sp, + void *addr, bool is_write, size_t access_size); + + // Sets the exit code to use when reporting an error. + // Returns the old value. + int __asan_set_error_exit_code(int exit_code); + + // Sets the callback to be called right before death on error. + // Passing 0 will unset the callback. + void __asan_set_death_callback(void (*callback)(void)); + + void __asan_set_error_report_callback(void (*callback)(const char*)); + + // User may provide function that would be called right when ASan detects + // an error. This can be used to notice cases when ASan detects an error, but + // the program crashes before ASan report is printed. + void __asan_on_error(); + + // User may provide its own implementation for symbolization function. + // It should print the description of instruction at address "pc" to + // "out_buffer". Description should be at most "out_size" bytes long. + // User-specified function should return true if symbolization was + // successful. + bool __asan_symbolize(const void *pc, char *out_buffer, + int out_size); + + // Returns the estimated number of bytes that will be reserved by allocator + // for request of "size" bytes. If ASan allocator can't allocate that much + // memory, returns the maximal possible allocation size, otherwise returns + // "size". + size_t __asan_get_estimated_allocated_size(size_t size); + // Returns true if p was returned by the ASan allocator and + // is not yet freed. + bool __asan_get_ownership(const void *p); + // Returns the number of bytes reserved for the pointer p. + // Requires (get_ownership(p) == true) or (p == 0). + size_t __asan_get_allocated_size(const void *p); + // Number of bytes, allocated and not yet freed by the application. + size_t __asan_get_current_allocated_bytes(); + // Number of bytes, mmaped by asan allocator to fulfill allocation requests. + // Generally, for request of X bytes, allocator can reserve and add to free + // lists a large number of chunks of size X to use them for future requests. + // All these chunks count toward the heap size. Currently, allocator never + // releases memory to OS (instead, it just puts freed chunks to free lists). + size_t __asan_get_heap_size(); + // Number of bytes, mmaped by asan allocator, which can be used to fulfill + // allocation requests. When a user program frees memory chunk, it can first + // fall into quarantine and will count toward __asan_get_free_bytes() later. + size_t __asan_get_free_bytes(); + // Number of bytes in unmapped pages, that are released to OS. Currently, + // always returns 0. + size_t __asan_get_unmapped_bytes(); + // Prints accumulated stats to stderr. Used for debugging. + void __asan_print_accumulated_stats(); + + // This function may be optionally provided by user and should return + // a string containing ASan runtime options. See asan_flags.h for details. + const char* __asan_default_options(); + + // Malloc hooks that may be optionally provided by user. + // __asan_malloc_hook(ptr, size) is called immediately after + // allocation of "size" bytes, which returned "ptr". + // __asan_free_hook(ptr) is called immediately before + // deallocation of "ptr". + void __asan_malloc_hook(void *ptr, size_t size); + void __asan_free_hook(void *ptr); +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_ASAN_INTERFACE_H diff --git a/projects/compiler-rt/include/sanitizer/common_interface_defs.h b/projects/compiler-rt/include/sanitizer/common_interface_defs.h new file mode 100644 index 00000000..4cc2aeae --- /dev/null +++ b/projects/compiler-rt/include/sanitizer/common_interface_defs.h @@ -0,0 +1,81 @@ +//===-- sanitizer/common_interface_defs.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Common part of the public sanitizer interface. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_COMMON_INTERFACE_DEFS_H +#define SANITIZER_COMMON_INTERFACE_DEFS_H + +#include +#include + +// GCC does not understand __has_feature. +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + // Tell the tools to write their reports to "path." instead of stderr. + void __sanitizer_set_report_path(const char *path); + + // Notify the tools that the sandbox is going to be turned on. The reserved + // parameter will be used in the future to hold a structure with functions + // that the tools may call to bypass the sandbox. + void __sanitizer_sandbox_on_notify(void *reserved); + + // This function is called by the tool when it has just finished reporting + // an error. 'error_summary' is a one-line string that summarizes + // the error message. This function can be overridden by the client. + void __sanitizer_report_error_summary(const char *error_summary); + + // Some of the sanitizers (e.g. asan/tsan) may miss bugs that happen + // in unaligned loads/stores. In order to find such bugs reliably one needs + // to replace plain unaligned loads/stores with these calls. + uint16_t __sanitizer_unaligned_load16(const void *p); + uint32_t __sanitizer_unaligned_load32(const void *p); + uint64_t __sanitizer_unaligned_load64(const void *p); + void __sanitizer_unaligned_store16(void *p, uint16_t x); + void __sanitizer_unaligned_store32(void *p, uint32_t x); + void __sanitizer_unaligned_store64(void *p, uint64_t x); + + // Record and dump coverage info. + void __sanitizer_cov_dump(); + + // Annotate the current state of a contiguous container, such as + // std::vector, std::string or similar. + // A contiguous container is a container that keeps all of its elements + // in a contiguous region of memory. The container owns the region of memory + // [beg, end); the memory [beg, mid) is used to store the current elements + // and the memory [mid, end) is reserved for future elements; + // end <= mid <= end. For example, in "std::vector<> v" + // beg = &v[0]; + // end = beg + v.capacity() * sizeof(v[0]); + // mid = beg + v.size() * sizeof(v[0]); + // + // This annotation tells the Sanitizer tool about the current state of the + // container so that the tool can report errors when memory from [mid, end) + // is accessed. Insert this annotation into methods like push_back/pop_back. + // Supply the old and the new values of mid (old_mid/new_mid). + // In the initial state mid == end and so should be the final + // state when the container is destroyed or when it reallocates the storage. + // + // Use with caution and don't use for anything other than vector-like classes. + // + // For AddressSanitizer, 'beg' should be 8-aligned. + void __sanitizer_annotate_contiguous_container(void *beg, void *end, + void *old_mid, void *new_mid); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_COMMON_INTERFACE_DEFS_H diff --git a/projects/compiler-rt/include/sanitizer/dfsan_interface.h b/projects/compiler-rt/include/sanitizer/dfsan_interface.h new file mode 100644 index 00000000..f14d45a2 --- /dev/null +++ b/projects/compiler-rt/include/sanitizer/dfsan_interface.h @@ -0,0 +1,87 @@ +//===-- dfsan_interface.h -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef DFSAN_INTERFACE_H +#define DFSAN_INTERFACE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t dfsan_label; + +/// Stores information associated with a specific label identifier. A label +/// may be a base label created using dfsan_create_label, with associated +/// text description and user data, or an automatically created union label, +/// which represents the union of two label identifiers (which may themselves +/// be base or union labels). +struct dfsan_label_info { + // Fields for union labels, set to 0 for base labels. + dfsan_label l1; + dfsan_label l2; + + // Fields for base labels. + const char *desc; + void *userdata; +}; + +/// Computes the union of \c l1 and \c l2, possibly creating a union label in +/// the process. +dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2); + +/// Creates and returns a base label with the given description and user data. +dfsan_label dfsan_create_label(const char *desc, void *userdata); + +/// Sets the label for each address in [addr,addr+size) to \c label. +void dfsan_set_label(dfsan_label label, void *addr, size_t size); + +/// Sets the label for each address in [addr,addr+size) to the union of the +/// current label for that address and \c label. +void dfsan_add_label(dfsan_label label, void *addr, size_t size); + +/// Retrieves the label associated with the given data. +/// +/// The type of 'data' is arbitrary. The function accepts a value of any type, +/// which can be truncated or extended (implicitly or explicitly) as necessary. +/// The truncation/extension operations will preserve the label of the original +/// value. +dfsan_label dfsan_get_label(long data); + +/// Retrieves the label associated with the data at the given address. +dfsan_label dfsan_read_label(const void *addr, size_t size); + +/// Retrieves a pointer to the dfsan_label_info struct for the given label. +const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label); + +/// Returns whether the given label label contains the label elem. +int dfsan_has_label(dfsan_label label, dfsan_label elem); + +/// If the given label label contains a label with the description desc, returns +/// that label, else returns 0. +dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc); + +#ifdef __cplusplus +} // extern "C" + +template +void dfsan_set_label(dfsan_label label, T &data) { // NOLINT + dfsan_set_label(label, (void *)&data, sizeof(T)); +} + +#endif + +#endif // DFSAN_INTERFACE_H diff --git a/projects/compiler-rt/include/sanitizer/linux_syscall_hooks.h b/projects/compiler-rt/include/sanitizer/linux_syscall_hooks.h new file mode 100644 index 00000000..89867c15 --- /dev/null +++ b/projects/compiler-rt/include/sanitizer/linux_syscall_hooks.h @@ -0,0 +1,3070 @@ +//===-- linux_syscall_hooks.h ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of public sanitizer interface. +// +// System call handlers. +// +// Interface methods declared in this header implement pre- and post- syscall +// actions for the active sanitizer. +// Usage: +// __sanitizer_syscall_pre_getfoo(...args...); +// long res = syscall(__NR_getfoo, ...args...); +// __sanitizer_syscall_post_getfoo(res, ...args...); +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LINUX_SYSCALL_HOOKS_H +#define SANITIZER_LINUX_SYSCALL_HOOKS_H + +#define __sanitizer_syscall_pre_time(tloc) \ + __sanitizer_syscall_pre_impl_time((long)(tloc)) +#define __sanitizer_syscall_post_time(res, tloc) \ + __sanitizer_syscall_post_impl_time(res, (long)(tloc)) +#define __sanitizer_syscall_pre_stime(tptr) \ + __sanitizer_syscall_pre_impl_stime((long)(tptr)) +#define __sanitizer_syscall_post_stime(res, tptr) \ + __sanitizer_syscall_post_impl_stime(res, (long)(tptr)) +#define __sanitizer_syscall_pre_gettimeofday(tv, tz) \ + __sanitizer_syscall_pre_impl_gettimeofday((long)(tv), (long)(tz)) +#define __sanitizer_syscall_post_gettimeofday(res, tv, tz) \ + __sanitizer_syscall_post_impl_gettimeofday(res, (long)(tv), (long)(tz)) +#define __sanitizer_syscall_pre_settimeofday(tv, tz) \ + __sanitizer_syscall_pre_impl_settimeofday((long)(tv), (long)(tz)) +#define __sanitizer_syscall_post_settimeofday(res, tv, tz) \ + __sanitizer_syscall_post_impl_settimeofday(res, (long)(tv), (long)(tz)) +#define __sanitizer_syscall_pre_adjtimex(txc_p) \ + __sanitizer_syscall_pre_impl_adjtimex((long)(txc_p)) +#define __sanitizer_syscall_post_adjtimex(res, txc_p) \ + __sanitizer_syscall_post_impl_adjtimex(res, (long)(txc_p)) +#define __sanitizer_syscall_pre_times(tbuf) \ + __sanitizer_syscall_pre_impl_times((long)(tbuf)) +#define __sanitizer_syscall_post_times(res, tbuf) \ + __sanitizer_syscall_post_impl_times(res, (long)(tbuf)) +#define __sanitizer_syscall_pre_gettid() __sanitizer_syscall_pre_impl_gettid() +#define __sanitizer_syscall_post_gettid(res) \ + __sanitizer_syscall_post_impl_gettid(res) +#define __sanitizer_syscall_pre_nanosleep(rqtp, rmtp) \ + __sanitizer_syscall_pre_impl_nanosleep((long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_post_nanosleep(res, rqtp, rmtp) \ + __sanitizer_syscall_post_impl_nanosleep(res, (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_pre_alarm(seconds) \ + __sanitizer_syscall_pre_impl_alarm((long)(seconds)) +#define __sanitizer_syscall_post_alarm(res, seconds) \ + __sanitizer_syscall_post_impl_alarm(res, (long)(seconds)) +#define __sanitizer_syscall_pre_getpid() __sanitizer_syscall_pre_impl_getpid() +#define __sanitizer_syscall_post_getpid(res) \ + __sanitizer_syscall_post_impl_getpid(res) +#define __sanitizer_syscall_pre_getppid() __sanitizer_syscall_pre_impl_getppid() +#define __sanitizer_syscall_post_getppid(res) \ + __sanitizer_syscall_post_impl_getppid(res) +#define __sanitizer_syscall_pre_getuid() __sanitizer_syscall_pre_impl_getuid() +#define __sanitizer_syscall_post_getuid(res) \ + __sanitizer_syscall_post_impl_getuid(res) +#define __sanitizer_syscall_pre_geteuid() __sanitizer_syscall_pre_impl_geteuid() +#define __sanitizer_syscall_post_geteuid(res) \ + __sanitizer_syscall_post_impl_geteuid(res) +#define __sanitizer_syscall_pre_getgid() __sanitizer_syscall_pre_impl_getgid() +#define __sanitizer_syscall_post_getgid(res) \ + __sanitizer_syscall_post_impl_getgid(res) +#define __sanitizer_syscall_pre_getegid() __sanitizer_syscall_pre_impl_getegid() +#define __sanitizer_syscall_post_getegid(res) \ + __sanitizer_syscall_post_impl_getegid(res) +#define __sanitizer_syscall_pre_getresuid(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_getresuid((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_getresuid(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_getresuid(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_getresgid(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_getresgid((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_getresgid(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_getresgid(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_getpgid(pid) \ + __sanitizer_syscall_pre_impl_getpgid((long)(pid)) +#define __sanitizer_syscall_post_getpgid(res, pid) \ + __sanitizer_syscall_post_impl_getpgid(res, (long)(pid)) +#define __sanitizer_syscall_pre_getpgrp() __sanitizer_syscall_pre_impl_getpgrp() +#define __sanitizer_syscall_post_getpgrp(res) \ + __sanitizer_syscall_post_impl_getpgrp(res) +#define __sanitizer_syscall_pre_getsid(pid) \ + __sanitizer_syscall_pre_impl_getsid((long)(pid)) +#define __sanitizer_syscall_post_getsid(res, pid) \ + __sanitizer_syscall_post_impl_getsid(res, (long)(pid)) +#define __sanitizer_syscall_pre_getgroups(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_getgroups((long)(gidsetsize), (long)(grouplist)) +#define __sanitizer_syscall_post_getgroups(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_getgroups(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_setregid(rgid, egid) \ + __sanitizer_syscall_pre_impl_setregid((long)(rgid), (long)(egid)) +#define __sanitizer_syscall_post_setregid(res, rgid, egid) \ + __sanitizer_syscall_post_impl_setregid(res, (long)(rgid), (long)(egid)) +#define __sanitizer_syscall_pre_setgid(gid) \ + __sanitizer_syscall_pre_impl_setgid((long)(gid)) +#define __sanitizer_syscall_post_setgid(res, gid) \ + __sanitizer_syscall_post_impl_setgid(res, (long)(gid)) +#define __sanitizer_syscall_pre_setreuid(ruid, euid) \ + __sanitizer_syscall_pre_impl_setreuid((long)(ruid), (long)(euid)) +#define __sanitizer_syscall_post_setreuid(res, ruid, euid) \ + __sanitizer_syscall_post_impl_setreuid(res, (long)(ruid), (long)(euid)) +#define __sanitizer_syscall_pre_setuid(uid) \ + __sanitizer_syscall_pre_impl_setuid((long)(uid)) +#define __sanitizer_syscall_post_setuid(res, uid) \ + __sanitizer_syscall_post_impl_setuid(res, (long)(uid)) +#define __sanitizer_syscall_pre_setresuid(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_setresuid((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_setresuid(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_setresuid(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_setresgid(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_setresgid((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_setresgid(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_setresgid(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_setfsuid(uid) \ + __sanitizer_syscall_pre_impl_setfsuid((long)(uid)) +#define __sanitizer_syscall_post_setfsuid(res, uid) \ + __sanitizer_syscall_post_impl_setfsuid(res, (long)(uid)) +#define __sanitizer_syscall_pre_setfsgid(gid) \ + __sanitizer_syscall_pre_impl_setfsgid((long)(gid)) +#define __sanitizer_syscall_post_setfsgid(res, gid) \ + __sanitizer_syscall_post_impl_setfsgid(res, (long)(gid)) +#define __sanitizer_syscall_pre_setpgid(pid, pgid) \ + __sanitizer_syscall_pre_impl_setpgid((long)(pid), (long)(pgid)) +#define __sanitizer_syscall_post_setpgid(res, pid, pgid) \ + __sanitizer_syscall_post_impl_setpgid(res, (long)(pid), (long)(pgid)) +#define __sanitizer_syscall_pre_setsid() __sanitizer_syscall_pre_impl_setsid() +#define __sanitizer_syscall_post_setsid(res) \ + __sanitizer_syscall_post_impl_setsid(res) +#define __sanitizer_syscall_pre_setgroups(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_setgroups((long)(gidsetsize), (long)(grouplist)) +#define __sanitizer_syscall_post_setgroups(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_setgroups(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_acct(name) \ + __sanitizer_syscall_pre_impl_acct((long)(name)) +#define __sanitizer_syscall_post_acct(res, name) \ + __sanitizer_syscall_post_impl_acct(res, (long)(name)) +#define __sanitizer_syscall_pre_capget(header, dataptr) \ + __sanitizer_syscall_pre_impl_capget((long)(header), (long)(dataptr)) +#define __sanitizer_syscall_post_capget(res, header, dataptr) \ + __sanitizer_syscall_post_impl_capget(res, (long)(header), (long)(dataptr)) +#define __sanitizer_syscall_pre_capset(header, data) \ + __sanitizer_syscall_pre_impl_capset((long)(header), (long)(data)) +#define __sanitizer_syscall_post_capset(res, header, data) \ + __sanitizer_syscall_post_impl_capset(res, (long)(header), (long)(data)) +#define __sanitizer_syscall_pre_personality(personality) \ + __sanitizer_syscall_pre_impl_personality((long)(personality)) +#define __sanitizer_syscall_post_personality(res, personality) \ + __sanitizer_syscall_post_impl_personality(res, (long)(personality)) +#define __sanitizer_syscall_pre_sigpending(set) \ + __sanitizer_syscall_pre_impl_sigpending((long)(set)) +#define __sanitizer_syscall_post_sigpending(res, set) \ + __sanitizer_syscall_post_impl_sigpending(res, (long)(set)) +#define __sanitizer_syscall_pre_sigprocmask(how, set, oset) \ + __sanitizer_syscall_pre_impl_sigprocmask((long)(how), (long)(set), \ + (long)(oset)) +#define __sanitizer_syscall_post_sigprocmask(res, how, set, oset) \ + __sanitizer_syscall_post_impl_sigprocmask(res, (long)(how), (long)(set), \ + (long)(oset)) +#define __sanitizer_syscall_pre_getitimer(which, value) \ + __sanitizer_syscall_pre_impl_getitimer((long)(which), (long)(value)) +#define __sanitizer_syscall_post_getitimer(res, which, value) \ + __sanitizer_syscall_post_impl_getitimer(res, (long)(which), (long)(value)) +#define __sanitizer_syscall_pre_setitimer(which, value, ovalue) \ + __sanitizer_syscall_pre_impl_setitimer((long)(which), (long)(value), \ + (long)(ovalue)) +#define __sanitizer_syscall_post_setitimer(res, which, value, ovalue) \ + __sanitizer_syscall_post_impl_setitimer(res, (long)(which), (long)(value), \ + (long)(ovalue)) +#define __sanitizer_syscall_pre_timer_create(which_clock, timer_event_spec, \ + created_timer_id) \ + __sanitizer_syscall_pre_impl_timer_create( \ + (long)(which_clock), (long)(timer_event_spec), (long)(created_timer_id)) +#define __sanitizer_syscall_post_timer_create( \ + res, which_clock, timer_event_spec, created_timer_id) \ + __sanitizer_syscall_post_impl_timer_create(res, (long)(which_clock), \ + (long)(timer_event_spec), \ + (long)(created_timer_id)) +#define __sanitizer_syscall_pre_timer_gettime(timer_id, setting) \ + __sanitizer_syscall_pre_impl_timer_gettime((long)(timer_id), (long)(setting)) +#define __sanitizer_syscall_post_timer_gettime(res, timer_id, setting) \ + __sanitizer_syscall_post_impl_timer_gettime(res, (long)(timer_id), \ + (long)(setting)) +#define __sanitizer_syscall_pre_timer_getoverrun(timer_id) \ + __sanitizer_syscall_pre_impl_timer_getoverrun((long)(timer_id)) +#define __sanitizer_syscall_post_timer_getoverrun(res, timer_id) \ + __sanitizer_syscall_post_impl_timer_getoverrun(res, (long)(timer_id)) +#define __sanitizer_syscall_pre_timer_settime(timer_id, flags, new_setting, \ + old_setting) \ + __sanitizer_syscall_pre_impl_timer_settime((long)(timer_id), (long)(flags), \ + (long)(new_setting), \ + (long)(old_setting)) +#define __sanitizer_syscall_post_timer_settime(res, timer_id, flags, \ + new_setting, old_setting) \ + __sanitizer_syscall_post_impl_timer_settime( \ + res, (long)(timer_id), (long)(flags), (long)(new_setting), \ + (long)(old_setting)) +#define __sanitizer_syscall_pre_timer_delete(timer_id) \ + __sanitizer_syscall_pre_impl_timer_delete((long)(timer_id)) +#define __sanitizer_syscall_post_timer_delete(res, timer_id) \ + __sanitizer_syscall_post_impl_timer_delete(res, (long)(timer_id)) +#define __sanitizer_syscall_pre_clock_settime(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_settime((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_settime(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_settime(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_gettime(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_gettime((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_gettime(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_gettime(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_adjtime(which_clock, tx) \ + __sanitizer_syscall_pre_impl_clock_adjtime((long)(which_clock), (long)(tx)) +#define __sanitizer_syscall_post_clock_adjtime(res, which_clock, tx) \ + __sanitizer_syscall_post_impl_clock_adjtime(res, (long)(which_clock), \ + (long)(tx)) +#define __sanitizer_syscall_pre_clock_getres(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_getres((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_getres(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_getres(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_nanosleep(which_clock, flags, rqtp, \ + rmtp) \ + __sanitizer_syscall_pre_impl_clock_nanosleep( \ + (long)(which_clock), (long)(flags), (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_post_clock_nanosleep(res, which_clock, flags, \ + rqtp, rmtp) \ + __sanitizer_syscall_post_impl_clock_nanosleep( \ + res, (long)(which_clock), (long)(flags), (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_pre_nice(increment) \ + __sanitizer_syscall_pre_impl_nice((long)(increment)) +#define __sanitizer_syscall_post_nice(res, increment) \ + __sanitizer_syscall_post_impl_nice(res, (long)(increment)) +#define __sanitizer_syscall_pre_sched_setscheduler(pid, policy, param) \ + __sanitizer_syscall_pre_impl_sched_setscheduler((long)(pid), (long)(policy), \ + (long)(param)) +#define __sanitizer_syscall_post_sched_setscheduler(res, pid, policy, param) \ + __sanitizer_syscall_post_impl_sched_setscheduler( \ + res, (long)(pid), (long)(policy), (long)(param)) +#define __sanitizer_syscall_pre_sched_setparam(pid, param) \ + __sanitizer_syscall_pre_impl_sched_setparam((long)(pid), (long)(param)) +#define __sanitizer_syscall_post_sched_setparam(res, pid, param) \ + __sanitizer_syscall_post_impl_sched_setparam(res, (long)(pid), (long)(param)) +#define __sanitizer_syscall_pre_sched_getscheduler(pid) \ + __sanitizer_syscall_pre_impl_sched_getscheduler((long)(pid)) +#define __sanitizer_syscall_post_sched_getscheduler(res, pid) \ + __sanitizer_syscall_post_impl_sched_getscheduler(res, (long)(pid)) +#define __sanitizer_syscall_pre_sched_getparam(pid, param) \ + __sanitizer_syscall_pre_impl_sched_getparam((long)(pid), (long)(param)) +#define __sanitizer_syscall_post_sched_getparam(res, pid, param) \ + __sanitizer_syscall_post_impl_sched_getparam(res, (long)(pid), (long)(param)) +#define __sanitizer_syscall_pre_sched_setaffinity(pid, len, user_mask_ptr) \ + __sanitizer_syscall_pre_impl_sched_setaffinity((long)(pid), (long)(len), \ + (long)(user_mask_ptr)) +#define __sanitizer_syscall_post_sched_setaffinity(res, pid, len, \ + user_mask_ptr) \ + __sanitizer_syscall_post_impl_sched_setaffinity( \ + res, (long)(pid), (long)(len), (long)(user_mask_ptr)) +#define __sanitizer_syscall_pre_sched_getaffinity(pid, len, user_mask_ptr) \ + __sanitizer_syscall_pre_impl_sched_getaffinity((long)(pid), (long)(len), \ + (long)(user_mask_ptr)) +#define __sanitizer_syscall_post_sched_getaffinity(res, pid, len, \ + user_mask_ptr) \ + __sanitizer_syscall_post_impl_sched_getaffinity( \ + res, (long)(pid), (long)(len), (long)(user_mask_ptr)) +#define __sanitizer_syscall_pre_sched_yield() \ + __sanitizer_syscall_pre_impl_sched_yield() +#define __sanitizer_syscall_post_sched_yield(res) \ + __sanitizer_syscall_post_impl_sched_yield(res) +#define __sanitizer_syscall_pre_sched_get_priority_max(policy) \ + __sanitizer_syscall_pre_impl_sched_get_priority_max((long)(policy)) +#define __sanitizer_syscall_post_sched_get_priority_max(res, policy) \ + __sanitizer_syscall_post_impl_sched_get_priority_max(res, (long)(policy)) +#define __sanitizer_syscall_pre_sched_get_priority_min(policy) \ + __sanitizer_syscall_pre_impl_sched_get_priority_min((long)(policy)) +#define __sanitizer_syscall_post_sched_get_priority_min(res, policy) \ + __sanitizer_syscall_post_impl_sched_get_priority_min(res, (long)(policy)) +#define __sanitizer_syscall_pre_sched_rr_get_interval(pid, interval) \ + __sanitizer_syscall_pre_impl_sched_rr_get_interval((long)(pid), \ + (long)(interval)) +#define __sanitizer_syscall_post_sched_rr_get_interval(res, pid, interval) \ + __sanitizer_syscall_post_impl_sched_rr_get_interval(res, (long)(pid), \ + (long)(interval)) +#define __sanitizer_syscall_pre_setpriority(which, who, niceval) \ + __sanitizer_syscall_pre_impl_setpriority((long)(which), (long)(who), \ + (long)(niceval)) +#define __sanitizer_syscall_post_setpriority(res, which, who, niceval) \ + __sanitizer_syscall_post_impl_setpriority(res, (long)(which), (long)(who), \ + (long)(niceval)) +#define __sanitizer_syscall_pre_getpriority(which, who) \ + __sanitizer_syscall_pre_impl_getpriority((long)(which), (long)(who)) +#define __sanitizer_syscall_post_getpriority(res, which, who) \ + __sanitizer_syscall_post_impl_getpriority(res, (long)(which), (long)(who)) +#define __sanitizer_syscall_pre_shutdown(arg0, arg1) \ + __sanitizer_syscall_pre_impl_shutdown((long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_post_shutdown(res, arg0, arg1) \ + __sanitizer_syscall_post_impl_shutdown(res, (long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_pre_reboot(magic1, magic2, cmd, arg) \ + __sanitizer_syscall_pre_impl_reboot((long)(magic1), (long)(magic2), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_reboot(res, magic1, magic2, cmd, arg) \ + __sanitizer_syscall_post_impl_reboot(res, (long)(magic1), (long)(magic2), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_restart_syscall() \ + __sanitizer_syscall_pre_impl_restart_syscall() +#define __sanitizer_syscall_post_restart_syscall(res) \ + __sanitizer_syscall_post_impl_restart_syscall(res) +#define __sanitizer_syscall_pre_kexec_load(entry, nr_segments, segments, \ + flags) \ + __sanitizer_syscall_pre_impl_kexec_load((long)(entry), (long)(nr_segments), \ + (long)(segments), (long)(flags)) +#define __sanitizer_syscall_post_kexec_load(res, entry, nr_segments, segments, \ + flags) \ + __sanitizer_syscall_post_impl_kexec_load(res, (long)(entry), \ + (long)(nr_segments), \ + (long)(segments), (long)(flags)) +#define __sanitizer_syscall_pre_exit(error_code) \ + __sanitizer_syscall_pre_impl_exit((long)(error_code)) +#define __sanitizer_syscall_post_exit(res, error_code) \ + __sanitizer_syscall_post_impl_exit(res, (long)(error_code)) +#define __sanitizer_syscall_pre_exit_group(error_code) \ + __sanitizer_syscall_pre_impl_exit_group((long)(error_code)) +#define __sanitizer_syscall_post_exit_group(res, error_code) \ + __sanitizer_syscall_post_impl_exit_group(res, (long)(error_code)) +#define __sanitizer_syscall_pre_wait4(pid, stat_addr, options, ru) \ + __sanitizer_syscall_pre_impl_wait4((long)(pid), (long)(stat_addr), \ + (long)(options), (long)(ru)) +#define __sanitizer_syscall_post_wait4(res, pid, stat_addr, options, ru) \ + __sanitizer_syscall_post_impl_wait4(res, (long)(pid), (long)(stat_addr), \ + (long)(options), (long)(ru)) +#define __sanitizer_syscall_pre_waitid(which, pid, infop, options, ru) \ + __sanitizer_syscall_pre_impl_waitid( \ + (long)(which), (long)(pid), (long)(infop), (long)(options), (long)(ru)) +#define __sanitizer_syscall_post_waitid(res, which, pid, infop, options, ru) \ + __sanitizer_syscall_post_impl_waitid(res, (long)(which), (long)(pid), \ + (long)(infop), (long)(options), \ + (long)(ru)) +#define __sanitizer_syscall_pre_waitpid(pid, stat_addr, options) \ + __sanitizer_syscall_pre_impl_waitpid((long)(pid), (long)(stat_addr), \ + (long)(options)) +#define __sanitizer_syscall_post_waitpid(res, pid, stat_addr, options) \ + __sanitizer_syscall_post_impl_waitpid(res, (long)(pid), (long)(stat_addr), \ + (long)(options)) +#define __sanitizer_syscall_pre_set_tid_address(tidptr) \ + __sanitizer_syscall_pre_impl_set_tid_address((long)(tidptr)) +#define __sanitizer_syscall_post_set_tid_address(res, tidptr) \ + __sanitizer_syscall_post_impl_set_tid_address(res, (long)(tidptr)) +#define __sanitizer_syscall_pre_init_module(umod, len, uargs) \ + __sanitizer_syscall_pre_impl_init_module((long)(umod), (long)(len), \ + (long)(uargs)) +#define __sanitizer_syscall_post_init_module(res, umod, len, uargs) \ + __sanitizer_syscall_post_impl_init_module(res, (long)(umod), (long)(len), \ + (long)(uargs)) +#define __sanitizer_syscall_pre_delete_module(name_user, flags) \ + __sanitizer_syscall_pre_impl_delete_module((long)(name_user), (long)(flags)) +#define __sanitizer_syscall_post_delete_module(res, name_user, flags) \ + __sanitizer_syscall_post_impl_delete_module(res, (long)(name_user), \ + (long)(flags)) +#define __sanitizer_syscall_pre_rt_sigprocmask(how, set, oset, sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigprocmask( \ + (long)(how), (long)(set), (long)(oset), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigprocmask(res, how, set, oset, \ + sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigprocmask( \ + res, (long)(how), (long)(set), (long)(oset), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_sigpending(set, sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigpending((long)(set), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigpending(res, set, sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigpending(res, (long)(set), \ + (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_sigtimedwait(uthese, uinfo, uts, \ + sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigtimedwait( \ + (long)(uthese), (long)(uinfo), (long)(uts), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigtimedwait(res, uthese, uinfo, uts, \ + sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigtimedwait( \ + res, (long)(uthese), (long)(uinfo), (long)(uts), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_tgsigqueueinfo(tgid, pid, sig, uinfo) \ + __sanitizer_syscall_pre_impl_rt_tgsigqueueinfo((long)(tgid), (long)(pid), \ + (long)(sig), (long)(uinfo)) +#define __sanitizer_syscall_post_rt_tgsigqueueinfo(res, tgid, pid, sig, uinfo) \ + __sanitizer_syscall_post_impl_rt_tgsigqueueinfo( \ + res, (long)(tgid), (long)(pid), (long)(sig), (long)(uinfo)) +#define __sanitizer_syscall_pre_kill(pid, sig) \ + __sanitizer_syscall_pre_impl_kill((long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_kill(res, pid, sig) \ + __sanitizer_syscall_post_impl_kill(res, (long)(pid), (long)(sig)) +#define __sanitizer_syscall_pre_tgkill(tgid, pid, sig) \ + __sanitizer_syscall_pre_impl_tgkill((long)(tgid), (long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_tgkill(res, tgid, pid, sig) \ + __sanitizer_syscall_post_impl_tgkill(res, (long)(tgid), (long)(pid), \ + (long)(sig)) +#define __sanitizer_syscall_pre_tkill(pid, sig) \ + __sanitizer_syscall_pre_impl_tkill((long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_tkill(res, pid, sig) \ + __sanitizer_syscall_post_impl_tkill(res, (long)(pid), (long)(sig)) +#define __sanitizer_syscall_pre_rt_sigqueueinfo(pid, sig, uinfo) \ + __sanitizer_syscall_pre_impl_rt_sigqueueinfo((long)(pid), (long)(sig), \ + (long)(uinfo)) +#define __sanitizer_syscall_post_rt_sigqueueinfo(res, pid, sig, uinfo) \ + __sanitizer_syscall_post_impl_rt_sigqueueinfo(res, (long)(pid), (long)(sig), \ + (long)(uinfo)) +#define __sanitizer_syscall_pre_sgetmask() \ + __sanitizer_syscall_pre_impl_sgetmask() +#define __sanitizer_syscall_post_sgetmask(res) \ + __sanitizer_syscall_post_impl_sgetmask(res) +#define __sanitizer_syscall_pre_ssetmask(newmask) \ + __sanitizer_syscall_pre_impl_ssetmask((long)(newmask)) +#define __sanitizer_syscall_post_ssetmask(res, newmask) \ + __sanitizer_syscall_post_impl_ssetmask(res, (long)(newmask)) +#define __sanitizer_syscall_pre_signal(sig, handler) \ + __sanitizer_syscall_pre_impl_signal((long)(sig), (long)(handler)) +#define __sanitizer_syscall_post_signal(res, sig, handler) \ + __sanitizer_syscall_post_impl_signal(res, (long)(sig), (long)(handler)) +#define __sanitizer_syscall_pre_pause() __sanitizer_syscall_pre_impl_pause() +#define __sanitizer_syscall_post_pause(res) \ + __sanitizer_syscall_post_impl_pause(res) +#define __sanitizer_syscall_pre_sync() __sanitizer_syscall_pre_impl_sync() +#define __sanitizer_syscall_post_sync(res) \ + __sanitizer_syscall_post_impl_sync(res) +#define __sanitizer_syscall_pre_fsync(fd) \ + __sanitizer_syscall_pre_impl_fsync((long)(fd)) +#define __sanitizer_syscall_post_fsync(res, fd) \ + __sanitizer_syscall_post_impl_fsync(res, (long)(fd)) +#define __sanitizer_syscall_pre_fdatasync(fd) \ + __sanitizer_syscall_pre_impl_fdatasync((long)(fd)) +#define __sanitizer_syscall_post_fdatasync(res, fd) \ + __sanitizer_syscall_post_impl_fdatasync(res, (long)(fd)) +#define __sanitizer_syscall_pre_bdflush(func, data) \ + __sanitizer_syscall_pre_impl_bdflush((long)(func), (long)(data)) +#define __sanitizer_syscall_post_bdflush(res, func, data) \ + __sanitizer_syscall_post_impl_bdflush(res, (long)(func), (long)(data)) +#define __sanitizer_syscall_pre_mount(dev_name, dir_name, type, flags, data) \ + __sanitizer_syscall_pre_impl_mount((long)(dev_name), (long)(dir_name), \ + (long)(type), (long)(flags), \ + (long)(data)) +#define __sanitizer_syscall_post_mount(res, dev_name, dir_name, type, flags, \ + data) \ + __sanitizer_syscall_post_impl_mount(res, (long)(dev_name), (long)(dir_name), \ + (long)(type), (long)(flags), \ + (long)(data)) +#define __sanitizer_syscall_pre_umount(name, flags) \ + __sanitizer_syscall_pre_impl_umount((long)(name), (long)(flags)) +#define __sanitizer_syscall_post_umount(res, name, flags) \ + __sanitizer_syscall_post_impl_umount(res, (long)(name), (long)(flags)) +#define __sanitizer_syscall_pre_oldumount(name) \ + __sanitizer_syscall_pre_impl_oldumount((long)(name)) +#define __sanitizer_syscall_post_oldumount(res, name) \ + __sanitizer_syscall_post_impl_oldumount(res, (long)(name)) +#define __sanitizer_syscall_pre_truncate(path, length) \ + __sanitizer_syscall_pre_impl_truncate((long)(path), (long)(length)) +#define __sanitizer_syscall_post_truncate(res, path, length) \ + __sanitizer_syscall_post_impl_truncate(res, (long)(path), (long)(length)) +#define __sanitizer_syscall_pre_ftruncate(fd, length) \ + __sanitizer_syscall_pre_impl_ftruncate((long)(fd), (long)(length)) +#define __sanitizer_syscall_post_ftruncate(res, fd, length) \ + __sanitizer_syscall_post_impl_ftruncate(res, (long)(fd), (long)(length)) +#define __sanitizer_syscall_pre_stat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_stat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_stat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_stat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_statfs(path, buf) \ + __sanitizer_syscall_pre_impl_statfs((long)(path), (long)(buf)) +#define __sanitizer_syscall_post_statfs(res, path, buf) \ + __sanitizer_syscall_post_impl_statfs(res, (long)(path), (long)(buf)) +#define __sanitizer_syscall_pre_statfs64(path, sz, buf) \ + __sanitizer_syscall_pre_impl_statfs64((long)(path), (long)(sz), (long)(buf)) +#define __sanitizer_syscall_post_statfs64(res, path, sz, buf) \ + __sanitizer_syscall_post_impl_statfs64(res, (long)(path), (long)(sz), \ + (long)(buf)) +#define __sanitizer_syscall_pre_fstatfs(fd, buf) \ + __sanitizer_syscall_pre_impl_fstatfs((long)(fd), (long)(buf)) +#define __sanitizer_syscall_post_fstatfs(res, fd, buf) \ + __sanitizer_syscall_post_impl_fstatfs(res, (long)(fd), (long)(buf)) +#define __sanitizer_syscall_pre_fstatfs64(fd, sz, buf) \ + __sanitizer_syscall_pre_impl_fstatfs64((long)(fd), (long)(sz), (long)(buf)) +#define __sanitizer_syscall_post_fstatfs64(res, fd, sz, buf) \ + __sanitizer_syscall_post_impl_fstatfs64(res, (long)(fd), (long)(sz), \ + (long)(buf)) +#define __sanitizer_syscall_pre_lstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_lstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_lstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_lstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_fstat(fd, statbuf) \ + __sanitizer_syscall_pre_impl_fstat((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_fstat(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_fstat(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_newstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_newstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_newstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_newstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_newlstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_newlstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_newlstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_newlstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_newfstat(fd, statbuf) \ + __sanitizer_syscall_pre_impl_newfstat((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_newfstat(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_newfstat(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_ustat(dev, ubuf) \ + __sanitizer_syscall_pre_impl_ustat((long)(dev), (long)(ubuf)) +#define __sanitizer_syscall_post_ustat(res, dev, ubuf) \ + __sanitizer_syscall_post_impl_ustat(res, (long)(dev), (long)(ubuf)) +#define __sanitizer_syscall_pre_stat64(filename, statbuf) \ + __sanitizer_syscall_pre_impl_stat64((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_stat64(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_stat64(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_fstat64(fd, statbuf) \ + __sanitizer_syscall_pre_impl_fstat64((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_fstat64(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_fstat64(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_lstat64(filename, statbuf) \ + __sanitizer_syscall_pre_impl_lstat64((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_lstat64(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_lstat64(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_setxattr(path, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_setxattr( \ + (long)(path), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_setxattr(res, path, name, value, size, flags) \ + __sanitizer_syscall_post_impl_setxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_lsetxattr(path, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_lsetxattr( \ + (long)(path), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_lsetxattr(res, path, name, value, size, \ + flags) \ + __sanitizer_syscall_post_impl_lsetxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_fsetxattr(fd, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_fsetxattr( \ + (long)(fd), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_fsetxattr(res, fd, name, value, size, flags) \ + __sanitizer_syscall_post_impl_fsetxattr(res, (long)(fd), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_getxattr(path, name, value, size) \ + __sanitizer_syscall_pre_impl_getxattr((long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_getxattr(res, path, name, value, size) \ + __sanitizer_syscall_post_impl_getxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_lgetxattr(path, name, value, size) \ + __sanitizer_syscall_pre_impl_lgetxattr((long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_lgetxattr(res, path, name, value, size) \ + __sanitizer_syscall_post_impl_lgetxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_fgetxattr(fd, name, value, size) \ + __sanitizer_syscall_pre_impl_fgetxattr((long)(fd), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_fgetxattr(res, fd, name, value, size) \ + __sanitizer_syscall_post_impl_fgetxattr(res, (long)(fd), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_listxattr(path, list, size) \ + __sanitizer_syscall_pre_impl_listxattr((long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_listxattr(res, path, list, size) \ + __sanitizer_syscall_post_impl_listxattr(res, (long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_llistxattr(path, list, size) \ + __sanitizer_syscall_pre_impl_llistxattr((long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_llistxattr(res, path, list, size) \ + __sanitizer_syscall_post_impl_llistxattr(res, (long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_flistxattr(fd, list, size) \ + __sanitizer_syscall_pre_impl_flistxattr((long)(fd), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_flistxattr(res, fd, list, size) \ + __sanitizer_syscall_post_impl_flistxattr(res, (long)(fd), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_removexattr(path, name) \ + __sanitizer_syscall_pre_impl_removexattr((long)(path), (long)(name)) +#define __sanitizer_syscall_post_removexattr(res, path, name) \ + __sanitizer_syscall_post_impl_removexattr(res, (long)(path), (long)(name)) +#define __sanitizer_syscall_pre_lremovexattr(path, name) \ + __sanitizer_syscall_pre_impl_lremovexattr((long)(path), (long)(name)) +#define __sanitizer_syscall_post_lremovexattr(res, path, name) \ + __sanitizer_syscall_post_impl_lremovexattr(res, (long)(path), (long)(name)) +#define __sanitizer_syscall_pre_fremovexattr(fd, name) \ + __sanitizer_syscall_pre_impl_fremovexattr((long)(fd), (long)(name)) +#define __sanitizer_syscall_post_fremovexattr(res, fd, name) \ + __sanitizer_syscall_post_impl_fremovexattr(res, (long)(fd), (long)(name)) +#define __sanitizer_syscall_pre_brk(brk) \ + __sanitizer_syscall_pre_impl_brk((long)(brk)) +#define __sanitizer_syscall_post_brk(res, brk) \ + __sanitizer_syscall_post_impl_brk(res, (long)(brk)) +#define __sanitizer_syscall_pre_mprotect(start, len, prot) \ + __sanitizer_syscall_pre_impl_mprotect((long)(start), (long)(len), \ + (long)(prot)) +#define __sanitizer_syscall_post_mprotect(res, start, len, prot) \ + __sanitizer_syscall_post_impl_mprotect(res, (long)(start), (long)(len), \ + (long)(prot)) +#define __sanitizer_syscall_pre_mremap(addr, old_len, new_len, flags, \ + new_addr) \ + __sanitizer_syscall_pre_impl_mremap((long)(addr), (long)(old_len), \ + (long)(new_len), (long)(flags), \ + (long)(new_addr)) +#define __sanitizer_syscall_post_mremap(res, addr, old_len, new_len, flags, \ + new_addr) \ + __sanitizer_syscall_post_impl_mremap(res, (long)(addr), (long)(old_len), \ + (long)(new_len), (long)(flags), \ + (long)(new_addr)) +#define __sanitizer_syscall_pre_remap_file_pages(start, size, prot, pgoff, \ + flags) \ + __sanitizer_syscall_pre_impl_remap_file_pages( \ + (long)(start), (long)(size), (long)(prot), (long)(pgoff), (long)(flags)) +#define __sanitizer_syscall_post_remap_file_pages(res, start, size, prot, \ + pgoff, flags) \ + __sanitizer_syscall_post_impl_remap_file_pages(res, (long)(start), \ + (long)(size), (long)(prot), \ + (long)(pgoff), (long)(flags)) +#define __sanitizer_syscall_pre_msync(start, len, flags) \ + __sanitizer_syscall_pre_impl_msync((long)(start), (long)(len), (long)(flags)) +#define __sanitizer_syscall_post_msync(res, start, len, flags) \ + __sanitizer_syscall_post_impl_msync(res, (long)(start), (long)(len), \ + (long)(flags)) +#define __sanitizer_syscall_pre_munmap(addr, len) \ + __sanitizer_syscall_pre_impl_munmap((long)(addr), (long)(len)) +#define __sanitizer_syscall_post_munmap(res, addr, len) \ + __sanitizer_syscall_post_impl_munmap(res, (long)(addr), (long)(len)) +#define __sanitizer_syscall_pre_mlock(start, len) \ + __sanitizer_syscall_pre_impl_mlock((long)(start), (long)(len)) +#define __sanitizer_syscall_post_mlock(res, start, len) \ + __sanitizer_syscall_post_impl_mlock(res, (long)(start), (long)(len)) +#define __sanitizer_syscall_pre_munlock(start, len) \ + __sanitizer_syscall_pre_impl_munlock((long)(start), (long)(len)) +#define __sanitizer_syscall_post_munlock(res, start, len) \ + __sanitizer_syscall_post_impl_munlock(res, (long)(start), (long)(len)) +#define __sanitizer_syscall_pre_mlockall(flags) \ + __sanitizer_syscall_pre_impl_mlockall((long)(flags)) +#define __sanitizer_syscall_post_mlockall(res, flags) \ + __sanitizer_syscall_post_impl_mlockall(res, (long)(flags)) +#define __sanitizer_syscall_pre_munlockall() \ + __sanitizer_syscall_pre_impl_munlockall() +#define __sanitizer_syscall_post_munlockall(res) \ + __sanitizer_syscall_post_impl_munlockall(res) +#define __sanitizer_syscall_pre_madvise(start, len, behavior) \ + __sanitizer_syscall_pre_impl_madvise((long)(start), (long)(len), \ + (long)(behavior)) +#define __sanitizer_syscall_post_madvise(res, start, len, behavior) \ + __sanitizer_syscall_post_impl_madvise(res, (long)(start), (long)(len), \ + (long)(behavior)) +#define __sanitizer_syscall_pre_mincore(start, len, vec) \ + __sanitizer_syscall_pre_impl_mincore((long)(start), (long)(len), (long)(vec)) +#define __sanitizer_syscall_post_mincore(res, start, len, vec) \ + __sanitizer_syscall_post_impl_mincore(res, (long)(start), (long)(len), \ + (long)(vec)) +#define __sanitizer_syscall_pre_pivot_root(new_root, put_old) \ + __sanitizer_syscall_pre_impl_pivot_root((long)(new_root), (long)(put_old)) +#define __sanitizer_syscall_post_pivot_root(res, new_root, put_old) \ + __sanitizer_syscall_post_impl_pivot_root(res, (long)(new_root), \ + (long)(put_old)) +#define __sanitizer_syscall_pre_chroot(filename) \ + __sanitizer_syscall_pre_impl_chroot((long)(filename)) +#define __sanitizer_syscall_post_chroot(res, filename) \ + __sanitizer_syscall_post_impl_chroot(res, (long)(filename)) +#define __sanitizer_syscall_pre_mknod(filename, mode, dev) \ + __sanitizer_syscall_pre_impl_mknod((long)(filename), (long)(mode), \ + (long)(dev)) +#define __sanitizer_syscall_post_mknod(res, filename, mode, dev) \ + __sanitizer_syscall_post_impl_mknod(res, (long)(filename), (long)(mode), \ + (long)(dev)) +#define __sanitizer_syscall_pre_link(oldname, newname) \ + __sanitizer_syscall_pre_impl_link((long)(oldname), (long)(newname)) +#define __sanitizer_syscall_post_link(res, oldname, newname) \ + __sanitizer_syscall_post_impl_link(res, (long)(oldname), (long)(newname)) +#define __sanitizer_syscall_pre_symlink(old, new_) \ + __sanitizer_syscall_pre_impl_symlink((long)(old), (long)(new_)) +#define __sanitizer_syscall_post_symlink(res, old, new_) \ + __sanitizer_syscall_post_impl_symlink(res, (long)(old), (long)(new_)) +#define __sanitizer_syscall_pre_unlink(pathname) \ + __sanitizer_syscall_pre_impl_unlink((long)(pathname)) +#define __sanitizer_syscall_post_unlink(res, pathname) \ + __sanitizer_syscall_post_impl_unlink(res, (long)(pathname)) +#define __sanitizer_syscall_pre_rename(oldname, newname) \ + __sanitizer_syscall_pre_impl_rename((long)(oldname), (long)(newname)) +#define __sanitizer_syscall_post_rename(res, oldname, newname) \ + __sanitizer_syscall_post_impl_rename(res, (long)(oldname), (long)(newname)) +#define __sanitizer_syscall_pre_chmod(filename, mode) \ + __sanitizer_syscall_pre_impl_chmod((long)(filename), (long)(mode)) +#define __sanitizer_syscall_post_chmod(res, filename, mode) \ + __sanitizer_syscall_post_impl_chmod(res, (long)(filename), (long)(mode)) +#define __sanitizer_syscall_pre_fchmod(fd, mode) \ + __sanitizer_syscall_pre_impl_fchmod((long)(fd), (long)(mode)) +#define __sanitizer_syscall_post_fchmod(res, fd, mode) \ + __sanitizer_syscall_post_impl_fchmod(res, (long)(fd), (long)(mode)) +#define __sanitizer_syscall_pre_fcntl(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_fcntl((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_fcntl(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_fcntl(res, (long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_fcntl64(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_fcntl64((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_fcntl64(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_fcntl64(res, (long)(fd), (long)(cmd), \ + (long)(arg)) +#define __sanitizer_syscall_pre_pipe(fildes) \ + __sanitizer_syscall_pre_impl_pipe((long)(fildes)) +#define __sanitizer_syscall_post_pipe(res, fildes) \ + __sanitizer_syscall_post_impl_pipe(res, (long)(fildes)) +#define __sanitizer_syscall_pre_pipe2(fildes, flags) \ + __sanitizer_syscall_pre_impl_pipe2((long)(fildes), (long)(flags)) +#define __sanitizer_syscall_post_pipe2(res, fildes, flags) \ + __sanitizer_syscall_post_impl_pipe2(res, (long)(fildes), (long)(flags)) +#define __sanitizer_syscall_pre_dup(fildes) \ + __sanitizer_syscall_pre_impl_dup((long)(fildes)) +#define __sanitizer_syscall_post_dup(res, fildes) \ + __sanitizer_syscall_post_impl_dup(res, (long)(fildes)) +#define __sanitizer_syscall_pre_dup2(oldfd, newfd) \ + __sanitizer_syscall_pre_impl_dup2((long)(oldfd), (long)(newfd)) +#define __sanitizer_syscall_post_dup2(res, oldfd, newfd) \ + __sanitizer_syscall_post_impl_dup2(res, (long)(oldfd), (long)(newfd)) +#define __sanitizer_syscall_pre_dup3(oldfd, newfd, flags) \ + __sanitizer_syscall_pre_impl_dup3((long)(oldfd), (long)(newfd), (long)(flags)) +#define __sanitizer_syscall_post_dup3(res, oldfd, newfd, flags) \ + __sanitizer_syscall_post_impl_dup3(res, (long)(oldfd), (long)(newfd), \ + (long)(flags)) +#define __sanitizer_syscall_pre_ioperm(from, num, on) \ + __sanitizer_syscall_pre_impl_ioperm((long)(from), (long)(num), (long)(on)) +#define __sanitizer_syscall_post_ioperm(res, from, num, on) \ + __sanitizer_syscall_post_impl_ioperm(res, (long)(from), (long)(num), \ + (long)(on)) +#define __sanitizer_syscall_pre_ioctl(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_ioctl((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_ioctl(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_ioctl(res, (long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_flock(fd, cmd) \ + __sanitizer_syscall_pre_impl_flock((long)(fd), (long)(cmd)) +#define __sanitizer_syscall_post_flock(res, fd, cmd) \ + __sanitizer_syscall_post_impl_flock(res, (long)(fd), (long)(cmd)) +#define __sanitizer_syscall_pre_io_setup(nr_reqs, ctx) \ + __sanitizer_syscall_pre_impl_io_setup((long)(nr_reqs), (long)(ctx)) +#define __sanitizer_syscall_post_io_setup(res, nr_reqs, ctx) \ + __sanitizer_syscall_post_impl_io_setup(res, (long)(nr_reqs), (long)(ctx)) +#define __sanitizer_syscall_pre_io_destroy(ctx) \ + __sanitizer_syscall_pre_impl_io_destroy((long)(ctx)) +#define __sanitizer_syscall_post_io_destroy(res, ctx) \ + __sanitizer_syscall_post_impl_io_destroy(res, (long)(ctx)) +#define __sanitizer_syscall_pre_io_getevents(ctx_id, min_nr, nr, events, \ + timeout) \ + __sanitizer_syscall_pre_impl_io_getevents((long)(ctx_id), (long)(min_nr), \ + (long)(nr), (long)(events), \ + (long)(timeout)) +#define __sanitizer_syscall_post_io_getevents(res, ctx_id, min_nr, nr, events, \ + timeout) \ + __sanitizer_syscall_post_impl_io_getevents(res, (long)(ctx_id), \ + (long)(min_nr), (long)(nr), \ + (long)(events), (long)(timeout)) +#define __sanitizer_syscall_pre_io_submit(ctx_id, arg1, arg2) \ + __sanitizer_syscall_pre_impl_io_submit((long)(ctx_id), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_io_submit(res, ctx_id, arg1, arg2) \ + __sanitizer_syscall_post_impl_io_submit(res, (long)(ctx_id), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_io_cancel(ctx_id, iocb, result) \ + __sanitizer_syscall_pre_impl_io_cancel((long)(ctx_id), (long)(iocb), \ + (long)(result)) +#define __sanitizer_syscall_post_io_cancel(res, ctx_id, iocb, result) \ + __sanitizer_syscall_post_impl_io_cancel(res, (long)(ctx_id), (long)(iocb), \ + (long)(result)) +#define __sanitizer_syscall_pre_sendfile(out_fd, in_fd, offset, count) \ + __sanitizer_syscall_pre_impl_sendfile((long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_post_sendfile(res, out_fd, in_fd, offset, count) \ + __sanitizer_syscall_post_impl_sendfile(res, (long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_pre_sendfile64(out_fd, in_fd, offset, count) \ + __sanitizer_syscall_pre_impl_sendfile64((long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_post_sendfile64(res, out_fd, in_fd, offset, count) \ + __sanitizer_syscall_post_impl_sendfile64(res, (long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_pre_readlink(path, buf, bufsiz) \ + __sanitizer_syscall_pre_impl_readlink((long)(path), (long)(buf), \ + (long)(bufsiz)) +#define __sanitizer_syscall_post_readlink(res, path, buf, bufsiz) \ + __sanitizer_syscall_post_impl_readlink(res, (long)(path), (long)(buf), \ + (long)(bufsiz)) +#define __sanitizer_syscall_pre_creat(pathname, mode) \ + __sanitizer_syscall_pre_impl_creat((long)(pathname), (long)(mode)) +#define __sanitizer_syscall_post_creat(res, pathname, mode) \ + __sanitizer_syscall_post_impl_creat(res, (long)(pathname), (long)(mode)) +#define __sanitizer_syscall_pre_open(filename, flags, mode) \ + __sanitizer_syscall_pre_impl_open((long)(filename), (long)(flags), \ + (long)(mode)) +#define __sanitizer_syscall_post_open(res, filename, flags, mode) \ + __sanitizer_syscall_post_impl_open(res, (long)(filename), (long)(flags), \ + (long)(mode)) +#define __sanitizer_syscall_pre_close(fd) \ + __sanitizer_syscall_pre_impl_close((long)(fd)) +#define __sanitizer_syscall_post_close(res, fd) \ + __sanitizer_syscall_post_impl_close(res, (long)(fd)) +#define __sanitizer_syscall_pre_access(filename, mode) \ + __sanitizer_syscall_pre_impl_access((long)(filename), (long)(mode)) +#define __sanitizer_syscall_post_access(res, filename, mode) \ + __sanitizer_syscall_post_impl_access(res, (long)(filename), (long)(mode)) +#define __sanitizer_syscall_pre_vhangup() __sanitizer_syscall_pre_impl_vhangup() +#define __sanitizer_syscall_post_vhangup(res) \ + __sanitizer_syscall_post_impl_vhangup(res) +#define __sanitizer_syscall_pre_chown(filename, user, group) \ + __sanitizer_syscall_pre_impl_chown((long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_post_chown(res, filename, user, group) \ + __sanitizer_syscall_post_impl_chown(res, (long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_lchown(filename, user, group) \ + __sanitizer_syscall_pre_impl_lchown((long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_post_lchown(res, filename, user, group) \ + __sanitizer_syscall_post_impl_lchown(res, (long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_fchown(fd, user, group) \ + __sanitizer_syscall_pre_impl_fchown((long)(fd), (long)(user), (long)(group)) +#define __sanitizer_syscall_post_fchown(res, fd, user, group) \ + __sanitizer_syscall_post_impl_fchown(res, (long)(fd), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_chown16(filename, user, group) \ + __sanitizer_syscall_pre_impl_chown16((long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_post_chown16(res, filename, user, group) \ + __sanitizer_syscall_post_impl_chown16(res, (long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_lchown16(filename, user, group) \ + __sanitizer_syscall_pre_impl_lchown16((long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_post_lchown16(res, filename, user, group) \ + __sanitizer_syscall_post_impl_lchown16(res, (long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_fchown16(fd, user, group) \ + __sanitizer_syscall_pre_impl_fchown16((long)(fd), (long)user, (long)group) +#define __sanitizer_syscall_post_fchown16(res, fd, user, group) \ + __sanitizer_syscall_post_impl_fchown16(res, (long)(fd), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_setregid16(rgid, egid) \ + __sanitizer_syscall_pre_impl_setregid16((long)rgid, (long)egid) +#define __sanitizer_syscall_post_setregid16(res, rgid, egid) \ + __sanitizer_syscall_post_impl_setregid16(res, (long)rgid, (long)egid) +#define __sanitizer_syscall_pre_setgid16(gid) \ + __sanitizer_syscall_pre_impl_setgid16((long)gid) +#define __sanitizer_syscall_post_setgid16(res, gid) \ + __sanitizer_syscall_post_impl_setgid16(res, (long)gid) +#define __sanitizer_syscall_pre_setreuid16(ruid, euid) \ + __sanitizer_syscall_pre_impl_setreuid16((long)ruid, (long)euid) +#define __sanitizer_syscall_post_setreuid16(res, ruid, euid) \ + __sanitizer_syscall_post_impl_setreuid16(res, (long)ruid, (long)euid) +#define __sanitizer_syscall_pre_setuid16(uid) \ + __sanitizer_syscall_pre_impl_setuid16((long)uid) +#define __sanitizer_syscall_post_setuid16(res, uid) \ + __sanitizer_syscall_post_impl_setuid16(res, (long)uid) +#define __sanitizer_syscall_pre_setresuid16(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_setresuid16((long)ruid, (long)euid, (long)suid) +#define __sanitizer_syscall_post_setresuid16(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_setresuid16(res, (long)ruid, (long)euid, \ + (long)suid) +#define __sanitizer_syscall_pre_getresuid16(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_getresuid16((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_getresuid16(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_getresuid16(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_setresgid16(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_setresgid16((long)rgid, (long)egid, (long)sgid) +#define __sanitizer_syscall_post_setresgid16(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_setresgid16(res, (long)rgid, (long)egid, \ + (long)sgid) +#define __sanitizer_syscall_pre_getresgid16(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_getresgid16((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_getresgid16(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_getresgid16(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_setfsuid16(uid) \ + __sanitizer_syscall_pre_impl_setfsuid16((long)uid) +#define __sanitizer_syscall_post_setfsuid16(res, uid) \ + __sanitizer_syscall_post_impl_setfsuid16(res, (long)uid) +#define __sanitizer_syscall_pre_setfsgid16(gid) \ + __sanitizer_syscall_pre_impl_setfsgid16((long)gid) +#define __sanitizer_syscall_post_setfsgid16(res, gid) \ + __sanitizer_syscall_post_impl_setfsgid16(res, (long)gid) +#define __sanitizer_syscall_pre_getgroups16(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_getgroups16((long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_post_getgroups16(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_getgroups16(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_setgroups16(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_setgroups16((long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_post_setgroups16(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_setgroups16(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_getuid16() \ + __sanitizer_syscall_pre_impl_getuid16() +#define __sanitizer_syscall_post_getuid16(res) \ + __sanitizer_syscall_post_impl_getuid16(res) +#define __sanitizer_syscall_pre_geteuid16() \ + __sanitizer_syscall_pre_impl_geteuid16() +#define __sanitizer_syscall_post_geteuid16(res) \ + __sanitizer_syscall_post_impl_geteuid16(res) +#define __sanitizer_syscall_pre_getgid16() \ + __sanitizer_syscall_pre_impl_getgid16() +#define __sanitizer_syscall_post_getgid16(res) \ + __sanitizer_syscall_post_impl_getgid16(res) +#define __sanitizer_syscall_pre_getegid16() \ + __sanitizer_syscall_pre_impl_getegid16() +#define __sanitizer_syscall_post_getegid16(res) \ + __sanitizer_syscall_post_impl_getegid16(res) +#define __sanitizer_syscall_pre_utime(filename, times) \ + __sanitizer_syscall_pre_impl_utime((long)(filename), (long)(times)) +#define __sanitizer_syscall_post_utime(res, filename, times) \ + __sanitizer_syscall_post_impl_utime(res, (long)(filename), (long)(times)) +#define __sanitizer_syscall_pre_utimes(filename, utimes) \ + __sanitizer_syscall_pre_impl_utimes((long)(filename), (long)(utimes)) +#define __sanitizer_syscall_post_utimes(res, filename, utimes) \ + __sanitizer_syscall_post_impl_utimes(res, (long)(filename), (long)(utimes)) +#define __sanitizer_syscall_pre_lseek(fd, offset, origin) \ + __sanitizer_syscall_pre_impl_lseek((long)(fd), (long)(offset), (long)(origin)) +#define __sanitizer_syscall_post_lseek(res, fd, offset, origin) \ + __sanitizer_syscall_post_impl_lseek(res, (long)(fd), (long)(offset), \ + (long)(origin)) +#define __sanitizer_syscall_pre_llseek(fd, offset_high, offset_low, result, \ + origin) \ + __sanitizer_syscall_pre_impl_llseek((long)(fd), (long)(offset_high), \ + (long)(offset_low), (long)(result), \ + (long)(origin)) +#define __sanitizer_syscall_post_llseek(res, fd, offset_high, offset_low, \ + result, origin) \ + __sanitizer_syscall_post_impl_llseek(res, (long)(fd), (long)(offset_high), \ + (long)(offset_low), (long)(result), \ + (long)(origin)) +#define __sanitizer_syscall_pre_read(fd, buf, count) \ + __sanitizer_syscall_pre_impl_read((long)(fd), (long)(buf), (long)(count)) +#define __sanitizer_syscall_post_read(res, fd, buf, count) \ + __sanitizer_syscall_post_impl_read(res, (long)(fd), (long)(buf), \ + (long)(count)) +#define __sanitizer_syscall_pre_readv(fd, vec, vlen) \ + __sanitizer_syscall_pre_impl_readv((long)(fd), (long)(vec), (long)(vlen)) +#define __sanitizer_syscall_post_readv(res, fd, vec, vlen) \ + __sanitizer_syscall_post_impl_readv(res, (long)(fd), (long)(vec), \ + (long)(vlen)) +#define __sanitizer_syscall_pre_write(fd, buf, count) \ + __sanitizer_syscall_pre_impl_write((long)(fd), (long)(buf), (long)(count)) +#define __sanitizer_syscall_post_write(res, fd, buf, count) \ + __sanitizer_syscall_post_impl_write(res, (long)(fd), (long)(buf), \ + (long)(count)) +#define __sanitizer_syscall_pre_writev(fd, vec, vlen) \ + __sanitizer_syscall_pre_impl_writev((long)(fd), (long)(vec), (long)(vlen)) +#define __sanitizer_syscall_post_writev(res, fd, vec, vlen) \ + __sanitizer_syscall_post_impl_writev(res, (long)(fd), (long)(vec), \ + (long)(vlen)) + +#ifdef _LP64 +#define __sanitizer_syscall_pre_pread64(fd, buf, count, pos) \ + __sanitizer_syscall_pre_impl_pread64((long)(fd), (long)(buf), (long)(count), \ + (long)(pos)) +#define __sanitizer_syscall_post_pread64(res, fd, buf, count, pos) \ + __sanitizer_syscall_post_impl_pread64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#define __sanitizer_syscall_pre_pwrite64(fd, buf, count, pos) \ + __sanitizer_syscall_pre_impl_pwrite64((long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#define __sanitizer_syscall_post_pwrite64(res, fd, buf, count, pos) \ + __sanitizer_syscall_post_impl_pwrite64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#else +#define __sanitizer_syscall_pre_pread64(fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_pre_impl_pread64((long)(fd), (long)(buf), (long)(count), \ + (long)(pos0), (long)(pos1)) +#define __sanitizer_syscall_post_pread64(res, fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_post_impl_pread64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos0), \ + (long)(pos1)) +#define __sanitizer_syscall_pre_pwrite64(fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_pre_impl_pwrite64( \ + (long)(fd), (long)(buf), (long)(count), (long)(pos0), (long)(pos1)) +#define __sanitizer_syscall_post_pwrite64(res, fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_post_impl_pwrite64( \ + res, (long)(fd), (long)(buf), (long)(count), (long)(pos0), (long)(pos1)) +#endif + +#define __sanitizer_syscall_pre_preadv(fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_pre_impl_preadv((long)(fd), (long)(vec), (long)(vlen), \ + (long)(pos_l), (long)(pos_h)) +#define __sanitizer_syscall_post_preadv(res, fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_post_impl_preadv(res, (long)(fd), (long)(vec), \ + (long)(vlen), (long)(pos_l), \ + (long)(pos_h)) +#define __sanitizer_syscall_pre_pwritev(fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_pre_impl_pwritev((long)(fd), (long)(vec), (long)(vlen), \ + (long)(pos_l), (long)(pos_h)) +#define __sanitizer_syscall_post_pwritev(res, fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_post_impl_pwritev(res, (long)(fd), (long)(vec), \ + (long)(vlen), (long)(pos_l), \ + (long)(pos_h)) +#define __sanitizer_syscall_pre_getcwd(buf, size) \ + __sanitizer_syscall_pre_impl_getcwd((long)(buf), (long)(size)) +#define __sanitizer_syscall_post_getcwd(res, buf, size) \ + __sanitizer_syscall_post_impl_getcwd(res, (long)(buf), (long)(size)) +#define __sanitizer_syscall_pre_mkdir(pathname, mode) \ + __sanitizer_syscall_pre_impl_mkdir((long)(pathname), (long)(mode)) +#define __sanitizer_syscall_post_mkdir(res, pathname, mode) \ + __sanitizer_syscall_post_impl_mkdir(res, (long)(pathname), (long)(mode)) +#define __sanitizer_syscall_pre_chdir(filename) \ + __sanitizer_syscall_pre_impl_chdir((long)(filename)) +#define __sanitizer_syscall_post_chdir(res, filename) \ + __sanitizer_syscall_post_impl_chdir(res, (long)(filename)) +#define __sanitizer_syscall_pre_fchdir(fd) \ + __sanitizer_syscall_pre_impl_fchdir((long)(fd)) +#define __sanitizer_syscall_post_fchdir(res, fd) \ + __sanitizer_syscall_post_impl_fchdir(res, (long)(fd)) +#define __sanitizer_syscall_pre_rmdir(pathname) \ + __sanitizer_syscall_pre_impl_rmdir((long)(pathname)) +#define __sanitizer_syscall_post_rmdir(res, pathname) \ + __sanitizer_syscall_post_impl_rmdir(res, (long)(pathname)) +#define __sanitizer_syscall_pre_lookup_dcookie(cookie64, buf, len) \ + __sanitizer_syscall_pre_impl_lookup_dcookie((long)(cookie64), (long)(buf), \ + (long)(len)) +#define __sanitizer_syscall_post_lookup_dcookie(res, cookie64, buf, len) \ + __sanitizer_syscall_post_impl_lookup_dcookie(res, (long)(cookie64), \ + (long)(buf), (long)(len)) +#define __sanitizer_syscall_pre_quotactl(cmd, special, id, addr) \ + __sanitizer_syscall_pre_impl_quotactl((long)(cmd), (long)(special), \ + (long)(id), (long)(addr)) +#define __sanitizer_syscall_post_quotactl(res, cmd, special, id, addr) \ + __sanitizer_syscall_post_impl_quotactl(res, (long)(cmd), (long)(special), \ + (long)(id), (long)(addr)) +#define __sanitizer_syscall_pre_getdents(fd, dirent, count) \ + __sanitizer_syscall_pre_impl_getdents((long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_post_getdents(res, fd, dirent, count) \ + __sanitizer_syscall_post_impl_getdents(res, (long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_pre_getdents64(fd, dirent, count) \ + __sanitizer_syscall_pre_impl_getdents64((long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_post_getdents64(res, fd, dirent, count) \ + __sanitizer_syscall_post_impl_getdents64(res, (long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_pre_setsockopt(fd, level, optname, optval, optlen) \ + __sanitizer_syscall_pre_impl_setsockopt((long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_post_setsockopt(res, fd, level, optname, optval, \ + optlen) \ + __sanitizer_syscall_post_impl_setsockopt(res, (long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_pre_getsockopt(fd, level, optname, optval, optlen) \ + __sanitizer_syscall_pre_impl_getsockopt((long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_post_getsockopt(res, fd, level, optname, optval, \ + optlen) \ + __sanitizer_syscall_post_impl_getsockopt(res, (long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_pre_bind(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_bind((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_bind(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_bind(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_connect(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_connect((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_connect(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_connect(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_accept(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_accept((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_accept(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_accept(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_accept4(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_accept4((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_post_accept4(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_accept4(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_getsockname(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_getsockname((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_getsockname(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_getsockname(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_getpeername(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_getpeername((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_getpeername(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_getpeername(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_send(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_send((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3)) +#define __sanitizer_syscall_post_send(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_send(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_sendto(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_sendto((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_sendto(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_sendto(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_sendmsg(fd, msg, flags) \ + __sanitizer_syscall_pre_impl_sendmsg((long)(fd), (long)(msg), (long)(flags)) +#define __sanitizer_syscall_post_sendmsg(res, fd, msg, flags) \ + __sanitizer_syscall_post_impl_sendmsg(res, (long)(fd), (long)(msg), \ + (long)(flags)) +#define __sanitizer_syscall_pre_sendmmsg(fd, msg, vlen, flags) \ + __sanitizer_syscall_pre_impl_sendmmsg((long)(fd), (long)(msg), (long)(vlen), \ + (long)(flags)) +#define __sanitizer_syscall_post_sendmmsg(res, fd, msg, vlen, flags) \ + __sanitizer_syscall_post_impl_sendmmsg(res, (long)(fd), (long)(msg), \ + (long)(vlen), (long)(flags)) +#define __sanitizer_syscall_pre_recv(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_recv((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3)) +#define __sanitizer_syscall_post_recv(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_recv(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_recvfrom(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_recvfrom((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_recvfrom(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_recvfrom(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_recvmsg(fd, msg, flags) \ + __sanitizer_syscall_pre_impl_recvmsg((long)(fd), (long)(msg), (long)(flags)) +#define __sanitizer_syscall_post_recvmsg(res, fd, msg, flags) \ + __sanitizer_syscall_post_impl_recvmsg(res, (long)(fd), (long)(msg), \ + (long)(flags)) +#define __sanitizer_syscall_pre_recvmmsg(fd, msg, vlen, flags, timeout) \ + __sanitizer_syscall_pre_impl_recvmmsg((long)(fd), (long)(msg), (long)(vlen), \ + (long)(flags), (long)(timeout)) +#define __sanitizer_syscall_post_recvmmsg(res, fd, msg, vlen, flags, timeout) \ + __sanitizer_syscall_post_impl_recvmmsg(res, (long)(fd), (long)(msg), \ + (long)(vlen), (long)(flags), \ + (long)(timeout)) +#define __sanitizer_syscall_pre_socket(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_socket((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_socket(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_socket(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_socketpair(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_socketpair((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_post_socketpair(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_socketpair(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_socketcall(call, args) \ + __sanitizer_syscall_pre_impl_socketcall((long)(call), (long)(args)) +#define __sanitizer_syscall_post_socketcall(res, call, args) \ + __sanitizer_syscall_post_impl_socketcall(res, (long)(call), (long)(args)) +#define __sanitizer_syscall_pre_listen(arg0, arg1) \ + __sanitizer_syscall_pre_impl_listen((long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_post_listen(res, arg0, arg1) \ + __sanitizer_syscall_post_impl_listen(res, (long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_pre_poll(ufds, nfds, timeout) \ + __sanitizer_syscall_pre_impl_poll((long)(ufds), (long)(nfds), (long)(timeout)) +#define __sanitizer_syscall_post_poll(res, ufds, nfds, timeout) \ + __sanitizer_syscall_post_impl_poll(res, (long)(ufds), (long)(nfds), \ + (long)(timeout)) +#define __sanitizer_syscall_pre_select(n, inp, outp, exp, tvp) \ + __sanitizer_syscall_pre_impl_select((long)(n), (long)(inp), (long)(outp), \ + (long)(exp), (long)(tvp)) +#define __sanitizer_syscall_post_select(res, n, inp, outp, exp, tvp) \ + __sanitizer_syscall_post_impl_select(res, (long)(n), (long)(inp), \ + (long)(outp), (long)(exp), (long)(tvp)) +#define __sanitizer_syscall_pre_old_select(arg) \ + __sanitizer_syscall_pre_impl_old_select((long)(arg)) +#define __sanitizer_syscall_post_old_select(res, arg) \ + __sanitizer_syscall_post_impl_old_select(res, (long)(arg)) +#define __sanitizer_syscall_pre_epoll_create(size) \ + __sanitizer_syscall_pre_impl_epoll_create((long)(size)) +#define __sanitizer_syscall_post_epoll_create(res, size) \ + __sanitizer_syscall_post_impl_epoll_create(res, (long)(size)) +#define __sanitizer_syscall_pre_epoll_create1(flags) \ + __sanitizer_syscall_pre_impl_epoll_create1((long)(flags)) +#define __sanitizer_syscall_post_epoll_create1(res, flags) \ + __sanitizer_syscall_post_impl_epoll_create1(res, (long)(flags)) +#define __sanitizer_syscall_pre_epoll_ctl(epfd, op, fd, event) \ + __sanitizer_syscall_pre_impl_epoll_ctl((long)(epfd), (long)(op), (long)(fd), \ + (long)(event)) +#define __sanitizer_syscall_post_epoll_ctl(res, epfd, op, fd, event) \ + __sanitizer_syscall_post_impl_epoll_ctl(res, (long)(epfd), (long)(op), \ + (long)(fd), (long)(event)) +#define __sanitizer_syscall_pre_epoll_wait(epfd, events, maxevents, timeout) \ + __sanitizer_syscall_pre_impl_epoll_wait((long)(epfd), (long)(events), \ + (long)(maxevents), (long)(timeout)) +#define __sanitizer_syscall_post_epoll_wait(res, epfd, events, maxevents, \ + timeout) \ + __sanitizer_syscall_post_impl_epoll_wait(res, (long)(epfd), (long)(events), \ + (long)(maxevents), (long)(timeout)) +#define __sanitizer_syscall_pre_epoll_pwait(epfd, events, maxevents, timeout, \ + sigmask, sigsetsize) \ + __sanitizer_syscall_pre_impl_epoll_pwait( \ + (long)(epfd), (long)(events), (long)(maxevents), (long)(timeout), \ + (long)(sigmask), (long)(sigsetsize)) +#define __sanitizer_syscall_post_epoll_pwait(res, epfd, events, maxevents, \ + timeout, sigmask, sigsetsize) \ + __sanitizer_syscall_post_impl_epoll_pwait( \ + res, (long)(epfd), (long)(events), (long)(maxevents), (long)(timeout), \ + (long)(sigmask), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_gethostname(name, len) \ + __sanitizer_syscall_pre_impl_gethostname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_gethostname(res, name, len) \ + __sanitizer_syscall_post_impl_gethostname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_sethostname(name, len) \ + __sanitizer_syscall_pre_impl_sethostname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_sethostname(res, name, len) \ + __sanitizer_syscall_post_impl_sethostname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_setdomainname(name, len) \ + __sanitizer_syscall_pre_impl_setdomainname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_setdomainname(res, name, len) \ + __sanitizer_syscall_post_impl_setdomainname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_newuname(name) \ + __sanitizer_syscall_pre_impl_newuname((long)(name)) +#define __sanitizer_syscall_post_newuname(res, name) \ + __sanitizer_syscall_post_impl_newuname(res, (long)(name)) +#define __sanitizer_syscall_pre_uname(arg0) \ + __sanitizer_syscall_pre_impl_uname((long)(arg0)) +#define __sanitizer_syscall_post_uname(res, arg0) \ + __sanitizer_syscall_post_impl_uname(res, (long)(arg0)) +#define __sanitizer_syscall_pre_olduname(arg0) \ + __sanitizer_syscall_pre_impl_olduname((long)(arg0)) +#define __sanitizer_syscall_post_olduname(res, arg0) \ + __sanitizer_syscall_post_impl_olduname(res, (long)(arg0)) +#define __sanitizer_syscall_pre_getrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_getrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_getrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_getrlimit(res, (long)(resource), (long)(rlim)) +#define __sanitizer_syscall_pre_old_getrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_old_getrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_old_getrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_old_getrlimit(res, (long)(resource), \ + (long)(rlim)) +#define __sanitizer_syscall_pre_setrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_setrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_setrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_setrlimit(res, (long)(resource), (long)(rlim)) +#define __sanitizer_syscall_pre_prlimit64(pid, resource, new_rlim, old_rlim) \ + __sanitizer_syscall_pre_impl_prlimit64((long)(pid), (long)(resource), \ + (long)(new_rlim), (long)(old_rlim)) +#define __sanitizer_syscall_post_prlimit64(res, pid, resource, new_rlim, \ + old_rlim) \ + __sanitizer_syscall_post_impl_prlimit64(res, (long)(pid), (long)(resource), \ + (long)(new_rlim), (long)(old_rlim)) +#define __sanitizer_syscall_pre_getrusage(who, ru) \ + __sanitizer_syscall_pre_impl_getrusage((long)(who), (long)(ru)) +#define __sanitizer_syscall_post_getrusage(res, who, ru) \ + __sanitizer_syscall_post_impl_getrusage(res, (long)(who), (long)(ru)) +#define __sanitizer_syscall_pre_umask(mask) \ + __sanitizer_syscall_pre_impl_umask((long)(mask)) +#define __sanitizer_syscall_post_umask(res, mask) \ + __sanitizer_syscall_post_impl_umask(res, (long)(mask)) +#define __sanitizer_syscall_pre_msgget(key, msgflg) \ + __sanitizer_syscall_pre_impl_msgget((long)(key), (long)(msgflg)) +#define __sanitizer_syscall_post_msgget(res, key, msgflg) \ + __sanitizer_syscall_post_impl_msgget(res, (long)(key), (long)(msgflg)) +#define __sanitizer_syscall_pre_msgsnd(msqid, msgp, msgsz, msgflg) \ + __sanitizer_syscall_pre_impl_msgsnd((long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgflg)) +#define __sanitizer_syscall_post_msgsnd(res, msqid, msgp, msgsz, msgflg) \ + __sanitizer_syscall_post_impl_msgsnd(res, (long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgflg)) +#define __sanitizer_syscall_pre_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg) \ + __sanitizer_syscall_pre_impl_msgrcv((long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgtyp), \ + (long)(msgflg)) +#define __sanitizer_syscall_post_msgrcv(res, msqid, msgp, msgsz, msgtyp, \ + msgflg) \ + __sanitizer_syscall_post_impl_msgrcv(res, (long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgtyp), \ + (long)(msgflg)) +#define __sanitizer_syscall_pre_msgctl(msqid, cmd, buf) \ + __sanitizer_syscall_pre_impl_msgctl((long)(msqid), (long)(cmd), (long)(buf)) +#define __sanitizer_syscall_post_msgctl(res, msqid, cmd, buf) \ + __sanitizer_syscall_post_impl_msgctl(res, (long)(msqid), (long)(cmd), \ + (long)(buf)) +#define __sanitizer_syscall_pre_semget(key, nsems, semflg) \ + __sanitizer_syscall_pre_impl_semget((long)(key), (long)(nsems), \ + (long)(semflg)) +#define __sanitizer_syscall_post_semget(res, key, nsems, semflg) \ + __sanitizer_syscall_post_impl_semget(res, (long)(key), (long)(nsems), \ + (long)(semflg)) +#define __sanitizer_syscall_pre_semop(semid, sops, nsops) \ + __sanitizer_syscall_pre_impl_semop((long)(semid), (long)(sops), (long)(nsops)) +#define __sanitizer_syscall_post_semop(res, semid, sops, nsops) \ + __sanitizer_syscall_post_impl_semop(res, (long)(semid), (long)(sops), \ + (long)(nsops)) +#define __sanitizer_syscall_pre_semctl(semid, semnum, cmd, arg) \ + __sanitizer_syscall_pre_impl_semctl((long)(semid), (long)(semnum), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_semctl(res, semid, semnum, cmd, arg) \ + __sanitizer_syscall_post_impl_semctl(res, (long)(semid), (long)(semnum), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_semtimedop(semid, sops, nsops, timeout) \ + __sanitizer_syscall_pre_impl_semtimedop((long)(semid), (long)(sops), \ + (long)(nsops), (long)(timeout)) +#define __sanitizer_syscall_post_semtimedop(res, semid, sops, nsops, timeout) \ + __sanitizer_syscall_post_impl_semtimedop(res, (long)(semid), (long)(sops), \ + (long)(nsops), (long)(timeout)) +#define __sanitizer_syscall_pre_shmat(shmid, shmaddr, shmflg) \ + __sanitizer_syscall_pre_impl_shmat((long)(shmid), (long)(shmaddr), \ + (long)(shmflg)) +#define __sanitizer_syscall_post_shmat(res, shmid, shmaddr, shmflg) \ + __sanitizer_syscall_post_impl_shmat(res, (long)(shmid), (long)(shmaddr), \ + (long)(shmflg)) +#define __sanitizer_syscall_pre_shmget(key, size, flag) \ + __sanitizer_syscall_pre_impl_shmget((long)(key), (long)(size), (long)(flag)) +#define __sanitizer_syscall_post_shmget(res, key, size, flag) \ + __sanitizer_syscall_post_impl_shmget(res, (long)(key), (long)(size), \ + (long)(flag)) +#define __sanitizer_syscall_pre_shmdt(shmaddr) \ + __sanitizer_syscall_pre_impl_shmdt((long)(shmaddr)) +#define __sanitizer_syscall_post_shmdt(res, shmaddr) \ + __sanitizer_syscall_post_impl_shmdt(res, (long)(shmaddr)) +#define __sanitizer_syscall_pre_shmctl(shmid, cmd, buf) \ + __sanitizer_syscall_pre_impl_shmctl((long)(shmid), (long)(cmd), (long)(buf)) +#define __sanitizer_syscall_post_shmctl(res, shmid, cmd, buf) \ + __sanitizer_syscall_post_impl_shmctl(res, (long)(shmid), (long)(cmd), \ + (long)(buf)) +#define __sanitizer_syscall_pre_ipc(call, first, second, third, ptr, fifth) \ + __sanitizer_syscall_pre_impl_ipc((long)(call), (long)(first), \ + (long)(second), (long)(third), (long)(ptr), \ + (long)(fifth)) +#define __sanitizer_syscall_post_ipc(res, call, first, second, third, ptr, \ + fifth) \ + __sanitizer_syscall_post_impl_ipc(res, (long)(call), (long)(first), \ + (long)(second), (long)(third), \ + (long)(ptr), (long)(fifth)) +#define __sanitizer_syscall_pre_mq_open(name, oflag, mode, attr) \ + __sanitizer_syscall_pre_impl_mq_open((long)(name), (long)(oflag), \ + (long)(mode), (long)(attr)) +#define __sanitizer_syscall_post_mq_open(res, name, oflag, mode, attr) \ + __sanitizer_syscall_post_impl_mq_open(res, (long)(name), (long)(oflag), \ + (long)(mode), (long)(attr)) +#define __sanitizer_syscall_pre_mq_unlink(name) \ + __sanitizer_syscall_pre_impl_mq_unlink((long)(name)) +#define __sanitizer_syscall_post_mq_unlink(res, name) \ + __sanitizer_syscall_post_impl_mq_unlink(res, (long)(name)) +#define __sanitizer_syscall_pre_mq_timedsend(mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_pre_impl_mq_timedsend((long)(mqdes), (long)(msg_ptr), \ + (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_post_mq_timedsend(res, mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_post_impl_mq_timedsend( \ + res, (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_pre_mq_timedreceive(mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_pre_impl_mq_timedreceive( \ + (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_post_mq_timedreceive(res, mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_post_impl_mq_timedreceive( \ + res, (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_pre_mq_notify(mqdes, notification) \ + __sanitizer_syscall_pre_impl_mq_notify((long)(mqdes), (long)(notification)) +#define __sanitizer_syscall_post_mq_notify(res, mqdes, notification) \ + __sanitizer_syscall_post_impl_mq_notify(res, (long)(mqdes), \ + (long)(notification)) +#define __sanitizer_syscall_pre_mq_getsetattr(mqdes, mqstat, omqstat) \ + __sanitizer_syscall_pre_impl_mq_getsetattr((long)(mqdes), (long)(mqstat), \ + (long)(omqstat)) +#define __sanitizer_syscall_post_mq_getsetattr(res, mqdes, mqstat, omqstat) \ + __sanitizer_syscall_post_impl_mq_getsetattr(res, (long)(mqdes), \ + (long)(mqstat), (long)(omqstat)) +#define __sanitizer_syscall_pre_pciconfig_iobase(which, bus, devfn) \ + __sanitizer_syscall_pre_impl_pciconfig_iobase((long)(which), (long)(bus), \ + (long)(devfn)) +#define __sanitizer_syscall_post_pciconfig_iobase(res, which, bus, devfn) \ + __sanitizer_syscall_post_impl_pciconfig_iobase(res, (long)(which), \ + (long)(bus), (long)(devfn)) +#define __sanitizer_syscall_pre_pciconfig_read(bus, dfn, off, len, buf) \ + __sanitizer_syscall_pre_impl_pciconfig_read( \ + (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_post_pciconfig_read(res, bus, dfn, off, len, buf) \ + __sanitizer_syscall_post_impl_pciconfig_read( \ + res, (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_pre_pciconfig_write(bus, dfn, off, len, buf) \ + __sanitizer_syscall_pre_impl_pciconfig_write( \ + (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_post_pciconfig_write(res, bus, dfn, off, len, buf) \ + __sanitizer_syscall_post_impl_pciconfig_write( \ + res, (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_pre_swapon(specialfile, swap_flags) \ + __sanitizer_syscall_pre_impl_swapon((long)(specialfile), (long)(swap_flags)) +#define __sanitizer_syscall_post_swapon(res, specialfile, swap_flags) \ + __sanitizer_syscall_post_impl_swapon(res, (long)(specialfile), \ + (long)(swap_flags)) +#define __sanitizer_syscall_pre_swapoff(specialfile) \ + __sanitizer_syscall_pre_impl_swapoff((long)(specialfile)) +#define __sanitizer_syscall_post_swapoff(res, specialfile) \ + __sanitizer_syscall_post_impl_swapoff(res, (long)(specialfile)) +#define __sanitizer_syscall_pre_sysctl(args) \ + __sanitizer_syscall_pre_impl_sysctl((long)(args)) +#define __sanitizer_syscall_post_sysctl(res, args) \ + __sanitizer_syscall_post_impl_sysctl(res, (long)(args)) +#define __sanitizer_syscall_pre_sysinfo(info) \ + __sanitizer_syscall_pre_impl_sysinfo((long)(info)) +#define __sanitizer_syscall_post_sysinfo(res, info) \ + __sanitizer_syscall_post_impl_sysinfo(res, (long)(info)) +#define __sanitizer_syscall_pre_sysfs(option, arg1, arg2) \ + __sanitizer_syscall_pre_impl_sysfs((long)(option), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_sysfs(res, option, arg1, arg2) \ + __sanitizer_syscall_post_impl_sysfs(res, (long)(option), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_syslog(type, buf, len) \ + __sanitizer_syscall_pre_impl_syslog((long)(type), (long)(buf), (long)(len)) +#define __sanitizer_syscall_post_syslog(res, type, buf, len) \ + __sanitizer_syscall_post_impl_syslog(res, (long)(type), (long)(buf), \ + (long)(len)) +#define __sanitizer_syscall_pre_uselib(library) \ + __sanitizer_syscall_pre_impl_uselib((long)(library)) +#define __sanitizer_syscall_post_uselib(res, library) \ + __sanitizer_syscall_post_impl_uselib(res, (long)(library)) +#define __sanitizer_syscall_pre_ni_syscall() \ + __sanitizer_syscall_pre_impl_ni_syscall() +#define __sanitizer_syscall_post_ni_syscall(res) \ + __sanitizer_syscall_post_impl_ni_syscall(res) +#define __sanitizer_syscall_pre_ptrace(request, pid, addr, data) \ + __sanitizer_syscall_pre_impl_ptrace((long)(request), (long)(pid), \ + (long)(addr), (long)(data)) +#define __sanitizer_syscall_post_ptrace(res, request, pid, addr, data) \ + __sanitizer_syscall_post_impl_ptrace(res, (long)(request), (long)(pid), \ + (long)(addr), (long)(data)) +#define __sanitizer_syscall_pre_add_key(_type, _description, _payload, plen, \ + destringid) \ + __sanitizer_syscall_pre_impl_add_key((long)(_type), (long)(_description), \ + (long)(_payload), (long)(plen), \ + (long)(destringid)) +#define __sanitizer_syscall_post_add_key(res, _type, _description, _payload, \ + plen, destringid) \ + __sanitizer_syscall_post_impl_add_key( \ + res, (long)(_type), (long)(_description), (long)(_payload), \ + (long)(plen), (long)(destringid)) +#define __sanitizer_syscall_pre_request_key(_type, _description, \ + _callout_info, destringid) \ + __sanitizer_syscall_pre_impl_request_key( \ + (long)(_type), (long)(_description), (long)(_callout_info), \ + (long)(destringid)) +#define __sanitizer_syscall_post_request_key(res, _type, _description, \ + _callout_info, destringid) \ + __sanitizer_syscall_post_impl_request_key( \ + res, (long)(_type), (long)(_description), (long)(_callout_info), \ + (long)(destringid)) +#define __sanitizer_syscall_pre_keyctl(cmd, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_keyctl((long)(cmd), (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_keyctl(res, cmd, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_post_impl_keyctl(res, (long)(cmd), (long)(arg2), \ + (long)(arg3), (long)(arg4), \ + (long)(arg5)) +#define __sanitizer_syscall_pre_ioprio_set(which, who, ioprio) \ + __sanitizer_syscall_pre_impl_ioprio_set((long)(which), (long)(who), \ + (long)(ioprio)) +#define __sanitizer_syscall_post_ioprio_set(res, which, who, ioprio) \ + __sanitizer_syscall_post_impl_ioprio_set(res, (long)(which), (long)(who), \ + (long)(ioprio)) +#define __sanitizer_syscall_pre_ioprio_get(which, who) \ + __sanitizer_syscall_pre_impl_ioprio_get((long)(which), (long)(who)) +#define __sanitizer_syscall_post_ioprio_get(res, which, who) \ + __sanitizer_syscall_post_impl_ioprio_get(res, (long)(which), (long)(who)) +#define __sanitizer_syscall_pre_set_mempolicy(mode, nmask, maxnode) \ + __sanitizer_syscall_pre_impl_set_mempolicy((long)(mode), (long)(nmask), \ + (long)(maxnode)) +#define __sanitizer_syscall_post_set_mempolicy(res, mode, nmask, maxnode) \ + __sanitizer_syscall_post_impl_set_mempolicy(res, (long)(mode), \ + (long)(nmask), (long)(maxnode)) +#define __sanitizer_syscall_pre_migrate_pages(pid, maxnode, from, to) \ + __sanitizer_syscall_pre_impl_migrate_pages((long)(pid), (long)(maxnode), \ + (long)(from), (long)(to)) +#define __sanitizer_syscall_post_migrate_pages(res, pid, maxnode, from, to) \ + __sanitizer_syscall_post_impl_migrate_pages( \ + res, (long)(pid), (long)(maxnode), (long)(from), (long)(to)) +#define __sanitizer_syscall_pre_move_pages(pid, nr_pages, pages, nodes, \ + status, flags) \ + __sanitizer_syscall_pre_impl_move_pages((long)(pid), (long)(nr_pages), \ + (long)(pages), (long)(nodes), \ + (long)(status), (long)(flags)) +#define __sanitizer_syscall_post_move_pages(res, pid, nr_pages, pages, nodes, \ + status, flags) \ + __sanitizer_syscall_post_impl_move_pages(res, (long)(pid), (long)(nr_pages), \ + (long)(pages), (long)(nodes), \ + (long)(status), (long)(flags)) +#define __sanitizer_syscall_pre_mbind(start, len, mode, nmask, maxnode, flags) \ + __sanitizer_syscall_pre_impl_mbind((long)(start), (long)(len), (long)(mode), \ + (long)(nmask), (long)(maxnode), \ + (long)(flags)) +#define __sanitizer_syscall_post_mbind(res, start, len, mode, nmask, maxnode, \ + flags) \ + __sanitizer_syscall_post_impl_mbind(res, (long)(start), (long)(len), \ + (long)(mode), (long)(nmask), \ + (long)(maxnode), (long)(flags)) +#define __sanitizer_syscall_pre_get_mempolicy(policy, nmask, maxnode, addr, \ + flags) \ + __sanitizer_syscall_pre_impl_get_mempolicy((long)(policy), (long)(nmask), \ + (long)(maxnode), (long)(addr), \ + (long)(flags)) +#define __sanitizer_syscall_post_get_mempolicy(res, policy, nmask, maxnode, \ + addr, flags) \ + __sanitizer_syscall_post_impl_get_mempolicy(res, (long)(policy), \ + (long)(nmask), (long)(maxnode), \ + (long)(addr), (long)(flags)) +#define __sanitizer_syscall_pre_inotify_init() \ + __sanitizer_syscall_pre_impl_inotify_init() +#define __sanitizer_syscall_post_inotify_init(res) \ + __sanitizer_syscall_post_impl_inotify_init(res) +#define __sanitizer_syscall_pre_inotify_init1(flags) \ + __sanitizer_syscall_pre_impl_inotify_init1((long)(flags)) +#define __sanitizer_syscall_post_inotify_init1(res, flags) \ + __sanitizer_syscall_post_impl_inotify_init1(res, (long)(flags)) +#define __sanitizer_syscall_pre_inotify_add_watch(fd, path, mask) \ + __sanitizer_syscall_pre_impl_inotify_add_watch((long)(fd), (long)(path), \ + (long)(mask)) +#define __sanitizer_syscall_post_inotify_add_watch(res, fd, path, mask) \ + __sanitizer_syscall_post_impl_inotify_add_watch(res, (long)(fd), \ + (long)(path), (long)(mask)) +#define __sanitizer_syscall_pre_inotify_rm_watch(fd, wd) \ + __sanitizer_syscall_pre_impl_inotify_rm_watch((long)(fd), (long)(wd)) +#define __sanitizer_syscall_post_inotify_rm_watch(res, fd, wd) \ + __sanitizer_syscall_post_impl_inotify_rm_watch(res, (long)(fd), (long)(wd)) +#define __sanitizer_syscall_pre_spu_run(fd, unpc, ustatus) \ + __sanitizer_syscall_pre_impl_spu_run((long)(fd), (long)(unpc), \ + (long)(ustatus)) +#define __sanitizer_syscall_post_spu_run(res, fd, unpc, ustatus) \ + __sanitizer_syscall_post_impl_spu_run(res, (long)(fd), (long)(unpc), \ + (long)(ustatus)) +#define __sanitizer_syscall_pre_spu_create(name, flags, mode, fd) \ + __sanitizer_syscall_pre_impl_spu_create((long)(name), (long)(flags), \ + (long)(mode), (long)(fd)) +#define __sanitizer_syscall_post_spu_create(res, name, flags, mode, fd) \ + __sanitizer_syscall_post_impl_spu_create(res, (long)(name), (long)(flags), \ + (long)(mode), (long)(fd)) +#define __sanitizer_syscall_pre_mknodat(dfd, filename, mode, dev) \ + __sanitizer_syscall_pre_impl_mknodat((long)(dfd), (long)(filename), \ + (long)(mode), (long)(dev)) +#define __sanitizer_syscall_post_mknodat(res, dfd, filename, mode, dev) \ + __sanitizer_syscall_post_impl_mknodat(res, (long)(dfd), (long)(filename), \ + (long)(mode), (long)(dev)) +#define __sanitizer_syscall_pre_mkdirat(dfd, pathname, mode) \ + __sanitizer_syscall_pre_impl_mkdirat((long)(dfd), (long)(pathname), \ + (long)(mode)) +#define __sanitizer_syscall_post_mkdirat(res, dfd, pathname, mode) \ + __sanitizer_syscall_post_impl_mkdirat(res, (long)(dfd), (long)(pathname), \ + (long)(mode)) +#define __sanitizer_syscall_pre_unlinkat(dfd, pathname, flag) \ + __sanitizer_syscall_pre_impl_unlinkat((long)(dfd), (long)(pathname), \ + (long)(flag)) +#define __sanitizer_syscall_post_unlinkat(res, dfd, pathname, flag) \ + __sanitizer_syscall_post_impl_unlinkat(res, (long)(dfd), (long)(pathname), \ + (long)(flag)) +#define __sanitizer_syscall_pre_symlinkat(oldname, newdfd, newname) \ + __sanitizer_syscall_pre_impl_symlinkat((long)(oldname), (long)(newdfd), \ + (long)(newname)) +#define __sanitizer_syscall_post_symlinkat(res, oldname, newdfd, newname) \ + __sanitizer_syscall_post_impl_symlinkat(res, (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_pre_linkat(olddfd, oldname, newdfd, newname, \ + flags) \ + __sanitizer_syscall_pre_impl_linkat((long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname), \ + (long)(flags)) +#define __sanitizer_syscall_post_linkat(res, olddfd, oldname, newdfd, newname, \ + flags) \ + __sanitizer_syscall_post_impl_linkat(res, (long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname), \ + (long)(flags)) +#define __sanitizer_syscall_pre_renameat(olddfd, oldname, newdfd, newname) \ + __sanitizer_syscall_pre_impl_renameat((long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_post_renameat(res, olddfd, oldname, newdfd, \ + newname) \ + __sanitizer_syscall_post_impl_renameat(res, (long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_pre_futimesat(dfd, filename, utimes) \ + __sanitizer_syscall_pre_impl_futimesat((long)(dfd), (long)(filename), \ + (long)(utimes)) +#define __sanitizer_syscall_post_futimesat(res, dfd, filename, utimes) \ + __sanitizer_syscall_post_impl_futimesat(res, (long)(dfd), (long)(filename), \ + (long)(utimes)) +#define __sanitizer_syscall_pre_faccessat(dfd, filename, mode) \ + __sanitizer_syscall_pre_impl_faccessat((long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_post_faccessat(res, dfd, filename, mode) \ + __sanitizer_syscall_post_impl_faccessat(res, (long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_pre_fchmodat(dfd, filename, mode) \ + __sanitizer_syscall_pre_impl_fchmodat((long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_post_fchmodat(res, dfd, filename, mode) \ + __sanitizer_syscall_post_impl_fchmodat(res, (long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_pre_fchownat(dfd, filename, user, group, flag) \ + __sanitizer_syscall_pre_impl_fchownat((long)(dfd), (long)(filename), \ + (long)(user), (long)(group), \ + (long)(flag)) +#define __sanitizer_syscall_post_fchownat(res, dfd, filename, user, group, \ + flag) \ + __sanitizer_syscall_post_impl_fchownat(res, (long)(dfd), (long)(filename), \ + (long)(user), (long)(group), \ + (long)(flag)) +#define __sanitizer_syscall_pre_openat(dfd, filename, flags, mode) \ + __sanitizer_syscall_pre_impl_openat((long)(dfd), (long)(filename), \ + (long)(flags), (long)(mode)) +#define __sanitizer_syscall_post_openat(res, dfd, filename, flags, mode) \ + __sanitizer_syscall_post_impl_openat(res, (long)(dfd), (long)(filename), \ + (long)(flags), (long)(mode)) +#define __sanitizer_syscall_pre_newfstatat(dfd, filename, statbuf, flag) \ + __sanitizer_syscall_pre_impl_newfstatat((long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_post_newfstatat(res, dfd, filename, statbuf, flag) \ + __sanitizer_syscall_post_impl_newfstatat(res, (long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_pre_fstatat64(dfd, filename, statbuf, flag) \ + __sanitizer_syscall_pre_impl_fstatat64((long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_post_fstatat64(res, dfd, filename, statbuf, flag) \ + __sanitizer_syscall_post_impl_fstatat64(res, (long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_pre_readlinkat(dfd, path, buf, bufsiz) \ + __sanitizer_syscall_pre_impl_readlinkat((long)(dfd), (long)(path), \ + (long)(buf), (long)(bufsiz)) +#define __sanitizer_syscall_post_readlinkat(res, dfd, path, buf, bufsiz) \ + __sanitizer_syscall_post_impl_readlinkat(res, (long)(dfd), (long)(path), \ + (long)(buf), (long)(bufsiz)) +#define __sanitizer_syscall_pre_utimensat(dfd, filename, utimes, flags) \ + __sanitizer_syscall_pre_impl_utimensat((long)(dfd), (long)(filename), \ + (long)(utimes), (long)(flags)) +#define __sanitizer_syscall_post_utimensat(res, dfd, filename, utimes, flags) \ + __sanitizer_syscall_post_impl_utimensat(res, (long)(dfd), (long)(filename), \ + (long)(utimes), (long)(flags)) +#define __sanitizer_syscall_pre_unshare(unshare_flags) \ + __sanitizer_syscall_pre_impl_unshare((long)(unshare_flags)) +#define __sanitizer_syscall_post_unshare(res, unshare_flags) \ + __sanitizer_syscall_post_impl_unshare(res, (long)(unshare_flags)) +#define __sanitizer_syscall_pre_splice(fd_in, off_in, fd_out, off_out, len, \ + flags) \ + __sanitizer_syscall_pre_impl_splice((long)(fd_in), (long)(off_in), \ + (long)(fd_out), (long)(off_out), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_post_splice(res, fd_in, off_in, fd_out, off_out, \ + len, flags) \ + __sanitizer_syscall_post_impl_splice(res, (long)(fd_in), (long)(off_in), \ + (long)(fd_out), (long)(off_out), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_pre_vmsplice(fd, iov, nr_segs, flags) \ + __sanitizer_syscall_pre_impl_vmsplice((long)(fd), (long)(iov), \ + (long)(nr_segs), (long)(flags)) +#define __sanitizer_syscall_post_vmsplice(res, fd, iov, nr_segs, flags) \ + __sanitizer_syscall_post_impl_vmsplice(res, (long)(fd), (long)(iov), \ + (long)(nr_segs), (long)(flags)) +#define __sanitizer_syscall_pre_tee(fdin, fdout, len, flags) \ + __sanitizer_syscall_pre_impl_tee((long)(fdin), (long)(fdout), (long)(len), \ + (long)(flags)) +#define __sanitizer_syscall_post_tee(res, fdin, fdout, len, flags) \ + __sanitizer_syscall_post_impl_tee(res, (long)(fdin), (long)(fdout), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_pre_get_robust_list(pid, head_ptr, len_ptr) \ + __sanitizer_syscall_pre_impl_get_robust_list((long)(pid), (long)(head_ptr), \ + (long)(len_ptr)) +#define __sanitizer_syscall_post_get_robust_list(res, pid, head_ptr, len_ptr) \ + __sanitizer_syscall_post_impl_get_robust_list( \ + res, (long)(pid), (long)(head_ptr), (long)(len_ptr)) +#define __sanitizer_syscall_pre_set_robust_list(head, len) \ + __sanitizer_syscall_pre_impl_set_robust_list((long)(head), (long)(len)) +#define __sanitizer_syscall_post_set_robust_list(res, head, len) \ + __sanitizer_syscall_post_impl_set_robust_list(res, (long)(head), (long)(len)) +#define __sanitizer_syscall_pre_getcpu(cpu, node, cache) \ + __sanitizer_syscall_pre_impl_getcpu((long)(cpu), (long)(node), (long)(cache)) +#define __sanitizer_syscall_post_getcpu(res, cpu, node, cache) \ + __sanitizer_syscall_post_impl_getcpu(res, (long)(cpu), (long)(node), \ + (long)(cache)) +#define __sanitizer_syscall_pre_signalfd(ufd, user_mask, sizemask) \ + __sanitizer_syscall_pre_impl_signalfd((long)(ufd), (long)(user_mask), \ + (long)(sizemask)) +#define __sanitizer_syscall_post_signalfd(res, ufd, user_mask, sizemask) \ + __sanitizer_syscall_post_impl_signalfd(res, (long)(ufd), (long)(user_mask), \ + (long)(sizemask)) +#define __sanitizer_syscall_pre_signalfd4(ufd, user_mask, sizemask, flags) \ + __sanitizer_syscall_pre_impl_signalfd4((long)(ufd), (long)(user_mask), \ + (long)(sizemask), (long)(flags)) +#define __sanitizer_syscall_post_signalfd4(res, ufd, user_mask, sizemask, \ + flags) \ + __sanitizer_syscall_post_impl_signalfd4(res, (long)(ufd), (long)(user_mask), \ + (long)(sizemask), (long)(flags)) +#define __sanitizer_syscall_pre_timerfd_create(clockid, flags) \ + __sanitizer_syscall_pre_impl_timerfd_create((long)(clockid), (long)(flags)) +#define __sanitizer_syscall_post_timerfd_create(res, clockid, flags) \ + __sanitizer_syscall_post_impl_timerfd_create(res, (long)(clockid), \ + (long)(flags)) +#define __sanitizer_syscall_pre_timerfd_settime(ufd, flags, utmr, otmr) \ + __sanitizer_syscall_pre_impl_timerfd_settime((long)(ufd), (long)(flags), \ + (long)(utmr), (long)(otmr)) +#define __sanitizer_syscall_post_timerfd_settime(res, ufd, flags, utmr, otmr) \ + __sanitizer_syscall_post_impl_timerfd_settime( \ + res, (long)(ufd), (long)(flags), (long)(utmr), (long)(otmr)) +#define __sanitizer_syscall_pre_timerfd_gettime(ufd, otmr) \ + __sanitizer_syscall_pre_impl_timerfd_gettime((long)(ufd), (long)(otmr)) +#define __sanitizer_syscall_post_timerfd_gettime(res, ufd, otmr) \ + __sanitizer_syscall_post_impl_timerfd_gettime(res, (long)(ufd), (long)(otmr)) +#define __sanitizer_syscall_pre_eventfd(count) \ + __sanitizer_syscall_pre_impl_eventfd((long)(count)) +#define __sanitizer_syscall_post_eventfd(res, count) \ + __sanitizer_syscall_post_impl_eventfd(res, (long)(count)) +#define __sanitizer_syscall_pre_eventfd2(count, flags) \ + __sanitizer_syscall_pre_impl_eventfd2((long)(count), (long)(flags)) +#define __sanitizer_syscall_post_eventfd2(res, count, flags) \ + __sanitizer_syscall_post_impl_eventfd2(res, (long)(count), (long)(flags)) +#define __sanitizer_syscall_pre_old_readdir(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_old_readdir((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_old_readdir(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_old_readdir(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_pselect6(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_pselect6((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_pselect6(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_pselect6(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_ppoll(arg0, arg1, arg2, arg3, arg4) \ + __sanitizer_syscall_pre_impl_ppoll((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3), (long)(arg4)) +#define __sanitizer_syscall_post_ppoll(res, arg0, arg1, arg2, arg3, arg4) \ + __sanitizer_syscall_post_impl_ppoll(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4)) +#define __sanitizer_syscall_pre_syncfs(fd) \ + __sanitizer_syscall_pre_impl_syncfs((long)(fd)) +#define __sanitizer_syscall_post_syncfs(res, fd) \ + __sanitizer_syscall_post_impl_syncfs(res, (long)(fd)) +#define __sanitizer_syscall_pre_perf_event_open(attr_uptr, pid, cpu, group_fd, \ + flags) \ + __sanitizer_syscall_pre_impl_perf_event_open((long)(attr_uptr), (long)(pid), \ + (long)(cpu), (long)(group_fd), \ + (long)(flags)) +#define __sanitizer_syscall_post_perf_event_open(res, attr_uptr, pid, cpu, \ + group_fd, flags) \ + __sanitizer_syscall_post_impl_perf_event_open( \ + res, (long)(attr_uptr), (long)(pid), (long)(cpu), (long)(group_fd), \ + (long)(flags)) +#define __sanitizer_syscall_pre_mmap_pgoff(addr, len, prot, flags, fd, pgoff) \ + __sanitizer_syscall_pre_impl_mmap_pgoff((long)(addr), (long)(len), \ + (long)(prot), (long)(flags), \ + (long)(fd), (long)(pgoff)) +#define __sanitizer_syscall_post_mmap_pgoff(res, addr, len, prot, flags, fd, \ + pgoff) \ + __sanitizer_syscall_post_impl_mmap_pgoff(res, (long)(addr), (long)(len), \ + (long)(prot), (long)(flags), \ + (long)(fd), (long)(pgoff)) +#define __sanitizer_syscall_pre_old_mmap(arg) \ + __sanitizer_syscall_pre_impl_old_mmap((long)(arg)) +#define __sanitizer_syscall_post_old_mmap(res, arg) \ + __sanitizer_syscall_post_impl_old_mmap(res, (long)(arg)) +#define __sanitizer_syscall_pre_name_to_handle_at(dfd, name, handle, mnt_id, \ + flag) \ + __sanitizer_syscall_pre_impl_name_to_handle_at( \ + (long)(dfd), (long)(name), (long)(handle), (long)(mnt_id), (long)(flag)) +#define __sanitizer_syscall_post_name_to_handle_at(res, dfd, name, handle, \ + mnt_id, flag) \ + __sanitizer_syscall_post_impl_name_to_handle_at( \ + res, (long)(dfd), (long)(name), (long)(handle), (long)(mnt_id), \ + (long)(flag)) +#define __sanitizer_syscall_pre_open_by_handle_at(mountdirfd, handle, flags) \ + __sanitizer_syscall_pre_impl_open_by_handle_at( \ + (long)(mountdirfd), (long)(handle), (long)(flags)) +#define __sanitizer_syscall_post_open_by_handle_at(res, mountdirfd, handle, \ + flags) \ + __sanitizer_syscall_post_impl_open_by_handle_at( \ + res, (long)(mountdirfd), (long)(handle), (long)(flags)) +#define __sanitizer_syscall_pre_setns(fd, nstype) \ + __sanitizer_syscall_pre_impl_setns((long)(fd), (long)(nstype)) +#define __sanitizer_syscall_post_setns(res, fd, nstype) \ + __sanitizer_syscall_post_impl_setns(res, (long)(fd), (long)(nstype)) +#define __sanitizer_syscall_pre_process_vm_readv(pid, lvec, liovcnt, rvec, \ + riovcnt, flags) \ + __sanitizer_syscall_pre_impl_process_vm_readv( \ + (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_post_process_vm_readv(res, pid, lvec, liovcnt, \ + rvec, riovcnt, flags) \ + __sanitizer_syscall_post_impl_process_vm_readv( \ + res, (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_pre_process_vm_writev(pid, lvec, liovcnt, rvec, \ + riovcnt, flags) \ + __sanitizer_syscall_pre_impl_process_vm_writev( \ + (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_post_process_vm_writev(res, pid, lvec, liovcnt, \ + rvec, riovcnt, flags) \ + __sanitizer_syscall_post_impl_process_vm_writev( \ + res, (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_pre_fork() \ + __sanitizer_syscall_pre_impl_fork() +#define __sanitizer_syscall_post_fork(res) \ + __sanitizer_syscall_post_impl_fork(res) +#define __sanitizer_syscall_pre_vfork() \ + __sanitizer_syscall_pre_impl_vfork() +#define __sanitizer_syscall_post_vfork(res) \ + __sanitizer_syscall_post_impl_vfork(res) + +// And now a few syscalls we don't handle yet. +#define __sanitizer_syscall_pre_afs_syscall(...) +#define __sanitizer_syscall_pre_arch_prctl(...) +#define __sanitizer_syscall_pre_break(...) +#define __sanitizer_syscall_pre_chown32(...) +#define __sanitizer_syscall_pre_clone(...) +#define __sanitizer_syscall_pre_create_module(...) +#define __sanitizer_syscall_pre_epoll_ctl_old(...) +#define __sanitizer_syscall_pre_epoll_wait_old(...) +#define __sanitizer_syscall_pre_execve(...) +#define __sanitizer_syscall_pre_fadvise64(...) +#define __sanitizer_syscall_pre_fadvise64_64(...) +#define __sanitizer_syscall_pre_fallocate(...) +#define __sanitizer_syscall_pre_fanotify_init(...) +#define __sanitizer_syscall_pre_fanotify_mark(...) +#define __sanitizer_syscall_pre_fchown32(...) +#define __sanitizer_syscall_pre_ftime(...) +#define __sanitizer_syscall_pre_ftruncate64(...) +#define __sanitizer_syscall_pre_futex(...) +#define __sanitizer_syscall_pre_getegid32(...) +#define __sanitizer_syscall_pre_geteuid32(...) +#define __sanitizer_syscall_pre_getgid32(...) +#define __sanitizer_syscall_pre_getgroups32(...) +#define __sanitizer_syscall_pre_get_kernel_syms(...) +#define __sanitizer_syscall_pre_getpmsg(...) +#define __sanitizer_syscall_pre_getresgid32(...) +#define __sanitizer_syscall_pre_getresuid32(...) +#define __sanitizer_syscall_pre_get_thread_area(...) +#define __sanitizer_syscall_pre_getuid32(...) +#define __sanitizer_syscall_pre_gtty(...) +#define __sanitizer_syscall_pre_idle(...) +#define __sanitizer_syscall_pre_iopl(...) +#define __sanitizer_syscall_pre_lchown32(...) +#define __sanitizer_syscall_pre__llseek(...) +#define __sanitizer_syscall_pre_lock(...) +#define __sanitizer_syscall_pre_madvise1(...) +#define __sanitizer_syscall_pre_mmap(...) +#define __sanitizer_syscall_pre_mmap2(...) +#define __sanitizer_syscall_pre_modify_ldt(...) +#define __sanitizer_syscall_pre_mpx(...) +#define __sanitizer_syscall_pre__newselect(...) +#define __sanitizer_syscall_pre_nfsservctl(...) +#define __sanitizer_syscall_pre_oldfstat(...) +#define __sanitizer_syscall_pre_oldlstat(...) +#define __sanitizer_syscall_pre_oldolduname(...) +#define __sanitizer_syscall_pre_oldstat(...) +#define __sanitizer_syscall_pre_prctl(...) +#define __sanitizer_syscall_pre_prof(...) +#define __sanitizer_syscall_pre_profil(...) +#define __sanitizer_syscall_pre_putpmsg(...) +#define __sanitizer_syscall_pre_query_module(...) +#define __sanitizer_syscall_pre_readahead(...) +#define __sanitizer_syscall_pre_readdir(...) +#define __sanitizer_syscall_pre_rt_sigaction(...) +#define __sanitizer_syscall_pre_rt_sigreturn(...) +#define __sanitizer_syscall_pre_rt_sigsuspend(...) +#define __sanitizer_syscall_pre_security(...) +#define __sanitizer_syscall_pre_setfsgid32(...) +#define __sanitizer_syscall_pre_setfsuid32(...) +#define __sanitizer_syscall_pre_setgid32(...) +#define __sanitizer_syscall_pre_setgroups32(...) +#define __sanitizer_syscall_pre_setregid32(...) +#define __sanitizer_syscall_pre_setresgid32(...) +#define __sanitizer_syscall_pre_setresuid32(...) +#define __sanitizer_syscall_pre_setreuid32(...) +#define __sanitizer_syscall_pre_set_thread_area(...) +#define __sanitizer_syscall_pre_setuid32(...) +#define __sanitizer_syscall_pre_sigaction(...) +#define __sanitizer_syscall_pre_sigaltstack(...) +#define __sanitizer_syscall_pre_sigreturn(...) +#define __sanitizer_syscall_pre_sigsuspend(...) +#define __sanitizer_syscall_pre_stty(...) +#define __sanitizer_syscall_pre_sync_file_range(...) +#define __sanitizer_syscall_pre__sysctl(...) +#define __sanitizer_syscall_pre_truncate64(...) +#define __sanitizer_syscall_pre_tuxcall(...) +#define __sanitizer_syscall_pre_ugetrlimit(...) +#define __sanitizer_syscall_pre_ulimit(...) +#define __sanitizer_syscall_pre_umount2(...) +#define __sanitizer_syscall_pre_vm86(...) +#define __sanitizer_syscall_pre_vm86old(...) +#define __sanitizer_syscall_pre_vserver(...) + +#define __sanitizer_syscall_post_afs_syscall(res, ...) +#define __sanitizer_syscall_post_arch_prctl(res, ...) +#define __sanitizer_syscall_post_break(res, ...) +#define __sanitizer_syscall_post_chown32(res, ...) +#define __sanitizer_syscall_post_clone(res, ...) +#define __sanitizer_syscall_post_create_module(res, ...) +#define __sanitizer_syscall_post_epoll_ctl_old(res, ...) +#define __sanitizer_syscall_post_epoll_wait_old(res, ...) +#define __sanitizer_syscall_post_execve(res, ...) +#define __sanitizer_syscall_post_fadvise64(res, ...) +#define __sanitizer_syscall_post_fadvise64_64(res, ...) +#define __sanitizer_syscall_post_fallocate(res, ...) +#define __sanitizer_syscall_post_fanotify_init(res, ...) +#define __sanitizer_syscall_post_fanotify_mark(res, ...) +#define __sanitizer_syscall_post_fchown32(res, ...) +#define __sanitizer_syscall_post_ftime(res, ...) +#define __sanitizer_syscall_post_ftruncate64(res, ...) +#define __sanitizer_syscall_post_futex(res, ...) +#define __sanitizer_syscall_post_getegid32(res, ...) +#define __sanitizer_syscall_post_geteuid32(res, ...) +#define __sanitizer_syscall_post_getgid32(res, ...) +#define __sanitizer_syscall_post_getgroups32(res, ...) +#define __sanitizer_syscall_post_get_kernel_syms(res, ...) +#define __sanitizer_syscall_post_getpmsg(res, ...) +#define __sanitizer_syscall_post_getresgid32(res, ...) +#define __sanitizer_syscall_post_getresuid32(res, ...) +#define __sanitizer_syscall_post_get_thread_area(res, ...) +#define __sanitizer_syscall_post_getuid32(res, ...) +#define __sanitizer_syscall_post_gtty(res, ...) +#define __sanitizer_syscall_post_idle(res, ...) +#define __sanitizer_syscall_post_iopl(res, ...) +#define __sanitizer_syscall_post_lchown32(res, ...) +#define __sanitizer_syscall_post__llseek(res, ...) +#define __sanitizer_syscall_post_lock(res, ...) +#define __sanitizer_syscall_post_madvise1(res, ...) +#define __sanitizer_syscall_post_mmap2(res, ...) +#define __sanitizer_syscall_post_mmap(res, ...) +#define __sanitizer_syscall_post_modify_ldt(res, ...) +#define __sanitizer_syscall_post_mpx(res, ...) +#define __sanitizer_syscall_post__newselect(res, ...) +#define __sanitizer_syscall_post_nfsservctl(res, ...) +#define __sanitizer_syscall_post_oldfstat(res, ...) +#define __sanitizer_syscall_post_oldlstat(res, ...) +#define __sanitizer_syscall_post_oldolduname(res, ...) +#define __sanitizer_syscall_post_oldstat(res, ...) +#define __sanitizer_syscall_post_prctl(res, ...) +#define __sanitizer_syscall_post_profil(res, ...) +#define __sanitizer_syscall_post_prof(res, ...) +#define __sanitizer_syscall_post_putpmsg(res, ...) +#define __sanitizer_syscall_post_query_module(res, ...) +#define __sanitizer_syscall_post_readahead(res, ...) +#define __sanitizer_syscall_post_readdir(res, ...) +#define __sanitizer_syscall_post_rt_sigaction(res, ...) +#define __sanitizer_syscall_post_rt_sigreturn(res, ...) +#define __sanitizer_syscall_post_rt_sigsuspend(res, ...) +#define __sanitizer_syscall_post_security(res, ...) +#define __sanitizer_syscall_post_setfsgid32(res, ...) +#define __sanitizer_syscall_post_setfsuid32(res, ...) +#define __sanitizer_syscall_post_setgid32(res, ...) +#define __sanitizer_syscall_post_setgroups32(res, ...) +#define __sanitizer_syscall_post_setregid32(res, ...) +#define __sanitizer_syscall_post_setresgid32(res, ...) +#define __sanitizer_syscall_post_setresuid32(res, ...) +#define __sanitizer_syscall_post_setreuid32(res, ...) +#define __sanitizer_syscall_post_set_thread_area(res, ...) +#define __sanitizer_syscall_post_setuid32(res, ...) +#define __sanitizer_syscall_post_sigaction(res, ...) +#define __sanitizer_syscall_post_sigaltstack(res, ...) +#define __sanitizer_syscall_post_sigreturn(res, ...) +#define __sanitizer_syscall_post_sigsuspend(res, ...) +#define __sanitizer_syscall_post_stty(res, ...) +#define __sanitizer_syscall_post_sync_file_range(res, ...) +#define __sanitizer_syscall_post__sysctl(res, ...) +#define __sanitizer_syscall_post_truncate64(res, ...) +#define __sanitizer_syscall_post_tuxcall(res, ...) +#define __sanitizer_syscall_post_ugetrlimit(res, ...) +#define __sanitizer_syscall_post_ulimit(res, ...) +#define __sanitizer_syscall_post_umount2(res, ...) +#define __sanitizer_syscall_post_vm86old(res, ...) +#define __sanitizer_syscall_post_vm86(res, ...) +#define __sanitizer_syscall_post_vserver(res, ...) + +#ifdef __cplusplus +extern "C" { +#endif + +// Private declarations. Do not call directly from user code. Use macros above. +void __sanitizer_syscall_pre_impl_time(long tloc); +void __sanitizer_syscall_post_impl_time(long res, long tloc); +void __sanitizer_syscall_pre_impl_stime(long tptr); +void __sanitizer_syscall_post_impl_stime(long res, long tptr); +void __sanitizer_syscall_pre_impl_gettimeofday(long tv, long tz); +void __sanitizer_syscall_post_impl_gettimeofday(long res, long tv, long tz); +void __sanitizer_syscall_pre_impl_settimeofday(long tv, long tz); +void __sanitizer_syscall_post_impl_settimeofday(long res, long tv, long tz); +void __sanitizer_syscall_pre_impl_adjtimex(long txc_p); +void __sanitizer_syscall_post_impl_adjtimex(long res, long txc_p); +void __sanitizer_syscall_pre_impl_times(long tbuf); +void __sanitizer_syscall_post_impl_times(long res, long tbuf); +void __sanitizer_syscall_pre_impl_gettid(); +void __sanitizer_syscall_post_impl_gettid(long res); +void __sanitizer_syscall_pre_impl_nanosleep(long rqtp, long rmtp); +void __sanitizer_syscall_post_impl_nanosleep(long res, long rqtp, long rmtp); +void __sanitizer_syscall_pre_impl_alarm(long seconds); +void __sanitizer_syscall_post_impl_alarm(long res, long seconds); +void __sanitizer_syscall_pre_impl_getpid(); +void __sanitizer_syscall_post_impl_getpid(long res); +void __sanitizer_syscall_pre_impl_getppid(); +void __sanitizer_syscall_post_impl_getppid(long res); +void __sanitizer_syscall_pre_impl_getuid(); +void __sanitizer_syscall_post_impl_getuid(long res); +void __sanitizer_syscall_pre_impl_geteuid(); +void __sanitizer_syscall_post_impl_geteuid(long res); +void __sanitizer_syscall_pre_impl_getgid(); +void __sanitizer_syscall_post_impl_getgid(long res); +void __sanitizer_syscall_pre_impl_getegid(); +void __sanitizer_syscall_post_impl_getegid(long res); +void __sanitizer_syscall_pre_impl_getresuid(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_getresuid(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_getresgid(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_getresgid(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_getpgid(long pid); +void __sanitizer_syscall_post_impl_getpgid(long res, long pid); +void __sanitizer_syscall_pre_impl_getpgrp(); +void __sanitizer_syscall_post_impl_getpgrp(long res); +void __sanitizer_syscall_pre_impl_getsid(long pid); +void __sanitizer_syscall_post_impl_getsid(long res, long pid); +void __sanitizer_syscall_pre_impl_getgroups(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_getgroups(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_setregid(long rgid, long egid); +void __sanitizer_syscall_post_impl_setregid(long res, long rgid, long egid); +void __sanitizer_syscall_pre_impl_setgid(long gid); +void __sanitizer_syscall_post_impl_setgid(long res, long gid); +void __sanitizer_syscall_pre_impl_setreuid(long ruid, long euid); +void __sanitizer_syscall_post_impl_setreuid(long res, long ruid, long euid); +void __sanitizer_syscall_pre_impl_setuid(long uid); +void __sanitizer_syscall_post_impl_setuid(long res, long uid); +void __sanitizer_syscall_pre_impl_setresuid(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_setresuid(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_setresgid(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_setresgid(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_setfsuid(long uid); +void __sanitizer_syscall_post_impl_setfsuid(long res, long uid); +void __sanitizer_syscall_pre_impl_setfsgid(long gid); +void __sanitizer_syscall_post_impl_setfsgid(long res, long gid); +void __sanitizer_syscall_pre_impl_setpgid(long pid, long pgid); +void __sanitizer_syscall_post_impl_setpgid(long res, long pid, long pgid); +void __sanitizer_syscall_pre_impl_setsid(); +void __sanitizer_syscall_post_impl_setsid(long res); +void __sanitizer_syscall_pre_impl_setgroups(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_setgroups(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_acct(long name); +void __sanitizer_syscall_post_impl_acct(long res, long name); +void __sanitizer_syscall_pre_impl_capget(long header, long dataptr); +void __sanitizer_syscall_post_impl_capget(long res, long header, long dataptr); +void __sanitizer_syscall_pre_impl_capset(long header, long data); +void __sanitizer_syscall_post_impl_capset(long res, long header, long data); +void __sanitizer_syscall_pre_impl_personality(long personality); +void __sanitizer_syscall_post_impl_personality(long res, long personality); +void __sanitizer_syscall_pre_impl_sigpending(long set); +void __sanitizer_syscall_post_impl_sigpending(long res, long set); +void __sanitizer_syscall_pre_impl_sigprocmask(long how, long set, long oset); +void __sanitizer_syscall_post_impl_sigprocmask(long res, long how, long set, + long oset); +void __sanitizer_syscall_pre_impl_getitimer(long which, long value); +void __sanitizer_syscall_post_impl_getitimer(long res, long which, long value); +void __sanitizer_syscall_pre_impl_setitimer(long which, long value, + long ovalue); +void __sanitizer_syscall_post_impl_setitimer(long res, long which, long value, + long ovalue); +void __sanitizer_syscall_pre_impl_timer_create(long which_clock, + long timer_event_spec, + long created_timer_id); +void __sanitizer_syscall_post_impl_timer_create(long res, long which_clock, + long timer_event_spec, + long created_timer_id); +void __sanitizer_syscall_pre_impl_timer_gettime(long timer_id, long setting); +void __sanitizer_syscall_post_impl_timer_gettime(long res, long timer_id, + long setting); +void __sanitizer_syscall_pre_impl_timer_getoverrun(long timer_id); +void __sanitizer_syscall_post_impl_timer_getoverrun(long res, long timer_id); +void __sanitizer_syscall_pre_impl_timer_settime(long timer_id, long flags, + long new_setting, + long old_setting); +void __sanitizer_syscall_post_impl_timer_settime(long res, long timer_id, + long flags, long new_setting, + long old_setting); +void __sanitizer_syscall_pre_impl_timer_delete(long timer_id); +void __sanitizer_syscall_post_impl_timer_delete(long res, long timer_id); +void __sanitizer_syscall_pre_impl_clock_settime(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_settime(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_gettime(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_gettime(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_adjtime(long which_clock, long tx); +void __sanitizer_syscall_post_impl_clock_adjtime(long res, long which_clock, + long tx); +void __sanitizer_syscall_pre_impl_clock_getres(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_getres(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_nanosleep(long which_clock, long flags, + long rqtp, long rmtp); +void __sanitizer_syscall_post_impl_clock_nanosleep(long res, long which_clock, + long flags, long rqtp, + long rmtp); +void __sanitizer_syscall_pre_impl_nice(long increment); +void __sanitizer_syscall_post_impl_nice(long res, long increment); +void __sanitizer_syscall_pre_impl_sched_setscheduler(long pid, long policy, + long param); +void __sanitizer_syscall_post_impl_sched_setscheduler(long res, long pid, + long policy, long param); +void __sanitizer_syscall_pre_impl_sched_setparam(long pid, long param); +void __sanitizer_syscall_post_impl_sched_setparam(long res, long pid, + long param); +void __sanitizer_syscall_pre_impl_sched_getscheduler(long pid); +void __sanitizer_syscall_post_impl_sched_getscheduler(long res, long pid); +void __sanitizer_syscall_pre_impl_sched_getparam(long pid, long param); +void __sanitizer_syscall_post_impl_sched_getparam(long res, long pid, + long param); +void __sanitizer_syscall_pre_impl_sched_setaffinity(long pid, long len, + long user_mask_ptr); +void __sanitizer_syscall_post_impl_sched_setaffinity(long res, long pid, + long len, + long user_mask_ptr); +void __sanitizer_syscall_pre_impl_sched_getaffinity(long pid, long len, + long user_mask_ptr); +void __sanitizer_syscall_post_impl_sched_getaffinity(long res, long pid, + long len, + long user_mask_ptr); +void __sanitizer_syscall_pre_impl_sched_yield(); +void __sanitizer_syscall_post_impl_sched_yield(long res); +void __sanitizer_syscall_pre_impl_sched_get_priority_max(long policy); +void __sanitizer_syscall_post_impl_sched_get_priority_max(long res, + long policy); +void __sanitizer_syscall_pre_impl_sched_get_priority_min(long policy); +void __sanitizer_syscall_post_impl_sched_get_priority_min(long res, + long policy); +void __sanitizer_syscall_pre_impl_sched_rr_get_interval(long pid, + long interval); +void __sanitizer_syscall_post_impl_sched_rr_get_interval(long res, long pid, + long interval); +void __sanitizer_syscall_pre_impl_setpriority(long which, long who, + long niceval); +void __sanitizer_syscall_post_impl_setpriority(long res, long which, long who, + long niceval); +void __sanitizer_syscall_pre_impl_getpriority(long which, long who); +void __sanitizer_syscall_post_impl_getpriority(long res, long which, long who); +void __sanitizer_syscall_pre_impl_shutdown(long arg0, long arg1); +void __sanitizer_syscall_post_impl_shutdown(long res, long arg0, long arg1); +void __sanitizer_syscall_pre_impl_reboot(long magic1, long magic2, long cmd, + long arg); +void __sanitizer_syscall_post_impl_reboot(long res, long magic1, long magic2, + long cmd, long arg); +void __sanitizer_syscall_pre_impl_restart_syscall(); +void __sanitizer_syscall_post_impl_restart_syscall(long res); +void __sanitizer_syscall_pre_impl_kexec_load(long entry, long nr_segments, + long segments, long flags); +void __sanitizer_syscall_post_impl_kexec_load(long res, long entry, + long nr_segments, long segments, + long flags); +void __sanitizer_syscall_pre_impl_exit(long error_code); +void __sanitizer_syscall_post_impl_exit(long res, long error_code); +void __sanitizer_syscall_pre_impl_exit_group(long error_code); +void __sanitizer_syscall_post_impl_exit_group(long res, long error_code); +void __sanitizer_syscall_pre_impl_wait4(long pid, long stat_addr, long options, + long ru); +void __sanitizer_syscall_post_impl_wait4(long res, long pid, long stat_addr, + long options, long ru); +void __sanitizer_syscall_pre_impl_waitid(long which, long pid, long infop, + long options, long ru); +void __sanitizer_syscall_post_impl_waitid(long res, long which, long pid, + long infop, long options, long ru); +void __sanitizer_syscall_pre_impl_waitpid(long pid, long stat_addr, + long options); +void __sanitizer_syscall_post_impl_waitpid(long res, long pid, long stat_addr, + long options); +void __sanitizer_syscall_pre_impl_set_tid_address(long tidptr); +void __sanitizer_syscall_post_impl_set_tid_address(long res, long tidptr); +void __sanitizer_syscall_pre_impl_init_module(long umod, long len, long uargs); +void __sanitizer_syscall_post_impl_init_module(long res, long umod, long len, + long uargs); +void __sanitizer_syscall_pre_impl_delete_module(long name_user, long flags); +void __sanitizer_syscall_post_impl_delete_module(long res, long name_user, + long flags); +void __sanitizer_syscall_pre_impl_rt_sigprocmask(long how, long set, long oset, + long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigprocmask(long res, long how, long set, + long oset, long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_sigpending(long set, long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigpending(long res, long set, + long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_sigtimedwait(long uthese, long uinfo, + long uts, long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigtimedwait(long res, long uthese, + long uinfo, long uts, + long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_tgsigqueueinfo(long tgid, long pid, + long sig, long uinfo); +void __sanitizer_syscall_post_impl_rt_tgsigqueueinfo(long res, long tgid, + long pid, long sig, + long uinfo); +void __sanitizer_syscall_pre_impl_kill(long pid, long sig); +void __sanitizer_syscall_post_impl_kill(long res, long pid, long sig); +void __sanitizer_syscall_pre_impl_tgkill(long tgid, long pid, long sig); +void __sanitizer_syscall_post_impl_tgkill(long res, long tgid, long pid, + long sig); +void __sanitizer_syscall_pre_impl_tkill(long pid, long sig); +void __sanitizer_syscall_post_impl_tkill(long res, long pid, long sig); +void __sanitizer_syscall_pre_impl_rt_sigqueueinfo(long pid, long sig, + long uinfo); +void __sanitizer_syscall_post_impl_rt_sigqueueinfo(long res, long pid, long sig, + long uinfo); +void __sanitizer_syscall_pre_impl_sgetmask(); +void __sanitizer_syscall_post_impl_sgetmask(long res); +void __sanitizer_syscall_pre_impl_ssetmask(long newmask); +void __sanitizer_syscall_post_impl_ssetmask(long res, long newmask); +void __sanitizer_syscall_pre_impl_signal(long sig, long handler); +void __sanitizer_syscall_post_impl_signal(long res, long sig, long handler); +void __sanitizer_syscall_pre_impl_pause(); +void __sanitizer_syscall_post_impl_pause(long res); +void __sanitizer_syscall_pre_impl_sync(); +void __sanitizer_syscall_post_impl_sync(long res); +void __sanitizer_syscall_pre_impl_fsync(long fd); +void __sanitizer_syscall_post_impl_fsync(long res, long fd); +void __sanitizer_syscall_pre_impl_fdatasync(long fd); +void __sanitizer_syscall_post_impl_fdatasync(long res, long fd); +void __sanitizer_syscall_pre_impl_bdflush(long func, long data); +void __sanitizer_syscall_post_impl_bdflush(long res, long func, long data); +void __sanitizer_syscall_pre_impl_mount(long dev_name, long dir_name, long type, + long flags, long data); +void __sanitizer_syscall_post_impl_mount(long res, long dev_name, long dir_name, + long type, long flags, long data); +void __sanitizer_syscall_pre_impl_umount(long name, long flags); +void __sanitizer_syscall_post_impl_umount(long res, long name, long flags); +void __sanitizer_syscall_pre_impl_oldumount(long name); +void __sanitizer_syscall_post_impl_oldumount(long res, long name); +void __sanitizer_syscall_pre_impl_truncate(long path, long length); +void __sanitizer_syscall_post_impl_truncate(long res, long path, long length); +void __sanitizer_syscall_pre_impl_ftruncate(long fd, long length); +void __sanitizer_syscall_post_impl_ftruncate(long res, long fd, long length); +void __sanitizer_syscall_pre_impl_stat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_stat(long res, long filename, long statbuf); +void __sanitizer_syscall_pre_impl_statfs(long path, long buf); +void __sanitizer_syscall_post_impl_statfs(long res, long path, long buf); +void __sanitizer_syscall_pre_impl_statfs64(long path, long sz, long buf); +void __sanitizer_syscall_post_impl_statfs64(long res, long path, long sz, + long buf); +void __sanitizer_syscall_pre_impl_fstatfs(long fd, long buf); +void __sanitizer_syscall_post_impl_fstatfs(long res, long fd, long buf); +void __sanitizer_syscall_pre_impl_fstatfs64(long fd, long sz, long buf); +void __sanitizer_syscall_post_impl_fstatfs64(long res, long fd, long sz, + long buf); +void __sanitizer_syscall_pre_impl_lstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_lstat(long res, long filename, long statbuf); +void __sanitizer_syscall_pre_impl_fstat(long fd, long statbuf); +void __sanitizer_syscall_post_impl_fstat(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_newstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_newstat(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_newlstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_newlstat(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_newfstat(long fd, long statbuf); +void __sanitizer_syscall_post_impl_newfstat(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_ustat(long dev, long ubuf); +void __sanitizer_syscall_post_impl_ustat(long res, long dev, long ubuf); +void __sanitizer_syscall_pre_impl_stat64(long filename, long statbuf); +void __sanitizer_syscall_post_impl_stat64(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_fstat64(long fd, long statbuf); +void __sanitizer_syscall_post_impl_fstat64(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_lstat64(long filename, long statbuf); +void __sanitizer_syscall_post_impl_lstat64(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_setxattr(long path, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_setxattr(long res, long path, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_lsetxattr(long path, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_lsetxattr(long res, long path, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_fsetxattr(long fd, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_fsetxattr(long res, long fd, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_getxattr(long path, long name, long value, + long size); +void __sanitizer_syscall_post_impl_getxattr(long res, long path, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_lgetxattr(long path, long name, long value, + long size); +void __sanitizer_syscall_post_impl_lgetxattr(long res, long path, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_fgetxattr(long fd, long name, long value, + long size); +void __sanitizer_syscall_post_impl_fgetxattr(long res, long fd, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_listxattr(long path, long list, long size); +void __sanitizer_syscall_post_impl_listxattr(long res, long path, long list, + long size); +void __sanitizer_syscall_pre_impl_llistxattr(long path, long list, long size); +void __sanitizer_syscall_post_impl_llistxattr(long res, long path, long list, + long size); +void __sanitizer_syscall_pre_impl_flistxattr(long fd, long list, long size); +void __sanitizer_syscall_post_impl_flistxattr(long res, long fd, long list, + long size); +void __sanitizer_syscall_pre_impl_removexattr(long path, long name); +void __sanitizer_syscall_post_impl_removexattr(long res, long path, long name); +void __sanitizer_syscall_pre_impl_lremovexattr(long path, long name); +void __sanitizer_syscall_post_impl_lremovexattr(long res, long path, long name); +void __sanitizer_syscall_pre_impl_fremovexattr(long fd, long name); +void __sanitizer_syscall_post_impl_fremovexattr(long res, long fd, long name); +void __sanitizer_syscall_pre_impl_brk(long brk); +void __sanitizer_syscall_post_impl_brk(long res, long brk); +void __sanitizer_syscall_pre_impl_mprotect(long start, long len, long prot); +void __sanitizer_syscall_post_impl_mprotect(long res, long start, long len, + long prot); +void __sanitizer_syscall_pre_impl_mremap(long addr, long old_len, long new_len, + long flags, long new_addr); +void __sanitizer_syscall_post_impl_mremap(long res, long addr, long old_len, + long new_len, long flags, + long new_addr); +void __sanitizer_syscall_pre_impl_remap_file_pages(long start, long size, + long prot, long pgoff, + long flags); +void __sanitizer_syscall_post_impl_remap_file_pages(long res, long start, + long size, long prot, + long pgoff, long flags); +void __sanitizer_syscall_pre_impl_msync(long start, long len, long flags); +void __sanitizer_syscall_post_impl_msync(long res, long start, long len, + long flags); +void __sanitizer_syscall_pre_impl_munmap(long addr, long len); +void __sanitizer_syscall_post_impl_munmap(long res, long addr, long len); +void __sanitizer_syscall_pre_impl_mlock(long start, long len); +void __sanitizer_syscall_post_impl_mlock(long res, long start, long len); +void __sanitizer_syscall_pre_impl_munlock(long start, long len); +void __sanitizer_syscall_post_impl_munlock(long res, long start, long len); +void __sanitizer_syscall_pre_impl_mlockall(long flags); +void __sanitizer_syscall_post_impl_mlockall(long res, long flags); +void __sanitizer_syscall_pre_impl_munlockall(); +void __sanitizer_syscall_post_impl_munlockall(long res); +void __sanitizer_syscall_pre_impl_madvise(long start, long len, long behavior); +void __sanitizer_syscall_post_impl_madvise(long res, long start, long len, + long behavior); +void __sanitizer_syscall_pre_impl_mincore(long start, long len, long vec); +void __sanitizer_syscall_post_impl_mincore(long res, long start, long len, + long vec); +void __sanitizer_syscall_pre_impl_pivot_root(long new_root, long put_old); +void __sanitizer_syscall_post_impl_pivot_root(long res, long new_root, + long put_old); +void __sanitizer_syscall_pre_impl_chroot(long filename); +void __sanitizer_syscall_post_impl_chroot(long res, long filename); +void __sanitizer_syscall_pre_impl_mknod(long filename, long mode, long dev); +void __sanitizer_syscall_post_impl_mknod(long res, long filename, long mode, + long dev); +void __sanitizer_syscall_pre_impl_link(long oldname, long newname); +void __sanitizer_syscall_post_impl_link(long res, long oldname, long newname); +void __sanitizer_syscall_pre_impl_symlink(long old, long new_); +void __sanitizer_syscall_post_impl_symlink(long res, long old, long new_); +void __sanitizer_syscall_pre_impl_unlink(long pathname); +void __sanitizer_syscall_post_impl_unlink(long res, long pathname); +void __sanitizer_syscall_pre_impl_rename(long oldname, long newname); +void __sanitizer_syscall_post_impl_rename(long res, long oldname, long newname); +void __sanitizer_syscall_pre_impl_chmod(long filename, long mode); +void __sanitizer_syscall_post_impl_chmod(long res, long filename, long mode); +void __sanitizer_syscall_pre_impl_fchmod(long fd, long mode); +void __sanitizer_syscall_post_impl_fchmod(long res, long fd, long mode); +void __sanitizer_syscall_pre_impl_fcntl(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_fcntl(long res, long fd, long cmd, long arg); +void __sanitizer_syscall_pre_impl_fcntl64(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_fcntl64(long res, long fd, long cmd, + long arg); +void __sanitizer_syscall_pre_impl_pipe(long fildes); +void __sanitizer_syscall_post_impl_pipe(long res, long fildes); +void __sanitizer_syscall_pre_impl_pipe2(long fildes, long flags); +void __sanitizer_syscall_post_impl_pipe2(long res, long fildes, long flags); +void __sanitizer_syscall_pre_impl_dup(long fildes); +void __sanitizer_syscall_post_impl_dup(long res, long fildes); +void __sanitizer_syscall_pre_impl_dup2(long oldfd, long newfd); +void __sanitizer_syscall_post_impl_dup2(long res, long oldfd, long newfd); +void __sanitizer_syscall_pre_impl_dup3(long oldfd, long newfd, long flags); +void __sanitizer_syscall_post_impl_dup3(long res, long oldfd, long newfd, + long flags); +void __sanitizer_syscall_pre_impl_ioperm(long from, long num, long on); +void __sanitizer_syscall_post_impl_ioperm(long res, long from, long num, + long on); +void __sanitizer_syscall_pre_impl_ioctl(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_ioctl(long res, long fd, long cmd, long arg); +void __sanitizer_syscall_pre_impl_flock(long fd, long cmd); +void __sanitizer_syscall_post_impl_flock(long res, long fd, long cmd); +void __sanitizer_syscall_pre_impl_io_setup(long nr_reqs, long ctx); +void __sanitizer_syscall_post_impl_io_setup(long res, long nr_reqs, long ctx); +void __sanitizer_syscall_pre_impl_io_destroy(long ctx); +void __sanitizer_syscall_post_impl_io_destroy(long res, long ctx); +void __sanitizer_syscall_pre_impl_io_getevents(long ctx_id, long min_nr, + long nr, long events, + long timeout); +void __sanitizer_syscall_post_impl_io_getevents(long res, long ctx_id, + long min_nr, long nr, + long events, long timeout); +void __sanitizer_syscall_pre_impl_io_submit(long ctx_id, long arg1, long arg2); +void __sanitizer_syscall_post_impl_io_submit(long res, long ctx_id, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_io_cancel(long ctx_id, long iocb, + long result); +void __sanitizer_syscall_post_impl_io_cancel(long res, long ctx_id, long iocb, + long result); +void __sanitizer_syscall_pre_impl_sendfile(long out_fd, long in_fd, long offset, + long count); +void __sanitizer_syscall_post_impl_sendfile(long res, long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_pre_impl_sendfile64(long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_post_impl_sendfile64(long res, long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_pre_impl_readlink(long path, long buf, long bufsiz); +void __sanitizer_syscall_post_impl_readlink(long res, long path, long buf, + long bufsiz); +void __sanitizer_syscall_pre_impl_creat(long pathname, long mode); +void __sanitizer_syscall_post_impl_creat(long res, long pathname, long mode); +void __sanitizer_syscall_pre_impl_open(long filename, long flags, long mode); +void __sanitizer_syscall_post_impl_open(long res, long filename, long flags, + long mode); +void __sanitizer_syscall_pre_impl_close(long fd); +void __sanitizer_syscall_post_impl_close(long res, long fd); +void __sanitizer_syscall_pre_impl_access(long filename, long mode); +void __sanitizer_syscall_post_impl_access(long res, long filename, long mode); +void __sanitizer_syscall_pre_impl_vhangup(); +void __sanitizer_syscall_post_impl_vhangup(long res); +void __sanitizer_syscall_pre_impl_chown(long filename, long user, long group); +void __sanitizer_syscall_post_impl_chown(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_lchown(long filename, long user, long group); +void __sanitizer_syscall_post_impl_lchown(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_fchown(long fd, long user, long group); +void __sanitizer_syscall_post_impl_fchown(long res, long fd, long user, + long group); +void __sanitizer_syscall_pre_impl_chown16(long filename, long user, long group); +void __sanitizer_syscall_post_impl_chown16(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_lchown16(long filename, long user, + long group); +void __sanitizer_syscall_post_impl_lchown16(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_fchown16(long fd, long user, long group); +void __sanitizer_syscall_post_impl_fchown16(long res, long fd, long user, + long group); +void __sanitizer_syscall_pre_impl_setregid16(long rgid, long egid); +void __sanitizer_syscall_post_impl_setregid16(long res, long rgid, long egid); +void __sanitizer_syscall_pre_impl_setgid16(long gid); +void __sanitizer_syscall_post_impl_setgid16(long res, long gid); +void __sanitizer_syscall_pre_impl_setreuid16(long ruid, long euid); +void __sanitizer_syscall_post_impl_setreuid16(long res, long ruid, long euid); +void __sanitizer_syscall_pre_impl_setuid16(long uid); +void __sanitizer_syscall_post_impl_setuid16(long res, long uid); +void __sanitizer_syscall_pre_impl_setresuid16(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_setresuid16(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_getresuid16(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_getresuid16(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_setresgid16(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_setresgid16(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_getresgid16(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_getresgid16(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_setfsuid16(long uid); +void __sanitizer_syscall_post_impl_setfsuid16(long res, long uid); +void __sanitizer_syscall_pre_impl_setfsgid16(long gid); +void __sanitizer_syscall_post_impl_setfsgid16(long res, long gid); +void __sanitizer_syscall_pre_impl_getgroups16(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_getgroups16(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_setgroups16(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_setgroups16(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_getuid16(); +void __sanitizer_syscall_post_impl_getuid16(long res); +void __sanitizer_syscall_pre_impl_geteuid16(); +void __sanitizer_syscall_post_impl_geteuid16(long res); +void __sanitizer_syscall_pre_impl_getgid16(); +void __sanitizer_syscall_post_impl_getgid16(long res); +void __sanitizer_syscall_pre_impl_getegid16(); +void __sanitizer_syscall_post_impl_getegid16(long res); +void __sanitizer_syscall_pre_impl_utime(long filename, long times); +void __sanitizer_syscall_post_impl_utime(long res, long filename, long times); +void __sanitizer_syscall_pre_impl_utimes(long filename, long utimes); +void __sanitizer_syscall_post_impl_utimes(long res, long filename, long utimes); +void __sanitizer_syscall_pre_impl_lseek(long fd, long offset, long origin); +void __sanitizer_syscall_post_impl_lseek(long res, long fd, long offset, + long origin); +void __sanitizer_syscall_pre_impl_llseek(long fd, long offset_high, + long offset_low, long result, + long origin); +void __sanitizer_syscall_post_impl_llseek(long res, long fd, long offset_high, + long offset_low, long result, + long origin); +void __sanitizer_syscall_pre_impl_read(long fd, long buf, long count); +void __sanitizer_syscall_post_impl_read(long res, long fd, long buf, + long count); +void __sanitizer_syscall_pre_impl_readv(long fd, long vec, long vlen); +void __sanitizer_syscall_post_impl_readv(long res, long fd, long vec, + long vlen); +void __sanitizer_syscall_pre_impl_write(long fd, long buf, long count); +void __sanitizer_syscall_post_impl_write(long res, long fd, long buf, + long count); +void __sanitizer_syscall_pre_impl_writev(long fd, long vec, long vlen); +void __sanitizer_syscall_post_impl_writev(long res, long fd, long vec, + long vlen); + +#ifdef _LP64 +void __sanitizer_syscall_pre_impl_pread64(long fd, long buf, long count, + long pos); +void __sanitizer_syscall_post_impl_pread64(long res, long fd, long buf, + long count, long pos); +void __sanitizer_syscall_pre_impl_pwrite64(long fd, long buf, long count, + long pos); +void __sanitizer_syscall_post_impl_pwrite64(long res, long fd, long buf, + long count, long pos); +#else +void __sanitizer_syscall_pre_impl_pread64(long fd, long buf, long count, + long pos0, long pos1); +void __sanitizer_syscall_post_impl_pread64(long res, long fd, long buf, + long count, long pos0, long pos1); +void __sanitizer_syscall_pre_impl_pwrite64(long fd, long buf, long count, + long pos0, long pos1); +void __sanitizer_syscall_post_impl_pwrite64(long res, long fd, long buf, + long count, long pos0, long pos1); +#endif + +void __sanitizer_syscall_pre_impl_preadv(long fd, long vec, long vlen, + long pos_l, long pos_h); +void __sanitizer_syscall_post_impl_preadv(long res, long fd, long vec, + long vlen, long pos_l, long pos_h); +void __sanitizer_syscall_pre_impl_pwritev(long fd, long vec, long vlen, + long pos_l, long pos_h); +void __sanitizer_syscall_post_impl_pwritev(long res, long fd, long vec, + long vlen, long pos_l, long pos_h); +void __sanitizer_syscall_pre_impl_getcwd(long buf, long size); +void __sanitizer_syscall_post_impl_getcwd(long res, long buf, long size); +void __sanitizer_syscall_pre_impl_mkdir(long pathname, long mode); +void __sanitizer_syscall_post_impl_mkdir(long res, long pathname, long mode); +void __sanitizer_syscall_pre_impl_chdir(long filename); +void __sanitizer_syscall_post_impl_chdir(long res, long filename); +void __sanitizer_syscall_pre_impl_fchdir(long fd); +void __sanitizer_syscall_post_impl_fchdir(long res, long fd); +void __sanitizer_syscall_pre_impl_rmdir(long pathname); +void __sanitizer_syscall_post_impl_rmdir(long res, long pathname); +void __sanitizer_syscall_pre_impl_lookup_dcookie(long cookie64, long buf, + long len); +void __sanitizer_syscall_post_impl_lookup_dcookie(long res, long cookie64, + long buf, long len); +void __sanitizer_syscall_pre_impl_quotactl(long cmd, long special, long id, + long addr); +void __sanitizer_syscall_post_impl_quotactl(long res, long cmd, long special, + long id, long addr); +void __sanitizer_syscall_pre_impl_getdents(long fd, long dirent, long count); +void __sanitizer_syscall_post_impl_getdents(long res, long fd, long dirent, + long count); +void __sanitizer_syscall_pre_impl_getdents64(long fd, long dirent, long count); +void __sanitizer_syscall_post_impl_getdents64(long res, long fd, long dirent, + long count); +void __sanitizer_syscall_pre_impl_setsockopt(long fd, long level, long optname, + long optval, long optlen); +void __sanitizer_syscall_post_impl_setsockopt(long res, long fd, long level, + long optname, long optval, + long optlen); +void __sanitizer_syscall_pre_impl_getsockopt(long fd, long level, long optname, + long optval, long optlen); +void __sanitizer_syscall_post_impl_getsockopt(long res, long fd, long level, + long optname, long optval, + long optlen); +void __sanitizer_syscall_pre_impl_bind(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_bind(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_connect(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_connect(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_accept(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_accept(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_accept4(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_accept4(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_getsockname(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_getsockname(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_getpeername(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_getpeername(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_send(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_send(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_sendto(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_sendto(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_sendmsg(long fd, long msg, long flags); +void __sanitizer_syscall_post_impl_sendmsg(long res, long fd, long msg, + long flags); +void __sanitizer_syscall_pre_impl_sendmmsg(long fd, long msg, long vlen, + long flags); +void __sanitizer_syscall_post_impl_sendmmsg(long res, long fd, long msg, + long vlen, long flags); +void __sanitizer_syscall_pre_impl_recv(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_recv(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_recvfrom(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_recvfrom(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_recvmsg(long fd, long msg, long flags); +void __sanitizer_syscall_post_impl_recvmsg(long res, long fd, long msg, + long flags); +void __sanitizer_syscall_pre_impl_recvmmsg(long fd, long msg, long vlen, + long flags, long timeout); +void __sanitizer_syscall_post_impl_recvmmsg(long res, long fd, long msg, + long vlen, long flags, + long timeout); +void __sanitizer_syscall_pre_impl_socket(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_socket(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_socketpair(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_socketpair(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_socketcall(long call, long args); +void __sanitizer_syscall_post_impl_socketcall(long res, long call, long args); +void __sanitizer_syscall_pre_impl_listen(long arg0, long arg1); +void __sanitizer_syscall_post_impl_listen(long res, long arg0, long arg1); +void __sanitizer_syscall_pre_impl_poll(long ufds, long nfds, long timeout); +void __sanitizer_syscall_post_impl_poll(long res, long ufds, long nfds, + long timeout); +void __sanitizer_syscall_pre_impl_select(long n, long inp, long outp, long exp, + long tvp); +void __sanitizer_syscall_post_impl_select(long res, long n, long inp, long outp, + long exp, long tvp); +void __sanitizer_syscall_pre_impl_old_select(long arg); +void __sanitizer_syscall_post_impl_old_select(long res, long arg); +void __sanitizer_syscall_pre_impl_epoll_create(long size); +void __sanitizer_syscall_post_impl_epoll_create(long res, long size); +void __sanitizer_syscall_pre_impl_epoll_create1(long flags); +void __sanitizer_syscall_post_impl_epoll_create1(long res, long flags); +void __sanitizer_syscall_pre_impl_epoll_ctl(long epfd, long op, long fd, + long event); +void __sanitizer_syscall_post_impl_epoll_ctl(long res, long epfd, long op, + long fd, long event); +void __sanitizer_syscall_pre_impl_epoll_wait(long epfd, long events, + long maxevents, long timeout); +void __sanitizer_syscall_post_impl_epoll_wait(long res, long epfd, long events, + long maxevents, long timeout); +void __sanitizer_syscall_pre_impl_epoll_pwait(long epfd, long events, + long maxevents, long timeout, + long sigmask, long sigsetsize); +void __sanitizer_syscall_post_impl_epoll_pwait(long res, long epfd, long events, + long maxevents, long timeout, + long sigmask, long sigsetsize); +void __sanitizer_syscall_pre_impl_gethostname(long name, long len); +void __sanitizer_syscall_post_impl_gethostname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_sethostname(long name, long len); +void __sanitizer_syscall_post_impl_sethostname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_setdomainname(long name, long len); +void __sanitizer_syscall_post_impl_setdomainname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_newuname(long name); +void __sanitizer_syscall_post_impl_newuname(long res, long name); +void __sanitizer_syscall_pre_impl_uname(long arg0); +void __sanitizer_syscall_post_impl_uname(long res, long arg0); +void __sanitizer_syscall_pre_impl_olduname(long arg0); +void __sanitizer_syscall_post_impl_olduname(long res, long arg0); +void __sanitizer_syscall_pre_impl_getrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_getrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_old_getrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_old_getrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_setrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_setrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_prlimit64(long pid, long resource, + long new_rlim, long old_rlim); +void __sanitizer_syscall_post_impl_prlimit64(long res, long pid, long resource, + long new_rlim, long old_rlim); +void __sanitizer_syscall_pre_impl_getrusage(long who, long ru); +void __sanitizer_syscall_post_impl_getrusage(long res, long who, long ru); +void __sanitizer_syscall_pre_impl_umask(long mask); +void __sanitizer_syscall_post_impl_umask(long res, long mask); +void __sanitizer_syscall_pre_impl_msgget(long key, long msgflg); +void __sanitizer_syscall_post_impl_msgget(long res, long key, long msgflg); +void __sanitizer_syscall_pre_impl_msgsnd(long msqid, long msgp, long msgsz, + long msgflg); +void __sanitizer_syscall_post_impl_msgsnd(long res, long msqid, long msgp, + long msgsz, long msgflg); +void __sanitizer_syscall_pre_impl_msgrcv(long msqid, long msgp, long msgsz, + long msgtyp, long msgflg); +void __sanitizer_syscall_post_impl_msgrcv(long res, long msqid, long msgp, + long msgsz, long msgtyp, long msgflg); +void __sanitizer_syscall_pre_impl_msgctl(long msqid, long cmd, long buf); +void __sanitizer_syscall_post_impl_msgctl(long res, long msqid, long cmd, + long buf); +void __sanitizer_syscall_pre_impl_semget(long key, long nsems, long semflg); +void __sanitizer_syscall_post_impl_semget(long res, long key, long nsems, + long semflg); +void __sanitizer_syscall_pre_impl_semop(long semid, long sops, long nsops); +void __sanitizer_syscall_post_impl_semop(long res, long semid, long sops, + long nsops); +void __sanitizer_syscall_pre_impl_semctl(long semid, long semnum, long cmd, + long arg); +void __sanitizer_syscall_post_impl_semctl(long res, long semid, long semnum, + long cmd, long arg); +void __sanitizer_syscall_pre_impl_semtimedop(long semid, long sops, long nsops, + long timeout); +void __sanitizer_syscall_post_impl_semtimedop(long res, long semid, long sops, + long nsops, long timeout); +void __sanitizer_syscall_pre_impl_shmat(long shmid, long shmaddr, long shmflg); +void __sanitizer_syscall_post_impl_shmat(long res, long shmid, long shmaddr, + long shmflg); +void __sanitizer_syscall_pre_impl_shmget(long key, long size, long flag); +void __sanitizer_syscall_post_impl_shmget(long res, long key, long size, + long flag); +void __sanitizer_syscall_pre_impl_shmdt(long shmaddr); +void __sanitizer_syscall_post_impl_shmdt(long res, long shmaddr); +void __sanitizer_syscall_pre_impl_shmctl(long shmid, long cmd, long buf); +void __sanitizer_syscall_post_impl_shmctl(long res, long shmid, long cmd, + long buf); +void __sanitizer_syscall_pre_impl_ipc(long call, long first, long second, + long third, long ptr, long fifth); +void __sanitizer_syscall_post_impl_ipc(long res, long call, long first, + long second, long third, long ptr, + long fifth); +void __sanitizer_syscall_pre_impl_mq_open(long name, long oflag, long mode, + long attr); +void __sanitizer_syscall_post_impl_mq_open(long res, long name, long oflag, + long mode, long attr); +void __sanitizer_syscall_pre_impl_mq_unlink(long name); +void __sanitizer_syscall_post_impl_mq_unlink(long res, long name); +void __sanitizer_syscall_pre_impl_mq_timedsend(long mqdes, long msg_ptr, + long msg_len, long msg_prio, + long abs_timeout); +void __sanitizer_syscall_post_impl_mq_timedsend(long res, long mqdes, + long msg_ptr, long msg_len, + long msg_prio, + long abs_timeout); +void __sanitizer_syscall_pre_impl_mq_timedreceive(long mqdes, long msg_ptr, + long msg_len, long msg_prio, + long abs_timeout); +void __sanitizer_syscall_post_impl_mq_timedreceive(long res, long mqdes, + long msg_ptr, long msg_len, + long msg_prio, + long abs_timeout); +void __sanitizer_syscall_pre_impl_mq_notify(long mqdes, long notification); +void __sanitizer_syscall_post_impl_mq_notify(long res, long mqdes, + long notification); +void __sanitizer_syscall_pre_impl_mq_getsetattr(long mqdes, long mqstat, + long omqstat); +void __sanitizer_syscall_post_impl_mq_getsetattr(long res, long mqdes, + long mqstat, long omqstat); +void __sanitizer_syscall_pre_impl_pciconfig_iobase(long which, long bus, + long devfn); +void __sanitizer_syscall_post_impl_pciconfig_iobase(long res, long which, + long bus, long devfn); +void __sanitizer_syscall_pre_impl_pciconfig_read(long bus, long dfn, long off, + long len, long buf); +void __sanitizer_syscall_post_impl_pciconfig_read(long res, long bus, long dfn, + long off, long len, long buf); +void __sanitizer_syscall_pre_impl_pciconfig_write(long bus, long dfn, long off, + long len, long buf); +void __sanitizer_syscall_post_impl_pciconfig_write(long res, long bus, long dfn, + long off, long len, + long buf); +void __sanitizer_syscall_pre_impl_swapon(long specialfile, long swap_flags); +void __sanitizer_syscall_post_impl_swapon(long res, long specialfile, + long swap_flags); +void __sanitizer_syscall_pre_impl_swapoff(long specialfile); +void __sanitizer_syscall_post_impl_swapoff(long res, long specialfile); +void __sanitizer_syscall_pre_impl_sysctl(long args); +void __sanitizer_syscall_post_impl_sysctl(long res, long args); +void __sanitizer_syscall_pre_impl_sysinfo(long info); +void __sanitizer_syscall_post_impl_sysinfo(long res, long info); +void __sanitizer_syscall_pre_impl_sysfs(long option, long arg1, long arg2); +void __sanitizer_syscall_post_impl_sysfs(long res, long option, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_syslog(long type, long buf, long len); +void __sanitizer_syscall_post_impl_syslog(long res, long type, long buf, + long len); +void __sanitizer_syscall_pre_impl_uselib(long library); +void __sanitizer_syscall_post_impl_uselib(long res, long library); +void __sanitizer_syscall_pre_impl_ni_syscall(); +void __sanitizer_syscall_post_impl_ni_syscall(long res); +void __sanitizer_syscall_pre_impl_ptrace(long request, long pid, long addr, + long data); +void __sanitizer_syscall_post_impl_ptrace(long res, long request, long pid, + long addr, long data); +void __sanitizer_syscall_pre_impl_add_key(long _type, long _description, + long _payload, long plen, + long destringid); +void __sanitizer_syscall_post_impl_add_key(long res, long _type, + long _description, long _payload, + long plen, long destringid); +void __sanitizer_syscall_pre_impl_request_key(long _type, long _description, + long _callout_info, + long destringid); +void __sanitizer_syscall_post_impl_request_key(long res, long _type, + long _description, + long _callout_info, + long destringid); +void __sanitizer_syscall_pre_impl_keyctl(long cmd, long arg2, long arg3, + long arg4, long arg5); +void __sanitizer_syscall_post_impl_keyctl(long res, long cmd, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_pre_impl_ioprio_set(long which, long who, long ioprio); +void __sanitizer_syscall_post_impl_ioprio_set(long res, long which, long who, + long ioprio); +void __sanitizer_syscall_pre_impl_ioprio_get(long which, long who); +void __sanitizer_syscall_post_impl_ioprio_get(long res, long which, long who); +void __sanitizer_syscall_pre_impl_set_mempolicy(long mode, long nmask, + long maxnode); +void __sanitizer_syscall_post_impl_set_mempolicy(long res, long mode, + long nmask, long maxnode); +void __sanitizer_syscall_pre_impl_migrate_pages(long pid, long maxnode, + long from, long to); +void __sanitizer_syscall_post_impl_migrate_pages(long res, long pid, + long maxnode, long from, + long to); +void __sanitizer_syscall_pre_impl_move_pages(long pid, long nr_pages, + long pages, long nodes, + long status, long flags); +void __sanitizer_syscall_post_impl_move_pages(long res, long pid, long nr_pages, + long pages, long nodes, + long status, long flags); +void __sanitizer_syscall_pre_impl_mbind(long start, long len, long mode, + long nmask, long maxnode, long flags); +void __sanitizer_syscall_post_impl_mbind(long res, long start, long len, + long mode, long nmask, long maxnode, + long flags); +void __sanitizer_syscall_pre_impl_get_mempolicy(long policy, long nmask, + long maxnode, long addr, + long flags); +void __sanitizer_syscall_post_impl_get_mempolicy(long res, long policy, + long nmask, long maxnode, + long addr, long flags); +void __sanitizer_syscall_pre_impl_inotify_init(); +void __sanitizer_syscall_post_impl_inotify_init(long res); +void __sanitizer_syscall_pre_impl_inotify_init1(long flags); +void __sanitizer_syscall_post_impl_inotify_init1(long res, long flags); +void __sanitizer_syscall_pre_impl_inotify_add_watch(long fd, long path, + long mask); +void __sanitizer_syscall_post_impl_inotify_add_watch(long res, long fd, + long path, long mask); +void __sanitizer_syscall_pre_impl_inotify_rm_watch(long fd, long wd); +void __sanitizer_syscall_post_impl_inotify_rm_watch(long res, long fd, long wd); +void __sanitizer_syscall_pre_impl_spu_run(long fd, long unpc, long ustatus); +void __sanitizer_syscall_post_impl_spu_run(long res, long fd, long unpc, + long ustatus); +void __sanitizer_syscall_pre_impl_spu_create(long name, long flags, long mode, + long fd); +void __sanitizer_syscall_post_impl_spu_create(long res, long name, long flags, + long mode, long fd); +void __sanitizer_syscall_pre_impl_mknodat(long dfd, long filename, long mode, + long dev); +void __sanitizer_syscall_post_impl_mknodat(long res, long dfd, long filename, + long mode, long dev); +void __sanitizer_syscall_pre_impl_mkdirat(long dfd, long pathname, long mode); +void __sanitizer_syscall_post_impl_mkdirat(long res, long dfd, long pathname, + long mode); +void __sanitizer_syscall_pre_impl_unlinkat(long dfd, long pathname, long flag); +void __sanitizer_syscall_post_impl_unlinkat(long res, long dfd, long pathname, + long flag); +void __sanitizer_syscall_pre_impl_symlinkat(long oldname, long newdfd, + long newname); +void __sanitizer_syscall_post_impl_symlinkat(long res, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_pre_impl_linkat(long olddfd, long oldname, long newdfd, + long newname, long flags); +void __sanitizer_syscall_post_impl_linkat(long res, long olddfd, long oldname, + long newdfd, long newname, + long flags); +void __sanitizer_syscall_pre_impl_renameat(long olddfd, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_post_impl_renameat(long res, long olddfd, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_pre_impl_futimesat(long dfd, long filename, + long utimes); +void __sanitizer_syscall_post_impl_futimesat(long res, long dfd, long filename, + long utimes); +void __sanitizer_syscall_pre_impl_faccessat(long dfd, long filename, long mode); +void __sanitizer_syscall_post_impl_faccessat(long res, long dfd, long filename, + long mode); +void __sanitizer_syscall_pre_impl_fchmodat(long dfd, long filename, long mode); +void __sanitizer_syscall_post_impl_fchmodat(long res, long dfd, long filename, + long mode); +void __sanitizer_syscall_pre_impl_fchownat(long dfd, long filename, long user, + long group, long flag); +void __sanitizer_syscall_post_impl_fchownat(long res, long dfd, long filename, + long user, long group, long flag); +void __sanitizer_syscall_pre_impl_openat(long dfd, long filename, long flags, + long mode); +void __sanitizer_syscall_post_impl_openat(long res, long dfd, long filename, + long flags, long mode); +void __sanitizer_syscall_pre_impl_newfstatat(long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_post_impl_newfstatat(long res, long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_pre_impl_fstatat64(long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_post_impl_fstatat64(long res, long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_pre_impl_readlinkat(long dfd, long path, long buf, + long bufsiz); +void __sanitizer_syscall_post_impl_readlinkat(long res, long dfd, long path, + long buf, long bufsiz); +void __sanitizer_syscall_pre_impl_utimensat(long dfd, long filename, + long utimes, long flags); +void __sanitizer_syscall_post_impl_utimensat(long res, long dfd, long filename, + long utimes, long flags); +void __sanitizer_syscall_pre_impl_unshare(long unshare_flags); +void __sanitizer_syscall_post_impl_unshare(long res, long unshare_flags); +void __sanitizer_syscall_pre_impl_splice(long fd_in, long off_in, long fd_out, + long off_out, long len, long flags); +void __sanitizer_syscall_post_impl_splice(long res, long fd_in, long off_in, + long fd_out, long off_out, long len, + long flags); +void __sanitizer_syscall_pre_impl_vmsplice(long fd, long iov, long nr_segs, + long flags); +void __sanitizer_syscall_post_impl_vmsplice(long res, long fd, long iov, + long nr_segs, long flags); +void __sanitizer_syscall_pre_impl_tee(long fdin, long fdout, long len, + long flags); +void __sanitizer_syscall_post_impl_tee(long res, long fdin, long fdout, + long len, long flags); +void __sanitizer_syscall_pre_impl_get_robust_list(long pid, long head_ptr, + long len_ptr); +void __sanitizer_syscall_post_impl_get_robust_list(long res, long pid, + long head_ptr, long len_ptr); +void __sanitizer_syscall_pre_impl_set_robust_list(long head, long len); +void __sanitizer_syscall_post_impl_set_robust_list(long res, long head, + long len); +void __sanitizer_syscall_pre_impl_getcpu(long cpu, long node, long cache); +void __sanitizer_syscall_post_impl_getcpu(long res, long cpu, long node, + long cache); +void __sanitizer_syscall_pre_impl_signalfd(long ufd, long user_mask, + long sizemask); +void __sanitizer_syscall_post_impl_signalfd(long res, long ufd, long user_mask, + long sizemask); +void __sanitizer_syscall_pre_impl_signalfd4(long ufd, long user_mask, + long sizemask, long flags); +void __sanitizer_syscall_post_impl_signalfd4(long res, long ufd, long user_mask, + long sizemask, long flags); +void __sanitizer_syscall_pre_impl_timerfd_create(long clockid, long flags); +void __sanitizer_syscall_post_impl_timerfd_create(long res, long clockid, + long flags); +void __sanitizer_syscall_pre_impl_timerfd_settime(long ufd, long flags, + long utmr, long otmr); +void __sanitizer_syscall_post_impl_timerfd_settime(long res, long ufd, + long flags, long utmr, + long otmr); +void __sanitizer_syscall_pre_impl_timerfd_gettime(long ufd, long otmr); +void __sanitizer_syscall_post_impl_timerfd_gettime(long res, long ufd, + long otmr); +void __sanitizer_syscall_pre_impl_eventfd(long count); +void __sanitizer_syscall_post_impl_eventfd(long res, long count); +void __sanitizer_syscall_pre_impl_eventfd2(long count, long flags); +void __sanitizer_syscall_post_impl_eventfd2(long res, long count, long flags); +void __sanitizer_syscall_pre_impl_old_readdir(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_old_readdir(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_pselect6(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_pselect6(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_ppoll(long arg0, long arg1, long arg2, + long arg3, long arg4); +void __sanitizer_syscall_post_impl_ppoll(long res, long arg0, long arg1, + long arg2, long arg3, long arg4); +void __sanitizer_syscall_pre_impl_fanotify_init(long flags, long event_f_flags); +void __sanitizer_syscall_post_impl_fanotify_init(long res, long flags, + long event_f_flags); +void __sanitizer_syscall_pre_impl_fanotify_mark(long fanotify_fd, long flags, + long mask, long fd, + long pathname); +void __sanitizer_syscall_post_impl_fanotify_mark(long res, long fanotify_fd, + long flags, long mask, long fd, + long pathname); +void __sanitizer_syscall_pre_impl_syncfs(long fd); +void __sanitizer_syscall_post_impl_syncfs(long res, long fd); +void __sanitizer_syscall_pre_impl_perf_event_open(long attr_uptr, long pid, + long cpu, long group_fd, + long flags); +void __sanitizer_syscall_post_impl_perf_event_open(long res, long attr_uptr, + long pid, long cpu, + long group_fd, long flags); +void __sanitizer_syscall_pre_impl_mmap_pgoff(long addr, long len, long prot, + long flags, long fd, long pgoff); +void __sanitizer_syscall_post_impl_mmap_pgoff(long res, long addr, long len, + long prot, long flags, long fd, + long pgoff); +void __sanitizer_syscall_pre_impl_old_mmap(long arg); +void __sanitizer_syscall_post_impl_old_mmap(long res, long arg); +void __sanitizer_syscall_pre_impl_name_to_handle_at(long dfd, long name, + long handle, long mnt_id, + long flag); +void __sanitizer_syscall_post_impl_name_to_handle_at(long res, long dfd, + long name, long handle, + long mnt_id, long flag); +void __sanitizer_syscall_pre_impl_open_by_handle_at(long mountdirfd, + long handle, long flags); +void __sanitizer_syscall_post_impl_open_by_handle_at(long res, long mountdirfd, + long handle, long flags); +void __sanitizer_syscall_pre_impl_setns(long fd, long nstype); +void __sanitizer_syscall_post_impl_setns(long res, long fd, long nstype); +void __sanitizer_syscall_pre_impl_process_vm_readv(long pid, long lvec, + long liovcnt, long rvec, + long riovcnt, long flags); +void __sanitizer_syscall_post_impl_process_vm_readv(long res, long pid, + long lvec, long liovcnt, + long rvec, long riovcnt, + long flags); +void __sanitizer_syscall_pre_impl_process_vm_writev(long pid, long lvec, + long liovcnt, long rvec, + long riovcnt, long flags); +void __sanitizer_syscall_post_impl_process_vm_writev(long res, long pid, + long lvec, long liovcnt, + long rvec, long riovcnt, + long flags); +void __sanitizer_syscall_pre_impl_fork(); +void __sanitizer_syscall_post_impl_fork(long res); +void __sanitizer_syscall_pre_impl_vfork(); +void __sanitizer_syscall_post_impl_vfork(long res); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_LINUX_SYSCALL_HOOKS_H diff --git a/projects/compiler-rt/include/sanitizer/lsan_interface.h b/projects/compiler-rt/include/sanitizer/lsan_interface.h new file mode 100644 index 00000000..df256c0e --- /dev/null +++ b/projects/compiler-rt/include/sanitizer/lsan_interface.h @@ -0,0 +1,52 @@ +//===-- sanitizer/lsan_interface.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LSAN_INTERFACE_H +#define SANITIZER_LSAN_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + // Allocations made between calls to __lsan_disable() and __lsan_enable() will + // be treated as non-leaks. Disable/enable pairs may be nested. + void __lsan_disable(); + void __lsan_enable(); + // The heap object into which p points will be treated as a non-leak. + void __lsan_ignore_object(const void *p); + // The user may optionally provide this function to disallow leak checking + // for the program it is linked into (if the return value is non-zero). This + // function must be defined as returning a constant value; any behavior beyond + // that is unsupported. + int __lsan_is_turned_off(); + // Calling this function makes LSan enter the leak checking phase immediately. + // Use this if normal end-of-process leak checking happens too late (e.g. if + // you have intentional memory leaks in your shutdown code). Calling this + // function overrides end-of-process leak checking; it must be called at + // most once per process. This function will terminate the process if there + // are memory leaks and the exit_code flag is non-zero. + void __lsan_do_leak_check(); +#ifdef __cplusplus +} // extern "C" + +namespace __lsan { +class ScopedDisabler { + public: + ScopedDisabler() { __lsan_disable(); } + ~ScopedDisabler() { __lsan_enable(); } +}; +} // namespace __lsan +#endif + +#endif // SANITIZER_LSAN_INTERFACE_H diff --git a/projects/compiler-rt/include/sanitizer/msan_interface.h b/projects/compiler-rt/include/sanitizer/msan_interface.h new file mode 100644 index 00000000..63af84fc --- /dev/null +++ b/projects/compiler-rt/include/sanitizer/msan_interface.h @@ -0,0 +1,162 @@ +//===-- msan_interface.h --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef MSAN_INTERFACE_H +#define MSAN_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if __has_feature(memory_sanitizer) + /* Returns a string describing a stack origin. + Return NULL if the origin is invalid, or is not a stack origin. */ + const char *__msan_get_origin_descr_if_stack(uint32_t id); + + + /* Set raw origin for the memory range. */ + void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin); + + /* Get raw origin for an address. */ + uint32_t __msan_get_origin(const volatile void *a); + + /* Returns non-zero if tracking origins. */ + int __msan_get_track_origins(); + + /* Returns the origin id of the latest UMR in the calling thread. */ + uint32_t __msan_get_umr_origin(); + + /* Make memory region fully initialized (without changing its contents). */ + void __msan_unpoison(const volatile void *a, size_t size); + + /* Make memory region fully uninitialized (without changing its contents). */ + void __msan_poison(const volatile void *a, size_t size); + + /* Make memory region partially uninitialized (without changing its contents). + */ + void __msan_partial_poison(const volatile void *data, void *shadow, + size_t size); + + /* Returns the offset of the first (at least partially) poisoned byte in the + memory range, or -1 if the whole range is good. */ + intptr_t __msan_test_shadow(const volatile void *x, size_t size); + + /* Set exit code when error(s) were detected. + Value of 0 means don't change the program exit code. */ + void __msan_set_exit_code(int exit_code); + + /* For testing: + __msan_set_expect_umr(1); + ... some buggy code ... + __msan_set_expect_umr(0); + The last line will verify that a UMR happened. */ + void __msan_set_expect_umr(int expect_umr); + + /* Change the value of keep_going flag. Non-zero value means don't terminate + program execution when an error is detected. This will not affect error in + modules that were compiled without the corresponding compiler flag. */ + void __msan_set_keep_going(int keep_going); + + /* Print shadow and origin for the memory range to stdout in a human-readable + format. */ + void __msan_print_shadow(const volatile void *x, size_t size); + + /* Print current function arguments shadow and origin to stdout in a + human-readable format. */ + void __msan_print_param_shadow(); + + /* Returns true if running under a dynamic tool (DynamoRio-based). */ + int __msan_has_dynamic_component(); + + /* Tell MSan about newly allocated memory (ex.: custom allocator). + Memory will be marked uninitialized, with origin at the call site. */ + void __msan_allocated_memory(const volatile void* data, size_t size); + + /* This function may be optionally provided by user and should return + a string containing Msan runtime options. See msan_flags.h for details. */ + const char* __msan_default_options(); + + + /***********************************/ + /* Allocator statistics interface. */ + + /* Returns the estimated number of bytes that will be reserved by allocator + for request of "size" bytes. If Msan allocator can't allocate that much + memory, returns the maximal possible allocation size, otherwise returns + "size". */ + size_t __msan_get_estimated_allocated_size(size_t size); + + /* Returns true if p was returned by the Msan allocator and + is not yet freed. */ + int __msan_get_ownership(const volatile void *p); + + /* Returns the number of bytes reserved for the pointer p. + Requires (get_ownership(p) == true) or (p == 0). */ + size_t __msan_get_allocated_size(const volatile void *p); + + /* Number of bytes, allocated and not yet freed by the application. */ + size_t __msan_get_current_allocated_bytes(); + + /* Number of bytes, mmaped by msan allocator to fulfill allocation requests. + Generally, for request of X bytes, allocator can reserve and add to free + lists a large number of chunks of size X to use them for future requests. + All these chunks count toward the heap size. Currently, allocator never + releases memory to OS (instead, it just puts freed chunks to free + lists). */ + size_t __msan_get_heap_size(); + + /* Number of bytes, mmaped by msan allocator, which can be used to fulfill + allocation requests. When a user program frees memory chunk, it can first + fall into quarantine and will count toward __msan_get_free_bytes() + later. */ + size_t __msan_get_free_bytes(); + + /* Number of bytes in unmapped pages, that are released to OS. Currently, + always returns 0. */ + size_t __msan_get_unmapped_bytes(); + + /* Malloc hooks that may be optionally provided by user. + __msan_malloc_hook(ptr, size) is called immediately after + allocation of "size" bytes, which returned "ptr". + __msan_free_hook(ptr) is called immediately before + deallocation of "ptr". */ + void __msan_malloc_hook(const volatile void *ptr, size_t size); + void __msan_free_hook(const volatile void *ptr); + +#else // __has_feature(memory_sanitizer) + +#define __msan_get_origin_descr_if_stack(id) ((const char*)0) +#define __msan_set_origin(a, size, origin) +#define __msan_get_origin(a) ((uint32_t)-1) +#define __msan_get_track_origins() (0) +#define __msan_get_umr_origin() ((uint32_t)-1) +#define __msan_unpoison(a, size) +#define __msan_poison(a, size) +#define __msan_partial_poison(data, shadow, size) +#define __msan_test_shadow(x, size) ((intptr_t)-1) +#define __msan_set_exit_code(exit_code) +#define __msan_set_expect_umr(expect_umr) +#define __msan_print_shadow(x, size) +#define __msan_print_param_shadow() +#define __msan_has_dynamic_component() (0) +#define __msan_allocated_memory(data, size) + +#endif // __has_feature(memory_sanitizer) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/projects/compiler-rt/lib/CMakeLists.txt b/projects/compiler-rt/lib/CMakeLists.txt new file mode 100644 index 00000000..b620828b --- /dev/null +++ b/projects/compiler-rt/lib/CMakeLists.txt @@ -0,0 +1,216 @@ +# First, add the subdirectories which contain feature-based runtime libraries +# and several convenience helper libraries. + +# Don't build sanitizers in the bootstrap build. +if(LLVM_USE_SANITIZER STREQUAL "") + # AddressSanitizer is supported on Linux and Mac OS X. + # 32-bit Windows support is experimental. + if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux") + set(SUPPORTS_BUILDING_ASAN TRUE) + elseif(CMAKE_SYSTEM_NAME MATCHES "Windows" + AND MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + set(SUPPORTS_BUILDING_ASAN TRUE) + else() + set(SUPPORTS_BUILDING_ASAN FALSE) + endif() + if(SUPPORTS_BUILDING_ASAN) + add_subdirectory(asan) + add_subdirectory(interception) + add_subdirectory(sanitizer_common) + endif() + if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux" AND NOT ANDROID) + # LSan, UBsan and profile can be built on Mac OS and Linux. + add_subdirectory(lsan) + add_subdirectory(profile) + add_subdirectory(ubsan) + endif() + if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT ANDROID) + # ThreadSanitizer and MemorySanitizer are supported on Linux only. + add_subdirectory(tsan) + add_subdirectory(msan) + add_subdirectory(msandr) + add_subdirectory(dfsan) + endif() +endif() + +# The top-level lib directory contains a large amount of C code which provides +# generic implementations of the core runtime library along with optimized +# architecture-specific code in various subdirectories. + +set(GENERIC_SOURCES + absvdi2.c + absvsi2.c + absvti2.c + adddf3.c + addsf3.c + addvdi3.c + addvsi3.c + addvti3.c + apple_versioning.c + ashldi3.c + ashlti3.c + ashrdi3.c + ashrti3.c + # FIXME: atomic.c may only be compiled if host compiler understands _Atomic + # atomic.c + clear_cache.c + clzdi2.c + clzsi2.c + clzti2.c + cmpdi2.c + cmpti2.c + comparedf2.c + comparesf2.c + ctzdi2.c + ctzsi2.c + ctzti2.c + divdc3.c + divdf3.c + divdi3.c + divmoddi4.c + divmodsi4.c + divsc3.c + divsf3.c + divsi3.c + divti3.c + divxc3.c + enable_execute_stack.c + eprintf.c + extendsfdf2.c + ffsdi2.c + ffsti2.c + fixdfdi.c + fixdfsi.c + fixdfti.c + fixsfdi.c + fixsfsi.c + fixsfti.c + fixunsdfdi.c + fixunsdfsi.c + fixunsdfti.c + fixunssfdi.c + fixunssfsi.c + fixunssfti.c + fixunsxfdi.c + fixunsxfsi.c + fixunsxfti.c + fixxfdi.c + fixxfti.c + floatdidf.c + floatdisf.c + floatdixf.c + floatsidf.c + floatsisf.c + floattidf.c + floattisf.c + floattixf.c + floatundidf.c + floatundisf.c + floatundixf.c + floatunsidf.c + floatunsisf.c + floatuntidf.c + floatuntisf.c + floatuntixf.c + gcc_personality_v0.c + int_util.c + lshrdi3.c + lshrti3.c + moddi3.c + modsi3.c + modti3.c + muldc3.c + muldf3.c + muldi3.c + mulodi4.c + mulosi4.c + muloti4.c + mulsc3.c + mulsf3.c + multi3.c + mulvdi3.c + mulvsi3.c + mulvti3.c + mulxc3.c + negdf2.c + negdi2.c + negsf2.c + negti2.c + negvdi2.c + negvsi2.c + negvti2.c + paritydi2.c + paritysi2.c + parityti2.c + popcountdi2.c + popcountsi2.c + popcountti2.c + powidf2.c + powisf2.c + powitf2.c + powixf2.c + subdf3.c + subsf3.c + subvdi3.c + subvsi3.c + subvti3.c + trampoline_setup.c + truncdfsf2.c + ucmpdi2.c + ucmpti2.c + udivdi3.c + udivmoddi4.c + udivmodsi4.c + udivmodti4.c + udivsi3.c + udivti3.c + umoddi3.c + umodsi3.c + umodti3.c + ) + +set(x86_64_SOURCES + x86_64/floatdidf.c + x86_64/floatdisf.c + x86_64/floatdixf.c + x86_64/floatundidf.S + x86_64/floatundisf.S + x86_64/floatundixf.S + ${GENERIC_SOURCES}) + +set(i386_SOURCES + i386/ashldi3.S + i386/ashrdi3.S + i386/divdi3.S + i386/floatdidf.S + i386/floatdisf.S + i386/floatdixf.S + i386/floatundidf.S + i386/floatundisf.S + i386/floatundixf.S + i386/lshrdi3.S + i386/moddi3.S + i386/muldi3.S + i386/udivdi3.S + i386/umoddi3.S + ${GENERIC_SOURCES}) + +if (NOT WIN32) + foreach(arch x86_64 i386) + if(CAN_TARGET_${arch}) + add_compiler_rt_static_runtime(clang_rt.${arch} ${arch} + SOURCES ${${arch}_SOURCES} + CFLAGS "-std=c99") + endif() + endforeach() +endif() + +# Generate configs for running lit and unit tests. +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.common.configured.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.common.configured) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.common.unit.configured.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.common.unit.configured) + diff --git a/projects/compiler-rt/lib/Makefile.mk b/projects/compiler-rt/lib/Makefile.mk new file mode 100644 index 00000000..f9d7800c --- /dev/null +++ b/projects/compiler-rt/lib/Makefile.mk @@ -0,0 +1,33 @@ +#===- lib/Makefile.mk --------------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := builtins +SubDirs := + +# Add arch specific optimized implementations. +SubDirs += i386 ppc x86_64 arm + +# Add other submodules. +SubDirs += asan +SubDirs += interception +SubDirs += profile +SubDirs += sanitizer_common +SubDirs += tsan +SubDirs += msan +SubDirs += ubsan +SubDirs += lsan +SubDirs += dfsan + +# Define the variables for this specific directory. +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) +ObjNames := $(Sources:%.c=%.o) +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) diff --git a/projects/compiler-rt/lib/absvdi2.c b/projects/compiler-rt/lib/absvdi2.c new file mode 100644 index 00000000..682c2355 --- /dev/null +++ b/projects/compiler-rt/lib/absvdi2.c @@ -0,0 +1,29 @@ +/*===-- absvdi2.c - Implement __absvdi2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------=== + * + * This file implements __absvdi2 for the compiler_rt library. + * + *===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: absolute value */ + +/* Effects: aborts if abs(x) < 0 */ + +COMPILER_RT_ABI di_int +__absvdi2(di_int a) +{ + const int N = (int)(sizeof(di_int) * CHAR_BIT); + if (a == ((di_int)1 << (N-1))) + compilerrt_abort(); + const di_int t = a >> (N - 1); + return (a ^ t) - t; +} diff --git a/projects/compiler-rt/lib/absvsi2.c b/projects/compiler-rt/lib/absvsi2.c new file mode 100644 index 00000000..4812af81 --- /dev/null +++ b/projects/compiler-rt/lib/absvsi2.c @@ -0,0 +1,29 @@ +/* ===-- absvsi2.c - Implement __absvsi2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __absvsi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: absolute value */ + +/* Effects: aborts if abs(x) < 0 */ + +COMPILER_RT_ABI si_int +__absvsi2(si_int a) +{ + const int N = (int)(sizeof(si_int) * CHAR_BIT); + if (a == (1 << (N-1))) + compilerrt_abort(); + const si_int t = a >> (N - 1); + return (a ^ t) - t; +} diff --git a/projects/compiler-rt/lib/absvti2.c b/projects/compiler-rt/lib/absvti2.c new file mode 100644 index 00000000..c1c72779 --- /dev/null +++ b/projects/compiler-rt/lib/absvti2.c @@ -0,0 +1,33 @@ +/* ===-- absvti2.c - Implement __absvdi2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __absvti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: absolute value */ + +/* Effects: aborts if abs(x) < 0 */ + +ti_int +__absvti2(ti_int a) +{ + const int N = (int)(sizeof(ti_int) * CHAR_BIT); + if (a == ((ti_int)1 << (N-1))) + compilerrt_abort(); + const ti_int s = a >> (N - 1); + return (a ^ s) - s; +} + +#endif diff --git a/projects/compiler-rt/lib/adddf3.c b/projects/compiler-rt/lib/adddf3.c new file mode 100644 index 00000000..a55e82d2 --- /dev/null +++ b/projects/compiler-rt/lib/adddf3.c @@ -0,0 +1,152 @@ +//===-- lib/adddf3.c - Double-precision addition ------------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float addition with the IEEE-754 +// default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(dadd, adddf3) + +COMPILER_RT_ABI fp_t +__adddf3(fp_t a, fp_t b) { + + rep_t aRep = toRep(a); + rep_t bRep = toRep(b); + const rep_t aAbs = aRep & absMask; + const rep_t bAbs = bRep & absMask; + + // Detect if a or b is zero, infinity, or NaN. + if (aAbs - 1U >= infRep - 1U || bAbs - 1U >= infRep - 1U) { + + // NaN + anything = qNaN + if (aAbs > infRep) return fromRep(toRep(a) | quietBit); + // anything + NaN = qNaN + if (bAbs > infRep) return fromRep(toRep(b) | quietBit); + + if (aAbs == infRep) { + // +/-infinity + -/+infinity = qNaN + if ((toRep(a) ^ toRep(b)) == signBit) return fromRep(qnanRep); + // +/-infinity + anything remaining = +/- infinity + else return a; + } + + // anything remaining + +/-infinity = +/-infinity + if (bAbs == infRep) return b; + + // zero + anything = anything + if (!aAbs) { + // but we need to get the sign right for zero + zero + if (!bAbs) return fromRep(toRep(a) & toRep(b)); + else return b; + } + + // anything + zero = anything + if (!bAbs) return a; + } + + // Swap a and b if necessary so that a has the larger absolute value. + if (bAbs > aAbs) { + const rep_t temp = aRep; + aRep = bRep; + bRep = temp; + } + + // Extract the exponent and significand from the (possibly swapped) a and b. + int aExponent = aRep >> significandBits & maxExponent; + int bExponent = bRep >> significandBits & maxExponent; + rep_t aSignificand = aRep & significandMask; + rep_t bSignificand = bRep & significandMask; + + // Normalize any denormals, and adjust the exponent accordingly. + if (aExponent == 0) aExponent = normalize(&aSignificand); + if (bExponent == 0) bExponent = normalize(&bSignificand); + + // The sign of the result is the sign of the larger operand, a. If they + // have opposite signs, we are performing a subtraction; otherwise addition. + const rep_t resultSign = aRep & signBit; + const bool subtraction = (aRep ^ bRep) & signBit; + + // Shift the significands to give us round, guard and sticky, and or in the + // implicit significand bit. (If we fell through from the denormal path it + // was already set by normalize( ), but setting it twice won't hurt + // anything.) + aSignificand = (aSignificand | implicitBit) << 3; + bSignificand = (bSignificand | implicitBit) << 3; + + // Shift the significand of b by the difference in exponents, with a sticky + // bottom bit to get rounding correct. + const unsigned int align = aExponent - bExponent; + if (align) { + if (align < typeWidth) { + const bool sticky = bSignificand << (typeWidth - align); + bSignificand = bSignificand >> align | sticky; + } else { + bSignificand = 1; // sticky; b is known to be non-zero. + } + } + + if (subtraction) { + aSignificand -= bSignificand; + + // If a == -b, return +zero. + if (aSignificand == 0) return fromRep(0); + + // If partial cancellation occured, we need to left-shift the result + // and adjust the exponent: + if (aSignificand < implicitBit << 3) { + const int shift = rep_clz(aSignificand) - rep_clz(implicitBit << 3); + aSignificand <<= shift; + aExponent -= shift; + } + } + + else /* addition */ { + aSignificand += bSignificand; + + // If the addition carried up, we need to right-shift the result and + // adjust the exponent: + if (aSignificand & implicitBit << 4) { + const bool sticky = aSignificand & 1; + aSignificand = aSignificand >> 1 | sticky; + aExponent += 1; + } + } + + // If we have overflowed the type, return +/- infinity: + if (aExponent >= maxExponent) return fromRep(infRep | resultSign); + + if (aExponent <= 0) { + // Result is denormal before rounding; the exponent is zero and we + // need to shift the significand. + const int shift = 1 - aExponent; + const bool sticky = aSignificand << (typeWidth - shift); + aSignificand = aSignificand >> shift | sticky; + aExponent = 0; + } + + // Low three bits are round, guard, and sticky. + const int roundGuardSticky = aSignificand & 0x7; + + // Shift the significand into place, and mask off the implicit bit. + rep_t result = aSignificand >> 3 & significandMask; + + // Insert the exponent and sign. + result |= (rep_t)aExponent << significandBits; + result |= resultSign; + + // Final rounding. The result may overflow to infinity, but that is the + // correct result in that case. + if (roundGuardSticky > 0x4) result++; + if (roundGuardSticky == 0x4) result += result & 1; + return fromRep(result); +} diff --git a/projects/compiler-rt/lib/addsf3.c b/projects/compiler-rt/lib/addsf3.c new file mode 100644 index 00000000..0268324d --- /dev/null +++ b/projects/compiler-rt/lib/addsf3.c @@ -0,0 +1,151 @@ +//===-- lib/addsf3.c - Single-precision addition ------------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float addition with the IEEE-754 +// default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(fadd, addsf3) + +fp_t __addsf3(fp_t a, fp_t b) { + + rep_t aRep = toRep(a); + rep_t bRep = toRep(b); + const rep_t aAbs = aRep & absMask; + const rep_t bAbs = bRep & absMask; + + // Detect if a or b is zero, infinity, or NaN. + if (aAbs - 1U >= infRep - 1U || bAbs - 1U >= infRep - 1U) { + + // NaN + anything = qNaN + if (aAbs > infRep) return fromRep(toRep(a) | quietBit); + // anything + NaN = qNaN + if (bAbs > infRep) return fromRep(toRep(b) | quietBit); + + if (aAbs == infRep) { + // +/-infinity + -/+infinity = qNaN + if ((toRep(a) ^ toRep(b)) == signBit) return fromRep(qnanRep); + // +/-infinity + anything remaining = +/- infinity + else return a; + } + + // anything remaining + +/-infinity = +/-infinity + if (bAbs == infRep) return b; + + // zero + anything = anything + if (!aAbs) { + // but we need to get the sign right for zero + zero + if (!bAbs) return fromRep(toRep(a) & toRep(b)); + else return b; + } + + // anything + zero = anything + if (!bAbs) return a; + } + + // Swap a and b if necessary so that a has the larger absolute value. + if (bAbs > aAbs) { + const rep_t temp = aRep; + aRep = bRep; + bRep = temp; + } + + // Extract the exponent and significand from the (possibly swapped) a and b. + int aExponent = aRep >> significandBits & maxExponent; + int bExponent = bRep >> significandBits & maxExponent; + rep_t aSignificand = aRep & significandMask; + rep_t bSignificand = bRep & significandMask; + + // Normalize any denormals, and adjust the exponent accordingly. + if (aExponent == 0) aExponent = normalize(&aSignificand); + if (bExponent == 0) bExponent = normalize(&bSignificand); + + // The sign of the result is the sign of the larger operand, a. If they + // have opposite signs, we are performing a subtraction; otherwise addition. + const rep_t resultSign = aRep & signBit; + const bool subtraction = (aRep ^ bRep) & signBit; + + // Shift the significands to give us round, guard and sticky, and or in the + // implicit significand bit. (If we fell through from the denormal path it + // was already set by normalize( ), but setting it twice won't hurt + // anything.) + aSignificand = (aSignificand | implicitBit) << 3; + bSignificand = (bSignificand | implicitBit) << 3; + + // Shift the significand of b by the difference in exponents, with a sticky + // bottom bit to get rounding correct. + const unsigned int align = aExponent - bExponent; + if (align) { + if (align < typeWidth) { + const bool sticky = bSignificand << (typeWidth - align); + bSignificand = bSignificand >> align | sticky; + } else { + bSignificand = 1; // sticky; b is known to be non-zero. + } + } + + if (subtraction) { + aSignificand -= bSignificand; + + // If a == -b, return +zero. + if (aSignificand == 0) return fromRep(0); + + // If partial cancellation occured, we need to left-shift the result + // and adjust the exponent: + if (aSignificand < implicitBit << 3) { + const int shift = rep_clz(aSignificand) - rep_clz(implicitBit << 3); + aSignificand <<= shift; + aExponent -= shift; + } + } + + else /* addition */ { + aSignificand += bSignificand; + + // If the addition carried up, we need to right-shift the result and + // adjust the exponent: + if (aSignificand & implicitBit << 4) { + const bool sticky = aSignificand & 1; + aSignificand = aSignificand >> 1 | sticky; + aExponent += 1; + } + } + + // If we have overflowed the type, return +/- infinity: + if (aExponent >= maxExponent) return fromRep(infRep | resultSign); + + if (aExponent <= 0) { + // Result is denormal before rounding; the exponent is zero and we + // need to shift the significand. + const int shift = 1 - aExponent; + const bool sticky = aSignificand << (typeWidth - shift); + aSignificand = aSignificand >> shift | sticky; + aExponent = 0; + } + + // Low three bits are round, guard, and sticky. + const int roundGuardSticky = aSignificand & 0x7; + + // Shift the significand into place, and mask off the implicit bit. + rep_t result = aSignificand >> 3 & significandMask; + + // Insert the exponent and sign. + result |= (rep_t)aExponent << significandBits; + result |= resultSign; + + // Final rounding. The result may overflow to infinity, but that is the + // correct result in that case. + if (roundGuardSticky > 0x4) result++; + if (roundGuardSticky == 0x4) result += result & 1; + return fromRep(result); +} diff --git a/projects/compiler-rt/lib/addvdi3.c b/projects/compiler-rt/lib/addvdi3.c new file mode 100644 index 00000000..db45a27f --- /dev/null +++ b/projects/compiler-rt/lib/addvdi3.c @@ -0,0 +1,36 @@ +/* ===-- addvdi3.c - Implement __addvdi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __addvdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a + b */ + +/* Effects: aborts if a + b overflows */ + +COMPILER_RT_ABI di_int +__addvdi3(di_int a, di_int b) +{ + di_int s = a + b; + if (b >= 0) + { + if (s < a) + compilerrt_abort(); + } + else + { + if (s >= a) + compilerrt_abort(); + } + return s; +} diff --git a/projects/compiler-rt/lib/addvsi3.c b/projects/compiler-rt/lib/addvsi3.c new file mode 100644 index 00000000..81f515cd --- /dev/null +++ b/projects/compiler-rt/lib/addvsi3.c @@ -0,0 +1,36 @@ +/* ===-- addvsi3.c - Implement __addvsi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __addvsi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a + b */ + +/* Effects: aborts if a + b overflows */ + +COMPILER_RT_ABI si_int +__addvsi3(si_int a, si_int b) +{ + si_int s = a + b; + if (b >= 0) + { + if (s < a) + compilerrt_abort(); + } + else + { + if (s >= a) + compilerrt_abort(); + } + return s; +} diff --git a/projects/compiler-rt/lib/addvti3.c b/projects/compiler-rt/lib/addvti3.c new file mode 100644 index 00000000..2efcf3b4 --- /dev/null +++ b/projects/compiler-rt/lib/addvti3.c @@ -0,0 +1,40 @@ +/* ===-- addvti3.c - Implement __addvti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __addvti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: a + b */ + +/* Effects: aborts if a + b overflows */ + +ti_int +__addvti3(ti_int a, ti_int b) +{ + ti_int s = a + b; + if (b >= 0) + { + if (s < a) + compilerrt_abort(); + } + else + { + if (s >= a) + compilerrt_abort(); + } + return s; +} + +#endif diff --git a/projects/compiler-rt/lib/apple_versioning.c b/projects/compiler-rt/lib/apple_versioning.c new file mode 100644 index 00000000..3797a1ab --- /dev/null +++ b/projects/compiler-rt/lib/apple_versioning.c @@ -0,0 +1,350 @@ +/* ===-- apple_versioning.c - Adds versioning symbols for ld ---------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + */ + + +#if __APPLE__ + #include + + #if __IPHONE_OS_VERSION_MIN_REQUIRED + #define NOT_HERE_BEFORE_10_6(sym) + #define NOT_HERE_IN_10_8_AND_EARLIER(sym) \ + extern const char sym##_tmp61 __asm("$ld$hide$os6.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp61 = 0; \ + extern const char sym##_tmp60 __asm("$ld$hide$os6.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp60 = 0; \ + extern const char sym##_tmp51 __asm("$ld$hide$os5.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp51 = 0; \ + extern const char sym##_tmp50 __asm("$ld$hide$os5.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp50 = 0; + #else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NOT_HERE_IN_10_8_AND_EARLIER(sym) \ + extern const char sym##_tmp8 __asm("$ld$hide$os10.8$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp8 = 0; \ + extern const char sym##_tmp7 __asm("$ld$hide$os10.7$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp7 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp6 = 0; + #endif + + +/* Symbols in libSystem.dylib in 10.6 and later, + * but are in libgcc_s.dylib in earlier versions + */ + +NOT_HERE_BEFORE_10_6(__absvdi2) +NOT_HERE_BEFORE_10_6(__absvsi2) +NOT_HERE_BEFORE_10_6(__absvti2) +NOT_HERE_BEFORE_10_6(__addvdi3) +NOT_HERE_BEFORE_10_6(__addvsi3) +NOT_HERE_BEFORE_10_6(__addvti3) +NOT_HERE_BEFORE_10_6(__ashldi3) +NOT_HERE_BEFORE_10_6(__ashlti3) +NOT_HERE_BEFORE_10_6(__ashrdi3) +NOT_HERE_BEFORE_10_6(__ashrti3) +NOT_HERE_BEFORE_10_6(__clear_cache) +NOT_HERE_BEFORE_10_6(__clzdi2) +NOT_HERE_BEFORE_10_6(__clzsi2) +NOT_HERE_BEFORE_10_6(__clzti2) +NOT_HERE_BEFORE_10_6(__cmpdi2) +NOT_HERE_BEFORE_10_6(__cmpti2) +NOT_HERE_BEFORE_10_6(__ctzdi2) +NOT_HERE_BEFORE_10_6(__ctzsi2) +NOT_HERE_BEFORE_10_6(__ctzti2) +NOT_HERE_BEFORE_10_6(__divdc3) +NOT_HERE_BEFORE_10_6(__divdi3) +NOT_HERE_BEFORE_10_6(__divsc3) +NOT_HERE_BEFORE_10_6(__divtc3) +NOT_HERE_BEFORE_10_6(__divti3) +NOT_HERE_BEFORE_10_6(__divxc3) +NOT_HERE_BEFORE_10_6(__enable_execute_stack) +NOT_HERE_BEFORE_10_6(__ffsdi2) +NOT_HERE_BEFORE_10_6(__ffsti2) +NOT_HERE_BEFORE_10_6(__fixdfdi) +NOT_HERE_BEFORE_10_6(__fixdfti) +NOT_HERE_BEFORE_10_6(__fixsfdi) +NOT_HERE_BEFORE_10_6(__fixsfti) +NOT_HERE_BEFORE_10_6(__fixtfdi) +NOT_HERE_BEFORE_10_6(__fixunsdfdi) +NOT_HERE_BEFORE_10_6(__fixunsdfsi) +NOT_HERE_BEFORE_10_6(__fixunsdfti) +NOT_HERE_BEFORE_10_6(__fixunssfdi) +NOT_HERE_BEFORE_10_6(__fixunssfsi) +NOT_HERE_BEFORE_10_6(__fixunssfti) +NOT_HERE_BEFORE_10_6(__fixunstfdi) +NOT_HERE_BEFORE_10_6(__fixunsxfdi) +NOT_HERE_BEFORE_10_6(__fixunsxfsi) +NOT_HERE_BEFORE_10_6(__fixunsxfti) +NOT_HERE_BEFORE_10_6(__fixxfdi) +NOT_HERE_BEFORE_10_6(__fixxfti) +NOT_HERE_BEFORE_10_6(__floatdidf) +NOT_HERE_BEFORE_10_6(__floatdisf) +NOT_HERE_BEFORE_10_6(__floatditf) +NOT_HERE_BEFORE_10_6(__floatdixf) +NOT_HERE_BEFORE_10_6(__floattidf) +NOT_HERE_BEFORE_10_6(__floattisf) +NOT_HERE_BEFORE_10_6(__floattixf) +NOT_HERE_BEFORE_10_6(__floatundidf) +NOT_HERE_BEFORE_10_6(__floatundisf) +NOT_HERE_BEFORE_10_6(__floatunditf) +NOT_HERE_BEFORE_10_6(__floatundixf) +NOT_HERE_BEFORE_10_6(__floatuntidf) +NOT_HERE_BEFORE_10_6(__floatuntisf) +NOT_HERE_BEFORE_10_6(__floatuntixf) +NOT_HERE_BEFORE_10_6(__gcc_personality_v0) +NOT_HERE_BEFORE_10_6(__lshrdi3) +NOT_HERE_BEFORE_10_6(__lshrti3) +NOT_HERE_BEFORE_10_6(__moddi3) +NOT_HERE_BEFORE_10_6(__modti3) +NOT_HERE_BEFORE_10_6(__muldc3) +NOT_HERE_BEFORE_10_6(__muldi3) +NOT_HERE_BEFORE_10_6(__mulsc3) +NOT_HERE_BEFORE_10_6(__multc3) +NOT_HERE_BEFORE_10_6(__multi3) +NOT_HERE_BEFORE_10_6(__mulvdi3) +NOT_HERE_BEFORE_10_6(__mulvsi3) +NOT_HERE_BEFORE_10_6(__mulvti3) +NOT_HERE_BEFORE_10_6(__mulxc3) +NOT_HERE_BEFORE_10_6(__negdi2) +NOT_HERE_BEFORE_10_6(__negti2) +NOT_HERE_BEFORE_10_6(__negvdi2) +NOT_HERE_BEFORE_10_6(__negvsi2) +NOT_HERE_BEFORE_10_6(__negvti2) +NOT_HERE_BEFORE_10_6(__paritydi2) +NOT_HERE_BEFORE_10_6(__paritysi2) +NOT_HERE_BEFORE_10_6(__parityti2) +NOT_HERE_BEFORE_10_6(__popcountdi2) +NOT_HERE_BEFORE_10_6(__popcountsi2) +NOT_HERE_BEFORE_10_6(__popcountti2) +NOT_HERE_BEFORE_10_6(__powidf2) +NOT_HERE_BEFORE_10_6(__powisf2) +NOT_HERE_BEFORE_10_6(__powitf2) +NOT_HERE_BEFORE_10_6(__powixf2) +NOT_HERE_BEFORE_10_6(__subvdi3) +NOT_HERE_BEFORE_10_6(__subvsi3) +NOT_HERE_BEFORE_10_6(__subvti3) +NOT_HERE_BEFORE_10_6(__ucmpdi2) +NOT_HERE_BEFORE_10_6(__ucmpti2) +NOT_HERE_BEFORE_10_6(__udivdi3) +NOT_HERE_BEFORE_10_6(__udivmoddi4) +NOT_HERE_BEFORE_10_6(__udivmodti4) +NOT_HERE_BEFORE_10_6(__udivti3) +NOT_HERE_BEFORE_10_6(__umoddi3) +NOT_HERE_BEFORE_10_6(__umodti3) + + +#if __ppc__ +NOT_HERE_BEFORE_10_6(__gcc_qadd) +NOT_HERE_BEFORE_10_6(__gcc_qdiv) +NOT_HERE_BEFORE_10_6(__gcc_qmul) +NOT_HERE_BEFORE_10_6(__gcc_qsub) +NOT_HERE_BEFORE_10_6(__trampoline_setup) +#endif /* __ppc__ */ + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_compare_exchange_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_exchange_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_add_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_and_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_or_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_sub_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_fetch_xor_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_load_8) + +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_1) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_2) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_4) +NOT_HERE_IN_10_8_AND_EARLIER(__atomic_store_8) + + +#if __arm__ && __DYNAMIC__ + #define NOT_HERE_UNTIL_AFTER_4_3(sym) \ + extern const char sym##_tmp1 __asm("$ld$hide$os3.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp1 = 0; \ + extern const char sym##_tmp2 __asm("$ld$hide$os3.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp2 = 0; \ + extern const char sym##_tmp3 __asm("$ld$hide$os3.2$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \ + extern const char sym##_tmp4 __asm("$ld$hide$os4.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os4.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os4.2$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp6 = 0; \ + extern const char sym##_tmp7 __asm("$ld$hide$os4.3$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp7 = 0; + +NOT_HERE_UNTIL_AFTER_4_3(__absvdi2) +NOT_HERE_UNTIL_AFTER_4_3(__absvsi2) +NOT_HERE_UNTIL_AFTER_4_3(__adddf3) +NOT_HERE_UNTIL_AFTER_4_3(__adddf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__addsf3) +NOT_HERE_UNTIL_AFTER_4_3(__addsf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__addvdi3) +NOT_HERE_UNTIL_AFTER_4_3(__addvsi3) +NOT_HERE_UNTIL_AFTER_4_3(__ashldi3) +NOT_HERE_UNTIL_AFTER_4_3(__ashrdi3) +NOT_HERE_UNTIL_AFTER_4_3(__bswapdi2) +NOT_HERE_UNTIL_AFTER_4_3(__bswapsi2) +NOT_HERE_UNTIL_AFTER_4_3(__clzdi2) +NOT_HERE_UNTIL_AFTER_4_3(__clzsi2) +NOT_HERE_UNTIL_AFTER_4_3(__cmpdi2) +NOT_HERE_UNTIL_AFTER_4_3(__ctzdi2) +NOT_HERE_UNTIL_AFTER_4_3(__ctzsi2) +NOT_HERE_UNTIL_AFTER_4_3(__divdc3) +NOT_HERE_UNTIL_AFTER_4_3(__divdf3) +NOT_HERE_UNTIL_AFTER_4_3(__divdf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__divdi3) +NOT_HERE_UNTIL_AFTER_4_3(__divsc3) +NOT_HERE_UNTIL_AFTER_4_3(__divsf3) +NOT_HERE_UNTIL_AFTER_4_3(__divsf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__divsi3) +NOT_HERE_UNTIL_AFTER_4_3(__eqdf2) +NOT_HERE_UNTIL_AFTER_4_3(__eqdf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__eqsf2) +NOT_HERE_UNTIL_AFTER_4_3(__eqsf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__extendsfdf2) +NOT_HERE_UNTIL_AFTER_4_3(__extendsfdf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__ffsdi2) +NOT_HERE_UNTIL_AFTER_4_3(__fixdfdi) +NOT_HERE_UNTIL_AFTER_4_3(__fixdfsi) +NOT_HERE_UNTIL_AFTER_4_3(__fixdfsivfp) +NOT_HERE_UNTIL_AFTER_4_3(__fixsfdi) +NOT_HERE_UNTIL_AFTER_4_3(__fixsfsi) +NOT_HERE_UNTIL_AFTER_4_3(__fixsfsivfp) +NOT_HERE_UNTIL_AFTER_4_3(__fixunsdfdi) +NOT_HERE_UNTIL_AFTER_4_3(__fixunsdfsi) +NOT_HERE_UNTIL_AFTER_4_3(__fixunsdfsivfp) +NOT_HERE_UNTIL_AFTER_4_3(__fixunssfdi) +NOT_HERE_UNTIL_AFTER_4_3(__fixunssfsi) +NOT_HERE_UNTIL_AFTER_4_3(__fixunssfsivfp) +NOT_HERE_UNTIL_AFTER_4_3(__floatdidf) +NOT_HERE_UNTIL_AFTER_4_3(__floatdisf) +NOT_HERE_UNTIL_AFTER_4_3(__floatsidf) +NOT_HERE_UNTIL_AFTER_4_3(__floatsidfvfp) +NOT_HERE_UNTIL_AFTER_4_3(__floatsisf) +NOT_HERE_UNTIL_AFTER_4_3(__floatsisfvfp) +NOT_HERE_UNTIL_AFTER_4_3(__floatundidf) +NOT_HERE_UNTIL_AFTER_4_3(__floatundisf) +NOT_HERE_UNTIL_AFTER_4_3(__floatunsidf) +NOT_HERE_UNTIL_AFTER_4_3(__floatunsisf) +NOT_HERE_UNTIL_AFTER_4_3(__floatunssidfvfp) +NOT_HERE_UNTIL_AFTER_4_3(__floatunssisfvfp) +NOT_HERE_UNTIL_AFTER_4_3(__gedf2) +NOT_HERE_UNTIL_AFTER_4_3(__gedf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__gesf2) +NOT_HERE_UNTIL_AFTER_4_3(__gesf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__gtdf2) +NOT_HERE_UNTIL_AFTER_4_3(__gtdf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__gtsf2) +NOT_HERE_UNTIL_AFTER_4_3(__gtsf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__ledf2) +NOT_HERE_UNTIL_AFTER_4_3(__ledf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__lesf2) +NOT_HERE_UNTIL_AFTER_4_3(__lesf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__lshrdi3) +NOT_HERE_UNTIL_AFTER_4_3(__ltdf2) +NOT_HERE_UNTIL_AFTER_4_3(__ltdf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__ltsf2) +NOT_HERE_UNTIL_AFTER_4_3(__ltsf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__moddi3) +NOT_HERE_UNTIL_AFTER_4_3(__modsi3) +NOT_HERE_UNTIL_AFTER_4_3(__muldc3) +NOT_HERE_UNTIL_AFTER_4_3(__muldf3) +NOT_HERE_UNTIL_AFTER_4_3(__muldf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__muldi3) +NOT_HERE_UNTIL_AFTER_4_3(__mulsc3) +NOT_HERE_UNTIL_AFTER_4_3(__mulsf3) +NOT_HERE_UNTIL_AFTER_4_3(__mulsf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__mulvdi3) +NOT_HERE_UNTIL_AFTER_4_3(__mulvsi3) +NOT_HERE_UNTIL_AFTER_4_3(__nedf2) +NOT_HERE_UNTIL_AFTER_4_3(__nedf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__negdi2) +NOT_HERE_UNTIL_AFTER_4_3(__negvdi2) +NOT_HERE_UNTIL_AFTER_4_3(__negvsi2) +NOT_HERE_UNTIL_AFTER_4_3(__nesf2) +NOT_HERE_UNTIL_AFTER_4_3(__nesf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__paritydi2) +NOT_HERE_UNTIL_AFTER_4_3(__paritysi2) +NOT_HERE_UNTIL_AFTER_4_3(__popcountdi2) +NOT_HERE_UNTIL_AFTER_4_3(__popcountsi2) +NOT_HERE_UNTIL_AFTER_4_3(__powidf2) +NOT_HERE_UNTIL_AFTER_4_3(__powisf2) +NOT_HERE_UNTIL_AFTER_4_3(__subdf3) +NOT_HERE_UNTIL_AFTER_4_3(__subdf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__subsf3) +NOT_HERE_UNTIL_AFTER_4_3(__subsf3vfp) +NOT_HERE_UNTIL_AFTER_4_3(__subvdi3) +NOT_HERE_UNTIL_AFTER_4_3(__subvsi3) +NOT_HERE_UNTIL_AFTER_4_3(__truncdfsf2) +NOT_HERE_UNTIL_AFTER_4_3(__truncdfsf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__ucmpdi2) +NOT_HERE_UNTIL_AFTER_4_3(__udivdi3) +NOT_HERE_UNTIL_AFTER_4_3(__udivmoddi4) +NOT_HERE_UNTIL_AFTER_4_3(__udivsi3) +NOT_HERE_UNTIL_AFTER_4_3(__umoddi3) +NOT_HERE_UNTIL_AFTER_4_3(__umodsi3) +NOT_HERE_UNTIL_AFTER_4_3(__unorddf2) +NOT_HERE_UNTIL_AFTER_4_3(__unorddf2vfp) +NOT_HERE_UNTIL_AFTER_4_3(__unordsf2) +NOT_HERE_UNTIL_AFTER_4_3(__unordsf2vfp) + +NOT_HERE_UNTIL_AFTER_4_3(__divmodsi4) +NOT_HERE_UNTIL_AFTER_4_3(__udivmodsi4) +#endif // __arm__ && __DYNAMIC__ + + + + + +#else /* !__APPLE__ */ + +extern int avoid_empty_file; + +#endif /* !__APPLE__*/ diff --git a/projects/compiler-rt/lib/arm/Makefile.mk b/projects/compiler-rt/lib/arm/Makefile.mk new file mode 100644 index 00000000..a2df115f --- /dev/null +++ b/projects/compiler-rt/lib/arm/Makefile.mk @@ -0,0 +1,20 @@ +#===- lib/arm/Makefile.mk ----------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := builtins +SubDirs := +OnlyArchs := armv5 armv6 armv7 armv7f armv7k armv7m armv7em armv7s + +AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) +ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o) +Implementation := Optimized + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard lib/*.h $(Dir)/*.h) diff --git a/projects/compiler-rt/lib/arm/adddf3vfp.S b/projects/compiler-rt/lib/arm/adddf3vfp.S new file mode 100644 index 00000000..c90b0c2e --- /dev/null +++ b/projects/compiler-rt/lib/arm/adddf3vfp.S @@ -0,0 +1,25 @@ +//===-- adddf3vfp.S - Implement adddf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// double __adddf3vfp(double a, double b) { return a + b; } +// +// Adds two double precision floating point numbers using the Darwin +// calling convention where double arguments are passsed in GPR pairs +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__adddf3vfp) + vmov d6, r0, r1 // move first param from r0/r1 pair into d6 + vmov d7, r2, r3 // move second param from r2/r3 pair into d7 + vadd.f64 d6, d6, d7 + vmov r0, r1, d6 // move result back to r0/r1 pair + bx lr diff --git a/projects/compiler-rt/lib/arm/addsf3vfp.S b/projects/compiler-rt/lib/arm/addsf3vfp.S new file mode 100644 index 00000000..43653d52 --- /dev/null +++ b/projects/compiler-rt/lib/arm/addsf3vfp.S @@ -0,0 +1,25 @@ +//===-- addsf3vfp.S - Implement addsf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __addsf3vfp(float a, float b); +// +// Adds two single precision floating point numbers using the Darwin +// calling convention where single arguments are passsed in GPRs +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__addsf3vfp) + vmov s14, r0 // move first param from r0 into float register + vmov s15, r1 // move second param from r1 into float register + vadd.f32 s14, s14, s15 + vmov r0, s14 // move result back to r0 + bx lr diff --git a/projects/compiler-rt/lib/arm/aeabi_dcmp.S b/projects/compiler-rt/lib/arm/aeabi_dcmp.S new file mode 100644 index 00000000..c4d07727 --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_dcmp.S @@ -0,0 +1,39 @@ +//===-- aeabi_dcmp.S - EABI dcmp* implementation ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// int __aeabi_dcmp{eq,lt,le,ge,gt}(double a, double b) { +// int result = __{eq,lt,le,ge,gt}df2(a, b); +// if (result {==,<,<=,>=,>} 0) { +// return 1; +// } else { +// return 0; +// } +// } + +#define DEFINE_AEABI_DCMP(cond) \ + .syntax unified SEPARATOR \ + .align 2 SEPARATOR \ +DEFINE_COMPILERRT_FUNCTION(__aeabi_dcmp ## cond) \ + push { r4, lr } SEPARATOR \ + bl SYMBOL_NAME(__ ## cond ## df2) SEPARATOR \ + cmp r0, #0 SEPARATOR \ + b ## cond 1f SEPARATOR \ + mov r0, #0 SEPARATOR \ + pop { r4, pc } SEPARATOR \ +1: SEPARATOR \ + mov r0, #1 SEPARATOR \ + pop { r4, pc } + +DEFINE_AEABI_DCMP(eq) +DEFINE_AEABI_DCMP(lt) +DEFINE_AEABI_DCMP(le) +DEFINE_AEABI_DCMP(ge) +DEFINE_AEABI_DCMP(gt) diff --git a/projects/compiler-rt/lib/arm/aeabi_fcmp.S b/projects/compiler-rt/lib/arm/aeabi_fcmp.S new file mode 100644 index 00000000..576a33f2 --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_fcmp.S @@ -0,0 +1,39 @@ +//===-- aeabi_fcmp.S - EABI fcmp* implementation ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// int __aeabi_fcmp{eq,lt,le,ge,gt}(float a, float b) { +// int result = __{eq,lt,le,ge,gt}sf2(a, b); +// if (result {==,<,<=,>=,>} 0) { +// return 1; +// } else { +// return 0; +// } +// } + +#define DEFINE_AEABI_FCMP(cond) \ + .syntax unified SEPARATOR \ + .align 2 SEPARATOR \ +DEFINE_COMPILERRT_FUNCTION(__aeabi_fcmp ## cond) \ + push { r4, lr } SEPARATOR \ + bl SYMBOL_NAME(__ ## cond ## sf2) SEPARATOR \ + cmp r0, #0 SEPARATOR \ + b ## cond 1f SEPARATOR \ + mov r0, #0 SEPARATOR \ + pop { r4, pc } SEPARATOR \ +1: SEPARATOR \ + mov r0, #1 SEPARATOR \ + pop { r4, pc } + +DEFINE_AEABI_FCMP(eq) +DEFINE_AEABI_FCMP(lt) +DEFINE_AEABI_FCMP(le) +DEFINE_AEABI_FCMP(ge) +DEFINE_AEABI_FCMP(gt) diff --git a/projects/compiler-rt/lib/arm/aeabi_idivmod.S b/projects/compiler-rt/lib/arm/aeabi_idivmod.S new file mode 100644 index 00000000..0237f222 --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_idivmod.S @@ -0,0 +1,27 @@ +//===-- aeabi_idivmod.S - EABI idivmod implementation ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { int quot, int rem} __aeabi_idivmod(int numerator, int denominator) { +// int rem, quot; +// quot = __divmodsi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod) + push { lr } + sub sp, sp, #4 + mov r2, sp + bl SYMBOL_NAME(__divmodsi4) + ldr r1, [sp] + add sp, sp, #4 + pop { pc } diff --git a/projects/compiler-rt/lib/arm/aeabi_ldivmod.S b/projects/compiler-rt/lib/arm/aeabi_ldivmod.S new file mode 100644 index 00000000..197c459e --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_ldivmod.S @@ -0,0 +1,30 @@ +//===-- aeabi_ldivmod.S - EABI ldivmod implementation ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { int64_t quot, int64_t rem} +// __aeabi_ldivmod(int64_t numerator, int64_t denominator) { +// int64_t rem, quot; +// quot = __divmoddi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod) + push {r11, lr} + sub sp, sp, #16 + add r12, sp, #8 + str r12, [sp] + bl SYMBOL_NAME(__divmoddi4) + ldr r2, [sp, #8] + ldr r3, [sp, #12] + add sp, sp, #16 + pop {r11, pc} diff --git a/projects/compiler-rt/lib/arm/aeabi_memcmp.S b/projects/compiler-rt/lib/arm/aeabi_memcmp.S new file mode 100644 index 00000000..ca29c10c --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_memcmp.S @@ -0,0 +1,19 @@ +//===-- aeabi_memcmp.S - EABI memcmp implementation -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memcmp(void *dest, void *src, size_t n) { memcmp(dest, src, n); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memcmp) + b memcmp + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp4, __aeabi_memcmp) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp8, __aeabi_memcmp) diff --git a/projects/compiler-rt/lib/arm/aeabi_memcpy.S b/projects/compiler-rt/lib/arm/aeabi_memcpy.S new file mode 100644 index 00000000..8b9c7fdf --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_memcpy.S @@ -0,0 +1,19 @@ +//===-- aeabi_memcpy.S - EABI memcpy implementation -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memcpy(void *dest, void *src, size_t n) { memcpy(dest, src, n); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memcpy) + b memcpy + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy4, __aeabi_memcpy) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy8, __aeabi_memcpy) diff --git a/projects/compiler-rt/lib/arm/aeabi_memmove.S b/projects/compiler-rt/lib/arm/aeabi_memmove.S new file mode 100644 index 00000000..c94ed2b2 --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_memmove.S @@ -0,0 +1,19 @@ +//===-- aeabi_memmove.S - EABI memmove implementation --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memmove(void *dest, void *src, size_t n) { memmove(dest, src, n); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memmove) + b memmove + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove4, __aeabi_memmove) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove8, __aeabi_memmove) diff --git a/projects/compiler-rt/lib/arm/aeabi_memset.S b/projects/compiler-rt/lib/arm/aeabi_memset.S new file mode 100644 index 00000000..30ab4bae --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_memset.S @@ -0,0 +1,32 @@ +//===-- aeabi_memset.S - EABI memset implementation -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memset(void *dest, size_t n, int c) { memset(dest, c, n); } +// void __aeabi_memclr(void *dest, size_t n) { __aeabi_memset(dest, n, 0); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memset) + mov r3, r1 + mov r1, r2 + mov r2, r3 + b memset + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset4, __aeabi_memset) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset8, __aeabi_memset) + +DEFINE_COMPILERRT_FUNCTION(__aeabi_memclr) + mov r2, r1 + mov r1, #0 + b memset + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr4, __aeabi_memclr) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr8, __aeabi_memclr) + diff --git a/projects/compiler-rt/lib/arm/aeabi_uidivmod.S b/projects/compiler-rt/lib/arm/aeabi_uidivmod.S new file mode 100644 index 00000000..f7e1d2eb --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_uidivmod.S @@ -0,0 +1,28 @@ +//===-- aeabi_uidivmod.S - EABI uidivmod implementation -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { unsigned quot, unsigned rem} +// __aeabi_uidivmod(unsigned numerator, unsigned denominator) { +// unsigned rem, quot; +// quot = __udivmodsi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_uidivmod) + push { lr } + sub sp, sp, #4 + mov r2, sp + bl SYMBOL_NAME(__udivmodsi4) + ldr r1, [sp] + add sp, sp, #4 + pop { pc } diff --git a/projects/compiler-rt/lib/arm/aeabi_uldivmod.S b/projects/compiler-rt/lib/arm/aeabi_uldivmod.S new file mode 100644 index 00000000..724049dd --- /dev/null +++ b/projects/compiler-rt/lib/arm/aeabi_uldivmod.S @@ -0,0 +1,30 @@ +//===-- aeabi_uldivmod.S - EABI uldivmod implementation -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { uint64_t quot, uint64_t rem} +// __aeabi_uldivmod(uint64_t numerator, uint64_t denominator) { +// uint64_t rem, quot; +// quot = __udivmoddi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod) + push {r11, lr} + sub sp, sp, #16 + add r12, sp, #8 + str r12, [sp] + bl SYMBOL_NAME(__udivmoddi4) + ldr r2, [sp, #8] + ldr r3, [sp, #12] + add sp, sp, #16 + pop {r11, pc} \ No newline at end of file diff --git a/projects/compiler-rt/lib/arm/bswapdi2.S b/projects/compiler-rt/lib/arm/bswapdi2.S new file mode 100644 index 00000000..a0283e14 --- /dev/null +++ b/projects/compiler-rt/lib/arm/bswapdi2.S @@ -0,0 +1,36 @@ +//===------- bswapdi2 - Implement bswapdi2 --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern uint64_t __bswapdi2(uint64_t); +// +// Reverse all the bytes in a 64-bit integer. +// +.align 2 +DEFINE_COMPILERRT_FUNCTION(__bswapdi2) +#if __ARM_ARCH_5TEJ__ || __ARM_ARCH_4T__ + // before armv6 does not have "rev" instruction + // r2 = rev(r0) + eor r2, r0, r0, ror #16 + bic r2, r2, #0xff0000 + mov r2, r2, lsr #8 + eor r2, r2, r0, ror #8 + // r0 = rev(r1) + eor r0, r1, r1, ror #16 + bic r0, r0, #0xff0000 + mov r0, r0, lsr #8 + eor r0, r0, r1, ror #8 +#else + rev r2, r0 // r2 = rev(r0) + rev r0, r1 // r0 = rev(r1) +#endif + mov r1, r2 // r1 = r2 = rev(r0) + bx lr diff --git a/projects/compiler-rt/lib/arm/bswapsi2.S b/projects/compiler-rt/lib/arm/bswapsi2.S new file mode 100644 index 00000000..4c3af1f6 --- /dev/null +++ b/projects/compiler-rt/lib/arm/bswapsi2.S @@ -0,0 +1,28 @@ +//===------- bswapsi2 - Implement bswapsi2 --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern uint32_t __bswapsi2(uint32_t); +// +// Reverse all the bytes in a 32-bit integer. +// +.align 2 +DEFINE_COMPILERRT_FUNCTION(__bswapsi2) +#if __ARM_ARCH_5TEJ__ || __ARM_ARCH_4T__ + // before armv6 does not have "rev" instruction + eor r1, r0, r0, ror #16 + bic r1, r1, #0xff0000 + mov r1, r1, lsr #8 + eor r0, r1, r0, ror #8 +#else + rev r0, r0 +#endif + bx lr diff --git a/projects/compiler-rt/lib/arm/comparesf2.S b/projects/compiler-rt/lib/arm/comparesf2.S new file mode 100644 index 00000000..ce6f4b9e --- /dev/null +++ b/projects/compiler-rt/lib/arm/comparesf2.S @@ -0,0 +1,143 @@ +//===-- comparesf2.S - Implement single-precision soft-float comparisons --===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the following soft-fp_t comparison routines: +// +// __eqsf2 __gesf2 __unordsf2 +// __lesf2 __gtsf2 +// __ltsf2 +// __nesf2 +// +// The semantics of the routines grouped in each column are identical, so there +// is a single implementation for each, with multiple names. +// +// The routines behave as follows: +// +// __lesf2(a,b) returns -1 if a < b +// 0 if a == b +// 1 if a > b +// 1 if either a or b is NaN +// +// __gesf2(a,b) returns -1 if a < b +// 0 if a == b +// 1 if a > b +// -1 if either a or b is NaN +// +// __unordsf2(a,b) returns 0 if both a and b are numbers +// 1 if either a or b is NaN +// +// Note that __lesf2( ) and __gesf2( ) are identical except in their handling of +// NaN values. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" +.syntax unified + +.align 2 +DEFINE_COMPILERRT_FUNCTION(__eqsf2) +DEFINE_COMPILERRT_FUNCTION(__lesf2) +DEFINE_COMPILERRT_FUNCTION(__ltsf2) +DEFINE_COMPILERRT_FUNCTION(__nesf2) + // Make copies of a and b with the sign bit shifted off the top. These will + // be used to detect zeros and NaNs. + mov r2, r0, lsl #1 + mov r3, r1, lsl #1 + + // We do the comparison in three stages (ignoring NaN values for the time + // being). First, we orr the absolute values of a and b; this sets the Z + // flag if both a and b are zero (of either sign). The shift of r3 doesn't + // effect this at all, but it *does* make sure that the C flag is clear for + // the subsequent operations. + orrs r12, r2, r3, lsr #1 + + // Next, we check if a and b have the same or different signs. If they have + // opposite signs, this eor will set the N flag. + it ne + eorsne r12, r0, r1 + + // If a and b are equal (either both zeros or bit identical; again, we're + // ignoring NaNs for now), this subtract will zero out r0. If they have the + // same sign, the flags are updated as they would be for a comparison of the + // absolute values of a and b. + it pl + subspl r0, r2, r3 + + // If a is smaller in magnitude than b and both have the same sign, place + // the negation of the sign of b in r0. Thus, if both are negative and + // a > b, this sets r0 to 0; if both are positive and a < b, this sets + // r0 to -1. + // + // This is also done if a and b have opposite signs and are not both zero, + // because in that case the subtract was not performed and the C flag is + // still clear from the shift argument in orrs; if a is positive and b + // negative, this places 0 in r0; if a is negative and b positive, -1 is + // placed in r0. + it lo + mvnlo r0, r1, asr #31 + + // If a is greater in magnitude than b and both have the same sign, place + // the sign of b in r0. Thus, if both are negative and a < b, -1 is placed + // in r0, which is the desired result. Conversely, if both are positive + // and a > b, zero is placed in r0. + it hi + movhi r0, r1, asr #31 + + // If you've been keeping track, at this point r0 contains -1 if a < b and + // 0 if a >= b. All that remains to be done is to set it to 1 if a > b. + // If a == b, then the Z flag is set, so we can get the correct final value + // into r0 by simply or'ing with 1 if Z is clear. + it ne + orrne r0, r0, #1 + + // Finally, we need to deal with NaNs. If either argument is NaN, replace + // the value in r0 with 1. + cmp r2, #0xff000000 + ite ls + cmpls r3, #0xff000000 + movhi r0, #1 + bx lr + +.align 2 +DEFINE_COMPILERRT_FUNCTION(__gesf2) +DEFINE_COMPILERRT_FUNCTION(__gtsf2) + // Identical to the preceeding except in that we return -1 for NaN values. + // Given that the two paths share so much code, one might be tempted to + // unify them; however, the extra code needed to do so makes the code size + // to performance tradeoff very hard to justify for such small functions. + mov r2, r0, lsl #1 + mov r3, r1, lsl #1 + orrs r12, r2, r3, lsr #1 + it ne + eorsne r12, r0, r1 + it pl + subspl r0, r2, r3 + it lo + mvnlo r0, r1, asr #31 + it hi + movhi r0, r1, asr #31 + it ne + orrne r0, r0, #1 + cmp r2, #0xff000000 + ite ls + cmpls r3, #0xff000000 + movhi r0, #-1 + bx lr + +.align 2 +DEFINE_COMPILERRT_FUNCTION(__unordsf2) + // Return 1 for NaN values, 0 otherwise. + mov r2, r0, lsl #1 + mov r3, r1, lsl #1 + mov r0, #0 + cmp r2, #0xff000000 + ite ls + cmpls r3, #0xff000000 + movhi r0, #1 + bx lr diff --git a/projects/compiler-rt/lib/arm/divdf3vfp.S b/projects/compiler-rt/lib/arm/divdf3vfp.S new file mode 100644 index 00000000..52de67f7 --- /dev/null +++ b/projects/compiler-rt/lib/arm/divdf3vfp.S @@ -0,0 +1,25 @@ +//===-- divdf3vfp.S - Implement divdf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __divdf3vfp(double a, double b); +// +// Divides two double precision floating point numbers using the Darwin +// calling convention where double arguments are passsed in GPR pairs +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__divdf3vfp) + vmov d6, r0, r1 // move first param from r0/r1 pair into d6 + vmov d7, r2, r3 // move second param from r2/r3 pair into d7 + vdiv.f64 d5, d6, d7 + vmov r0, r1, d5 // move result back to r0/r1 pair + bx lr diff --git a/projects/compiler-rt/lib/arm/divmodsi4.S b/projects/compiler-rt/lib/arm/divmodsi4.S new file mode 100644 index 00000000..6495a8b4 --- /dev/null +++ b/projects/compiler-rt/lib/arm/divmodsi4.S @@ -0,0 +1,60 @@ +/*===-- divmodsi4.S - 32-bit signed integer divide and modulus ------------===// + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------===// + * + * This file implements the __divmodsi4 (32-bit signed integer divide and + * modulus) function for the ARM architecture. A naive digit-by-digit + * computation is employed for simplicity. + * + *===----------------------------------------------------------------------===*/ + +#include "../assembly.h" + +#define ESTABLISH_FRAME \ + push {r4-r7, lr} ;\ + add r7, sp, #12 +#define CLEAR_FRAME_AND_RETURN \ + pop {r4-r7, pc} + +.syntax unified +.align 3 +DEFINE_COMPILERRT_FUNCTION(__divmodsi4) +#if __ARM_ARCH_EXT_IDIV__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + mov r3, r0 + sdiv r0, r3, r1 + mls r1, r0, r1, r3 + str r1, [r2] + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else + ESTABLISH_FRAME +// Set aside the sign of the quotient and modulus, and the address for the +// modulus. + eor r4, r0, r1 + mov r5, r0 + mov r6, r2 +// Take the absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). + eor ip, r0, r0, asr #31 + eor lr, r1, r1, asr #31 + sub r0, ip, r0, asr #31 + sub r1, lr, r1, asr #31 +// Unsigned divmod: + bl SYMBOL_NAME(__udivmodsi4) +// Apply the sign of quotient and modulus + ldr r1, [r6] + eor r0, r0, r4, asr #31 + eor r1, r1, r5, asr #31 + sub r0, r0, r4, asr #31 + sub r1, r1, r5, asr #31 + str r1, [r6] + CLEAR_FRAME_AND_RETURN +#endif diff --git a/projects/compiler-rt/lib/arm/divsf3vfp.S b/projects/compiler-rt/lib/arm/divsf3vfp.S new file mode 100644 index 00000000..81ba9030 --- /dev/null +++ b/projects/compiler-rt/lib/arm/divsf3vfp.S @@ -0,0 +1,25 @@ +//===-- divsf3vfp.S - Implement divsf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __divsf3vfp(float a, float b); +// +// Divides two single precision floating point numbers using the Darwin +// calling convention where single arguments are passsed like 32-bit ints. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__divsf3vfp) + vmov s14, r0 // move first param from r0 into float register + vmov s15, r1 // move second param from r1 into float register + vdiv.f32 s13, s14, s15 + vmov r0, s13 // move result back to r0 + bx lr diff --git a/projects/compiler-rt/lib/arm/divsi3.S b/projects/compiler-rt/lib/arm/divsi3.S new file mode 100644 index 00000000..b631db29 --- /dev/null +++ b/projects/compiler-rt/lib/arm/divsi3.S @@ -0,0 +1,51 @@ +/*===-- divsi3.S - 32-bit signed integer divide ---------------------------===// + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------===// + * + * This file implements the __divsi3 (32-bit signed integer divide) function + * for the ARM architecture as a wrapper around the unsigned routine. + * + *===----------------------------------------------------------------------===*/ + +#include "../assembly.h" + +#define ESTABLISH_FRAME \ + push {r4, r7, lr} ;\ + add r7, sp, #4 +#define CLEAR_FRAME_AND_RETURN \ + pop {r4, r7, pc} + +.syntax unified +.align 3 +// Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine. +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_idiv, __divsi3) +DEFINE_COMPILERRT_FUNCTION(__divsi3) +#if __ARM_ARCH_EXT_IDIV__ + tst r1,r1 + beq LOCAL_LABEL(divzero) + sdiv r0, r0, r1 + bx lr +LOCAL_LABEL(divzero): + mov r0,#0 + bx lr +#else +ESTABLISH_FRAME +// Set aside the sign of the quotient. + eor r4, r0, r1 +// Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). + eor r2, r0, r0, asr #31 + eor r3, r1, r1, asr #31 + sub r0, r2, r0, asr #31 + sub r1, r3, r1, asr #31 +// abs(a) / abs(b) + bl SYMBOL_NAME(__udivsi3) +// Apply sign of quotient to result and return. + eor r0, r0, r4, asr #31 + sub r0, r0, r4, asr #31 + CLEAR_FRAME_AND_RETURN +#endif diff --git a/projects/compiler-rt/lib/arm/eqdf2vfp.S b/projects/compiler-rt/lib/arm/eqdf2vfp.S new file mode 100644 index 00000000..c41e55a3 --- /dev/null +++ b/projects/compiler-rt/lib/arm/eqdf2vfp.S @@ -0,0 +1,28 @@ +//===-- eqdf2vfp.S - Implement eqdf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __eqdf2vfp(double a, double b); +// +// Returns one iff a == b and neither is NaN. +// Uses Darwin calling convention where double precision arguments are passsed +// like in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__eqdf2vfp) + vmov d6, r0, r1 // load r0/r1 pair in double register + vmov d7, r2, r3 // load r2/r3 pair in double register + vcmp.f64 d6, d7 + vmrs apsr_nzcv, fpscr + moveq r0, #1 // set result register to 1 if equal + movne r0, #0 + bx lr diff --git a/projects/compiler-rt/lib/arm/eqsf2vfp.S b/projects/compiler-rt/lib/arm/eqsf2vfp.S new file mode 100644 index 00000000..730ef88d --- /dev/null +++ b/projects/compiler-rt/lib/arm/eqsf2vfp.S @@ -0,0 +1,29 @@ +//===-- eqsf2vfp.S - Implement eqsf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __eqsf2vfp(float a, float b); +// +// Returns one iff a == b and neither is NaN. +// Uses Darwin calling convention where single precision arguments are passsed +// like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__eqsf2vfp) + vmov s14, r0 // move from GPR 0 to float register + vmov s15, r1 // move from GPR 1 to float register + vcmp.f32 s14, s15 + vmrs apsr_nzcv, fpscr + moveq r0, #1 // set result register to 1 if equal + movne r0, #0 + bx lr + diff --git a/projects/compiler-rt/lib/arm/extendsfdf2vfp.S b/projects/compiler-rt/lib/arm/extendsfdf2vfp.S new file mode 100644 index 00000000..17a146e0 --- /dev/null +++ b/projects/compiler-rt/lib/arm/extendsfdf2vfp.S @@ -0,0 +1,25 @@ +//===-- extendsfdf2vfp.S - Implement extendsfdf2vfp -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __extendsfdf2vfp(float a); +// +// Converts single precision float to double precision result. +// Uses Darwin calling convention where a single precision parameter is +// passed in a GPR and a double precision result is returned in R0/R1 pair. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__extendsfdf2vfp) + vmov s15, r0 // load float register from R0 + vcvt.f64.f32 d7, s15 // convert single to double + vmov r0, r1, d7 // return result in r0/r1 pair + bx lr diff --git a/projects/compiler-rt/lib/arm/fixdfsivfp.S b/projects/compiler-rt/lib/arm/fixdfsivfp.S new file mode 100644 index 00000000..b7c3299d --- /dev/null +++ b/projects/compiler-rt/lib/arm/fixdfsivfp.S @@ -0,0 +1,25 @@ +//===-- fixdfsivfp.S - Implement fixdfsivfp -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __fixdfsivfp(double a); +// +// Converts double precision float to a 32-bit int rounding towards zero. +// Uses Darwin calling convention where a double precision parameter is +// passed in GPR register pair. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__fixdfsivfp) + vmov d7, r0, r1 // load double register from R0/R1 + vcvt.s32.f64 s15, d7 // convert double to 32-bit int into s15 + vmov r0, s15 // move s15 to result register + bx lr diff --git a/projects/compiler-rt/lib/arm/fixsfsivfp.S b/projects/compiler-rt/lib/arm/fixsfsivfp.S new file mode 100644 index 00000000..1cea6a48 --- /dev/null +++ b/projects/compiler-rt/lib/arm/fixsfsivfp.S @@ -0,0 +1,25 @@ +//===-- fixsfsivfp.S - Implement fixsfsivfp -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __fixsfsivfp(float a); +// +// Converts single precision float to a 32-bit int rounding towards zero. +// Uses Darwin calling convention where a single precision parameter is +// passed in a GPR.. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__fixsfsivfp) + vmov s15, r0 // load float register from R0 + vcvt.s32.f32 s15, s15 // convert single to 32-bit int into s15 + vmov r0, s15 // move s15 to result register + bx lr diff --git a/projects/compiler-rt/lib/arm/fixunsdfsivfp.S b/projects/compiler-rt/lib/arm/fixunsdfsivfp.S new file mode 100644 index 00000000..54b03592 --- /dev/null +++ b/projects/compiler-rt/lib/arm/fixunsdfsivfp.S @@ -0,0 +1,26 @@ +//===-- fixunsdfsivfp.S - Implement fixunsdfsivfp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern unsigned int __fixunsdfsivfp(double a); +// +// Converts double precision float to a 32-bit unsigned int rounding towards +// zero. All negative values become zero. +// Uses Darwin calling convention where a double precision parameter is +// passed in GPR register pair. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__fixunsdfsivfp) + vmov d7, r0, r1 // load double register from R0/R1 + vcvt.u32.f64 s15, d7 // convert double to 32-bit int into s15 + vmov r0, s15 // move s15 to result register + bx lr diff --git a/projects/compiler-rt/lib/arm/fixunssfsivfp.S b/projects/compiler-rt/lib/arm/fixunssfsivfp.S new file mode 100644 index 00000000..12adb529 --- /dev/null +++ b/projects/compiler-rt/lib/arm/fixunssfsivfp.S @@ -0,0 +1,26 @@ +//===-- fixunssfsivfp.S - Implement fixunssfsivfp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern unsigned int __fixunssfsivfp(float a); +// +// Converts single precision float to a 32-bit unsigned int rounding towards +// zero. All negative values become zero. +// Uses Darwin calling convention where a single precision parameter is +// passed in a GPR.. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__fixunssfsivfp) + vmov s15, r0 // load float register from R0 + vcvt.u32.f32 s15, s15 // convert single to 32-bit unsigned into s15 + vmov r0, s15 // move s15 to result register + bx lr diff --git a/projects/compiler-rt/lib/arm/floatsidfvfp.S b/projects/compiler-rt/lib/arm/floatsidfvfp.S new file mode 100644 index 00000000..e6a1eb3e --- /dev/null +++ b/projects/compiler-rt/lib/arm/floatsidfvfp.S @@ -0,0 +1,25 @@ +//===-- floatsidfvfp.S - Implement floatsidfvfp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __floatsidfvfp(int a); +// +// Converts a 32-bit int to a double precision float. +// Uses Darwin calling convention where a double precision result is +// return in GPR register pair. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__floatsidfvfp) + vmov s15, r0 // move int to float register s15 + vcvt.f64.s32 d7, s15 // convert 32-bit int in s15 to double in d7 + vmov r0, r1, d7 // move d7 to result register pair r0/r1 + bx lr diff --git a/projects/compiler-rt/lib/arm/floatsisfvfp.S b/projects/compiler-rt/lib/arm/floatsisfvfp.S new file mode 100644 index 00000000..0d3a24fc --- /dev/null +++ b/projects/compiler-rt/lib/arm/floatsisfvfp.S @@ -0,0 +1,25 @@ +//===-- floatsisfvfp.S - Implement floatsisfvfp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __floatsisfvfp(int a); +// +// Converts single precision float to a 32-bit int rounding towards zero. +// Uses Darwin calling convention where a single precision result is +// return in a GPR.. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__floatsisfvfp) + vmov s15, r0 // move int to float register s15 + vcvt.f32.s32 s15, s15 // convert 32-bit int in s15 to float in s15 + vmov r0, s15 // move s15 to result register + bx lr diff --git a/projects/compiler-rt/lib/arm/floatunssidfvfp.S b/projects/compiler-rt/lib/arm/floatunssidfvfp.S new file mode 100644 index 00000000..770b2029 --- /dev/null +++ b/projects/compiler-rt/lib/arm/floatunssidfvfp.S @@ -0,0 +1,25 @@ +//===-- floatunssidfvfp.S - Implement floatunssidfvfp ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __floatunssidfvfp(unsigned int a); +// +// Converts a 32-bit int to a double precision float. +// Uses Darwin calling convention where a double precision result is +// return in GPR register pair. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__floatunssidfvfp) + vmov s15, r0 // move int to float register s15 + vcvt.f64.u32 d7, s15 // convert 32-bit int in s15 to double in d7 + vmov r0, r1, d7 // move d7 to result register pair r0/r1 + bx lr diff --git a/projects/compiler-rt/lib/arm/floatunssisfvfp.S b/projects/compiler-rt/lib/arm/floatunssisfvfp.S new file mode 100644 index 00000000..16b3ffb1 --- /dev/null +++ b/projects/compiler-rt/lib/arm/floatunssisfvfp.S @@ -0,0 +1,25 @@ +//===-- floatunssisfvfp.S - Implement floatunssisfvfp ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __floatunssisfvfp(unsigned int a); +// +// Converts single precision float to a 32-bit int rounding towards zero. +// Uses Darwin calling convention where a single precision result is +// return in a GPR.. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__floatunssisfvfp) + vmov s15, r0 // move int to float register s15 + vcvt.f32.u32 s15, s15 // convert 32-bit int in s15 to float in s15 + vmov r0, s15 // move s15 to result register + bx lr diff --git a/projects/compiler-rt/lib/arm/gedf2vfp.S b/projects/compiler-rt/lib/arm/gedf2vfp.S new file mode 100644 index 00000000..55603b83 --- /dev/null +++ b/projects/compiler-rt/lib/arm/gedf2vfp.S @@ -0,0 +1,28 @@ +//===-- gedf2vfp.S - Implement gedf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __gedf2vfp(double a, double b); +// +// Returns one iff a >= b and neither is NaN. +// Uses Darwin calling convention where double precision arguments are passsed +// like in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__gedf2vfp) + vmov d6, r0, r1 // load r0/r1 pair in double register + vmov d7, r2, r3 // load r2/r3 pair in double register + vcmp.f64 d6, d7 + vmrs apsr_nzcv, fpscr + movge r0, #1 // set result register to 1 if greater than or equal + movlt r0, #0 + bx lr diff --git a/projects/compiler-rt/lib/arm/gesf2vfp.S b/projects/compiler-rt/lib/arm/gesf2vfp.S new file mode 100644 index 00000000..02da35c0 --- /dev/null +++ b/projects/compiler-rt/lib/arm/gesf2vfp.S @@ -0,0 +1,29 @@ +//===-- gesf2vfp.S - Implement gesf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __gesf2vfp(float a, float b); +// +// Returns one iff a >= b and neither is NaN. +// Uses Darwin calling convention where single precision arguments are passsed +// like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__gesf2vfp) + vmov s14, r0 // move from GPR 0 to float register + vmov s15, r1 // move from GPR 1 to float register + vcmp.f32 s14, s15 + vmrs apsr_nzcv, fpscr + movge r0, #1 // set result register to 1 if greater than or equal + movlt r0, #0 + bx lr + diff --git a/projects/compiler-rt/lib/arm/gtdf2vfp.S b/projects/compiler-rt/lib/arm/gtdf2vfp.S new file mode 100644 index 00000000..b5b1e148 --- /dev/null +++ b/projects/compiler-rt/lib/arm/gtdf2vfp.S @@ -0,0 +1,28 @@ +//===-- gtdf2vfp.S - Implement gtdf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __gtdf2vfp(double a, double b); +// +// Returns one iff a > b and neither is NaN. +// Uses Darwin calling convention where double precision arguments are passsed +// like in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__gtdf2vfp) + vmov d6, r0, r1 // load r0/r1 pair in double register + vmov d7, r2, r3 // load r2/r3 pair in double register + vcmp.f64 d6, d7 + vmrs apsr_nzcv, fpscr + movgt r0, #1 // set result register to 1 if equal + movle r0, #0 + bx lr diff --git a/projects/compiler-rt/lib/arm/gtsf2vfp.S b/projects/compiler-rt/lib/arm/gtsf2vfp.S new file mode 100644 index 00000000..685a9cec --- /dev/null +++ b/projects/compiler-rt/lib/arm/gtsf2vfp.S @@ -0,0 +1,29 @@ +//===-- gtsf2vfp.S - Implement gtsf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __gtsf2vfp(float a, float b); +// +// Returns one iff a > b and neither is NaN. +// Uses Darwin calling convention where single precision arguments are passsed +// like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__gtsf2vfp) + vmov s14, r0 // move from GPR 0 to float register + vmov s15, r1 // move from GPR 1 to float register + vcmp.f32 s14, s15 + vmrs apsr_nzcv, fpscr + movgt r0, #1 // set result register to 1 if equal + movle r0, #0 + bx lr + diff --git a/projects/compiler-rt/lib/arm/ledf2vfp.S b/projects/compiler-rt/lib/arm/ledf2vfp.S new file mode 100644 index 00000000..6e140dde --- /dev/null +++ b/projects/compiler-rt/lib/arm/ledf2vfp.S @@ -0,0 +1,28 @@ +//===-- ledf2vfp.S - Implement ledf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __ledf2vfp(double a, double b); +// +// Returns one iff a <= b and neither is NaN. +// Uses Darwin calling convention where double precision arguments are passsed +// like in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__ledf2vfp) + vmov d6, r0, r1 // load r0/r1 pair in double register + vmov d7, r2, r3 // load r2/r3 pair in double register + vcmp.f64 d6, d7 + vmrs apsr_nzcv, fpscr + movls r0, #1 // set result register to 1 if equal + movhi r0, #0 + bx lr diff --git a/projects/compiler-rt/lib/arm/lesf2vfp.S b/projects/compiler-rt/lib/arm/lesf2vfp.S new file mode 100644 index 00000000..7b282509 --- /dev/null +++ b/projects/compiler-rt/lib/arm/lesf2vfp.S @@ -0,0 +1,29 @@ +//===-- lesf2vfp.S - Implement lesf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __lesf2vfp(float a, float b); +// +// Returns one iff a <= b and neither is NaN. +// Uses Darwin calling convention where single precision arguments are passsed +// like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__lesf2vfp) + vmov s14, r0 // move from GPR 0 to float register + vmov s15, r1 // move from GPR 1 to float register + vcmp.f32 s14, s15 + vmrs apsr_nzcv, fpscr + movls r0, #1 // set result register to 1 if equal + movhi r0, #0 + bx lr + diff --git a/projects/compiler-rt/lib/arm/ltdf2vfp.S b/projects/compiler-rt/lib/arm/ltdf2vfp.S new file mode 100644 index 00000000..a09e67a2 --- /dev/null +++ b/projects/compiler-rt/lib/arm/ltdf2vfp.S @@ -0,0 +1,28 @@ +//===-- ltdf2vfp.S - Implement ltdf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __ltdf2vfp(double a, double b); +// +// Returns one iff a < b and neither is NaN. +// Uses Darwin calling convention where double precision arguments are passsed +// like in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__ltdf2vfp) + vmov d6, r0, r1 // load r0/r1 pair in double register + vmov d7, r2, r3 // load r2/r3 pair in double register + vcmp.f64 d6, d7 + vmrs apsr_nzcv, fpscr + movmi r0, #1 // set result register to 1 if equal + movpl r0, #0 + bx lr diff --git a/projects/compiler-rt/lib/arm/ltsf2vfp.S b/projects/compiler-rt/lib/arm/ltsf2vfp.S new file mode 100644 index 00000000..8c7f9a86 --- /dev/null +++ b/projects/compiler-rt/lib/arm/ltsf2vfp.S @@ -0,0 +1,29 @@ +//===-- ltsf2vfp.S - Implement ltsf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __ltsf2vfp(float a, float b); +// +// Returns one iff a < b and neither is NaN. +// Uses Darwin calling convention where single precision arguments are passsed +// like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__ltsf2vfp) + vmov s14, r0 // move from GPR 0 to float register + vmov s15, r1 // move from GPR 1 to float register + vcmp.f32 s14, s15 + vmrs apsr_nzcv, fpscr + movmi r0, #1 // set result register to 1 if equal + movpl r0, #0 + bx lr + diff --git a/projects/compiler-rt/lib/arm/modsi3.S b/projects/compiler-rt/lib/arm/modsi3.S new file mode 100644 index 00000000..fe75b41b --- /dev/null +++ b/projects/compiler-rt/lib/arm/modsi3.S @@ -0,0 +1,50 @@ +/*===-- modsi3.S - 32-bit signed integer modulus --------------------------===// + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------===// + * + * This file implements the __modsi3 (32-bit signed integer modulus) function + * for the ARM architecture as a wrapper around the unsigned routine. + * + *===----------------------------------------------------------------------===*/ + +#include "../assembly.h" + +#define ESTABLISH_FRAME \ + push {r4, r7, lr} ;\ + add r7, sp, #4 +#define CLEAR_FRAME_AND_RETURN \ + pop {r4, r7, pc} + +.syntax unified +.align 3 +DEFINE_COMPILERRT_FUNCTION(__modsi3) +#if __ARM_ARCH_EXT_IDIV__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + sdiv r2, r0, r1 + mls r0, r2, r1, r0 + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else + ESTABLISH_FRAME + // Set aside the sign of the dividend. + mov r4, r0 + // Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). + eor r2, r0, r0, asr #31 + eor r3, r1, r1, asr #31 + sub r0, r2, r0, asr #31 + sub r1, r3, r1, asr #31 + // abs(a) % abs(b) + bl SYMBOL_NAME(__umodsi3) + // Apply sign of dividend to result and return. + eor r0, r0, r4, asr #31 + sub r0, r0, r4, asr #31 + CLEAR_FRAME_AND_RETURN +#endif diff --git a/projects/compiler-rt/lib/arm/muldf3vfp.S b/projects/compiler-rt/lib/arm/muldf3vfp.S new file mode 100644 index 00000000..838581eb --- /dev/null +++ b/projects/compiler-rt/lib/arm/muldf3vfp.S @@ -0,0 +1,25 @@ +//===-- muldf3vfp.S - Implement muldf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __muldf3vfp(double a, double b); +// +// Multiplies two double precision floating point numbers using the Darwin +// calling convention where double arguments are passsed in GPR pairs +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__muldf3vfp) + vmov d6, r0, r1 // move first param from r0/r1 pair into d6 + vmov d7, r2, r3 // move second param from r2/r3 pair into d7 + vmul.f64 d6, d6, d7 + vmov r0, r1, d6 // move result back to r0/r1 pair + bx lr diff --git a/projects/compiler-rt/lib/arm/mulsf3vfp.S b/projects/compiler-rt/lib/arm/mulsf3vfp.S new file mode 100644 index 00000000..ea25913c --- /dev/null +++ b/projects/compiler-rt/lib/arm/mulsf3vfp.S @@ -0,0 +1,25 @@ +//===-- mulsf3vfp.S - Implement mulsf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __mulsf3vfp(float a, float b); +// +// Multiplies two single precision floating point numbers using the Darwin +// calling convention where single arguments are passsed like 32-bit ints. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__mulsf3vfp) + vmov s14, r0 // move first param from r0 into float register + vmov s15, r1 // move second param from r1 into float register + vmul.f32 s13, s14, s15 + vmov r0, s13 // move result back to r0 + bx lr diff --git a/projects/compiler-rt/lib/arm/nedf2vfp.S b/projects/compiler-rt/lib/arm/nedf2vfp.S new file mode 100644 index 00000000..21670816 --- /dev/null +++ b/projects/compiler-rt/lib/arm/nedf2vfp.S @@ -0,0 +1,28 @@ +//===-- nedf2vfp.S - Implement nedf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __nedf2vfp(double a, double b); +// +// Returns zero if a and b are unequal and neither is NaN. +// Uses Darwin calling convention where double precision arguments are passsed +// like in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__nedf2vfp) + vmov d6, r0, r1 // load r0/r1 pair in double register + vmov d7, r2, r3 // load r2/r3 pair in double register + vcmp.f64 d6, d7 + vmrs apsr_nzcv, fpscr + movne r0, #1 // set result register to 0 if unequal + moveq r0, #0 + bx lr diff --git a/projects/compiler-rt/lib/arm/negdf2vfp.S b/projects/compiler-rt/lib/arm/negdf2vfp.S new file mode 100644 index 00000000..64c9b692 --- /dev/null +++ b/projects/compiler-rt/lib/arm/negdf2vfp.S @@ -0,0 +1,22 @@ +//===-- negdf2vfp.S - Implement negdf2vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __negdf2vfp(double a, double b); +// +// Returns the negation a double precision floating point numbers using the +// Darwin calling convention where double arguments are passsed in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__negdf2vfp) + eor r1, r1, #-2147483648 // flip sign bit on double in r0/r1 pair + bx lr diff --git a/projects/compiler-rt/lib/arm/negsf2vfp.S b/projects/compiler-rt/lib/arm/negsf2vfp.S new file mode 100644 index 00000000..b883b733 --- /dev/null +++ b/projects/compiler-rt/lib/arm/negsf2vfp.S @@ -0,0 +1,22 @@ +//===-- negsf2vfp.S - Implement negsf2vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __negsf2vfp(float a); +// +// Returns the negation of a single precision floating point numbers using the +// Darwin calling convention where single arguments are passsed like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__negsf2vfp) + eor r0, r0, #-2147483648 // flip sign bit on float in r0 + bx lr diff --git a/projects/compiler-rt/lib/arm/nesf2vfp.S b/projects/compiler-rt/lib/arm/nesf2vfp.S new file mode 100644 index 00000000..fa7aa80e --- /dev/null +++ b/projects/compiler-rt/lib/arm/nesf2vfp.S @@ -0,0 +1,29 @@ +//===-- nesf2vfp.S - Implement nesf2vfp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __nesf2vfp(float a, float b); +// +// Returns one iff a != b and neither is NaN. +// Uses Darwin calling convention where single precision arguments are passsed +// like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__nesf2vfp) + vmov s14, r0 // move from GPR 0 to float register + vmov s15, r1 // move from GPR 1 to float register + vcmp.f32 s14, s15 + vmrs apsr_nzcv, fpscr + movne r0, #1 // set result register to 1 if unequal + moveq r0, #0 + bx lr + diff --git a/projects/compiler-rt/lib/arm/restore_vfp_d8_d15_regs.S b/projects/compiler-rt/lib/arm/restore_vfp_d8_d15_regs.S new file mode 100644 index 00000000..7f441db5 --- /dev/null +++ b/projects/compiler-rt/lib/arm/restore_vfp_d8_d15_regs.S @@ -0,0 +1,37 @@ +//===-- save_restore_regs.S - Implement save/restore* ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// When compiling C++ functions that need to handle thrown exceptions the +// compiler is required to save all registers and call __Unwind_SjLj_Register +// in the function prolog. But when compiling for thumb1, there are +// no instructions to access the floating point registers, so the +// compiler needs to add a call to the helper function _save_vfp_d8_d15_regs +// written in ARM to save the float registers. In the epilog, the compiler +// must also add a call to __restore_vfp_d8_d15_regs to restore those registers. +// + + .text + .syntax unified + +// +// Restore registers d8-d15 from stack +// + .align 2 +DEFINE_COMPILERRT_PRIVATE_FUNCTION(__restore_vfp_d8_d15_regs) + vldmia sp!, {d8-d15} // pop registers d8-d15 off stack + bx lr // return to prolog + + + + // tell linker it can break up file at label boundaries + .subsections_via_symbols + diff --git a/projects/compiler-rt/lib/arm/save_vfp_d8_d15_regs.S b/projects/compiler-rt/lib/arm/save_vfp_d8_d15_regs.S new file mode 100644 index 00000000..fbd21ba3 --- /dev/null +++ b/projects/compiler-rt/lib/arm/save_vfp_d8_d15_regs.S @@ -0,0 +1,35 @@ +//===-- save_restore_regs.S - Implement save/restore* ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// When compiling C++ functions that need to handle thrown exceptions the +// compiler is required to save all registers and call __Unwind_SjLj_Register +// in the function prolog. But when compiling for thumb1, there are +// no instructions to access the floating point registers, so the +// compiler needs to add a call to the helper function _save_vfp_d8_d15_regs +// written in ARM to save the float registers. In the epilog, the compiler +// must also add a call to __restore_vfp_d8_d15_regs to restore those registers. +// + + .text + .syntax unified + +// +// Save registers d8-d15 onto stack +// + .align 2 +DEFINE_COMPILERRT_PRIVATE_FUNCTION(__save_vfp_d8_d15_regs) + vstmdb sp!, {d8-d15} // push registers d8-d15 onto stack + bx lr // return to prolog + + // tell linker it can break up file at label boundaries + .subsections_via_symbols + diff --git a/projects/compiler-rt/lib/arm/softfloat-alias.list b/projects/compiler-rt/lib/arm/softfloat-alias.list new file mode 100644 index 00000000..cc6a4b3c --- /dev/null +++ b/projects/compiler-rt/lib/arm/softfloat-alias.list @@ -0,0 +1,21 @@ +# +# These are soft float functions which can be +# aliased to the *vfp functions on arm processors +# that support floating point instructions. +# +___adddf3vfp ___adddf3 +___addsf3vfp ___addsf3 +___divdf3vfp ___divdf3 +___divsf3vfp ___divsf3 +___extendsfdf2vfp ___extendsfdf2 +___fixdfsivfp ___fixdfsi +___fixsfsivfp ___fixsfsi +___floatsidfvfp ___floatsidf +___floatsisfvfp ___floatsisf +___muldf3vfp ___muldf3 +___mulsf3vfp ___mulsf3 +___subdf3vfp ___subdf3 +___subsf3vfp ___subsf3 +___truncdfsf2vfp ___truncdfsf2 +___floatunssidfvfp ___floatunsidf +___floatunssisfvfp ___floatunsisf diff --git a/projects/compiler-rt/lib/arm/subdf3vfp.S b/projects/compiler-rt/lib/arm/subdf3vfp.S new file mode 100644 index 00000000..3f88baac --- /dev/null +++ b/projects/compiler-rt/lib/arm/subdf3vfp.S @@ -0,0 +1,25 @@ +//===-- subdf3vfp.S - Implement subdf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern double __subdf3vfp(double a, double b); +// +// Returns difference between two double precision floating point numbers using +// the Darwin calling convention where double arguments are passsed in GPR pairs +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__subdf3vfp) + vmov d6, r0, r1 // move first param from r0/r1 pair into d6 + vmov d7, r2, r3 // move second param from r2/r3 pair into d7 + vsub.f64 d6, d6, d7 + vmov r0, r1, d6 // move result back to r0/r1 pair + bx lr diff --git a/projects/compiler-rt/lib/arm/subsf3vfp.S b/projects/compiler-rt/lib/arm/subsf3vfp.S new file mode 100644 index 00000000..ed02ba92 --- /dev/null +++ b/projects/compiler-rt/lib/arm/subsf3vfp.S @@ -0,0 +1,26 @@ +//===-- subsf3vfp.S - Implement subsf3vfp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __subsf3vfp(float a, float b); +// +// Returns the difference between two single precision floating point numbers +// using the Darwin calling convention where single arguments are passsed +// like 32-bit ints. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__subsf3vfp) + vmov s14, r0 // move first param from r0 into float register + vmov s15, r1 // move second param from r1 into float register + vsub.f32 s14, s14, s15 + vmov r0, s14 // move result back to r0 + bx lr diff --git a/projects/compiler-rt/lib/arm/switch16.S b/projects/compiler-rt/lib/arm/switch16.S new file mode 100644 index 00000000..9c3f0cf9 --- /dev/null +++ b/projects/compiler-rt/lib/arm/switch16.S @@ -0,0 +1,45 @@ +//===-- switch.S - Implement switch* --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// When compiling switch statements in thumb mode, the compiler +// can use these __switch* helper functions The compiler emits a blx to +// the __switch* function followed by a table of displacements for each +// case statement. On entry, R0 is the index into the table. The __switch* +// function uses the return address in lr to find the start of the table. +// The first entry in the table is the count of the entries in the table. +// It then uses R0 to index into the table and get the displacement of the +// address to jump to. If R0 is greater than the size of the table, it jumps +// to the last entry in the table. Each displacement in the table is actually +// the distance from lr to the label, thus making the tables PIC. + + + .text + .syntax unified + +// +// The table contains signed 2-byte sized elements which are 1/2 the distance +// from lr to the target label. +// + .align 2 +DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch16) + ldrh ip, [lr, #-1] // get first 16-bit word in table + cmp r0, ip // compare with index + add r0, lr, r0, lsl #1 // compute address of element in table + add ip, lr, ip, lsl #1 // compute address of last element in table + ite lo + ldrshlo r0, [r0, #1] // load 16-bit element if r0 is in range + ldrshhs r0, [ip, #1] // load 16-bit element if r0 out of range + add ip, lr, r0, lsl #1 // compute label = lr + element*2 + bx ip // jump to computed label + + // tell linker it can break up file at label boundaries + .subsections_via_symbols diff --git a/projects/compiler-rt/lib/arm/switch32.S b/projects/compiler-rt/lib/arm/switch32.S new file mode 100644 index 00000000..3152dfa1 --- /dev/null +++ b/projects/compiler-rt/lib/arm/switch32.S @@ -0,0 +1,47 @@ +//===-- switch.S - Implement switch* --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// When compiling switch statements in thumb mode, the compiler +// can use these __switch* helper functions The compiler emits a blx to +// the __switch* function followed by a table of displacements for each +// case statement. On entry, R0 is the index into the table. The __switch* +// function uses the return address in lr to find the start of the table. +// The first entry in the table is the count of the entries in the table. +// It then uses R0 to index into the table and get the displacement of the +// address to jump to. If R0 is greater than the size of the table, it jumps +// to the last entry in the table. Each displacement in the table is actually +// the distance from lr to the label, thus making the tables PIC. + + + .text + .syntax unified + +// +// The table contains signed 4-byte sized elements which are the distance +// from lr to the target label. +// + .align 2 +DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch32) + ldr ip, [lr, #-1] // get first 32-bit word in table + cmp r0, ip // compare with index + add r0, lr, r0, lsl #2 // compute address of element in table + add ip, lr, ip, lsl #2 // compute address of last element in table + ite lo + ldrlo r0, [r0, #3] // load 32-bit element if r0 is in range + ldrhs r0, [ip, #3] // load 32-bit element if r0 out of range + add ip, lr, r0 // compute label = lr + element + bx ip // jump to computed label + + + // tell linker it can break up file at label boundaries + .subsections_via_symbols + diff --git a/projects/compiler-rt/lib/arm/switch8.S b/projects/compiler-rt/lib/arm/switch8.S new file mode 100644 index 00000000..15729ebc --- /dev/null +++ b/projects/compiler-rt/lib/arm/switch8.S @@ -0,0 +1,44 @@ +//===-- switch.S - Implement switch* --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// When compiling switch statements in thumb mode, the compiler +// can use these __switch* helper functions The compiler emits a blx to +// the __switch* function followed by a table of displacements for each +// case statement. On entry, R0 is the index into the table. The __switch* +// function uses the return address in lr to find the start of the table. +// The first entry in the table is the count of the entries in the table. +// It then uses R0 to index into the table and get the displacement of the +// address to jump to. If R0 is greater than the size of the table, it jumps +// to the last entry in the table. Each displacement in the table is actually +// the distance from lr to the label, thus making the tables PIC. + + + .text + .syntax unified + +// +// The table contains signed byte sized elements which are 1/2 the distance +// from lr to the target label. +// + .align 2 +DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch8) + ldrb ip, [lr, #-1] // get first byte in table + cmp r0, ip // signed compare with index + ite lo + ldrsblo r0, [lr, r0] // get indexed byte out of table + ldrsbhs r0, [lr, ip] // if out of range, use last entry in table + add ip, lr, r0, lsl #1 // compute label = lr + element*2 + bx ip // jump to computed label + + // tell linker it can break up file at label boundaries + .subsections_via_symbols + diff --git a/projects/compiler-rt/lib/arm/switchu8.S b/projects/compiler-rt/lib/arm/switchu8.S new file mode 100644 index 00000000..0a4efac8 --- /dev/null +++ b/projects/compiler-rt/lib/arm/switchu8.S @@ -0,0 +1,44 @@ +//===-- switch.S - Implement switch* --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// When compiling switch statements in thumb mode, the compiler +// can use these __switch* helper functions The compiler emits a blx to +// the __switch* function followed by a table of displacements for each +// case statement. On entry, R0 is the index into the table. The __switch* +// function uses the return address in lr to find the start of the table. +// The first entry in the table is the count of the entries in the table. +// It then uses R0 to index into the table and get the displacement of the +// address to jump to. If R0 is greater than the size of the table, it jumps +// to the last entry in the table. Each displacement in the table is actually +// the distance from lr to the label, thus making the tables PIC. + + + .text + .syntax unified + +// +// The table contains unsigned byte sized elements which are 1/2 the distance +// from lr to the target label. +// + .align 2 +DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switchu8) + ldrb ip, [lr, #-1] // get first byte in table + cmp r0, ip // compare with index + ite lo + ldrblo r0, [lr, r0] // get indexed byte out of table + ldrbhs r0, [lr, ip] // if out of range, use last entry in table + add ip, lr, r0, lsl #1 // compute label = lr + element*2 + bx ip // jump to computed label + + // tell linker it can break up file at label boundaries + .subsections_via_symbols + diff --git a/projects/compiler-rt/lib/arm/sync_synchronize.S b/projects/compiler-rt/lib/arm/sync_synchronize.S new file mode 100644 index 00000000..06dade96 --- /dev/null +++ b/projects/compiler-rt/lib/arm/sync_synchronize.S @@ -0,0 +1,34 @@ +//===-- sync_synchronize - Implement memory barrier * ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// When compiling a use of the gcc built-in __sync_synchronize() in thumb1 mode +// the compiler may emit a call to __sync_synchronize. +// On Darwin the implementation jumps to an OS supplied function named +// OSMemoryBarrier +// + + .text + .syntax unified + +#if __APPLE__ + + .align 2 +DEFINE_COMPILERRT_PRIVATE_FUNCTION(__sync_synchronize) + stmfd sp!, {r7, lr} + add r7, sp, #0 + bl _OSMemoryBarrier + ldmfd sp!, {r7, pc} + + // tell linker it can break up file at label boundaries + .subsections_via_symbols + +#endif diff --git a/projects/compiler-rt/lib/arm/truncdfsf2vfp.S b/projects/compiler-rt/lib/arm/truncdfsf2vfp.S new file mode 100644 index 00000000..371aee94 --- /dev/null +++ b/projects/compiler-rt/lib/arm/truncdfsf2vfp.S @@ -0,0 +1,25 @@ +//===-- truncdfsf2vfp.S - Implement truncdfsf2vfp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern float __truncdfsf2vfp(double a); +// +// Converts double precision float to signle precision result. +// Uses Darwin calling convention where a double precision parameter is +// passed in a R0/R1 pair and a signle precision result is returned in R0. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__truncdfsf2vfp) + vmov d7, r0, r1 // load double from r0/r1 pair + vcvt.f32.f64 s15, d7 // convert double to single (trucate precision) + vmov r0, s15 // return result in r0 + bx lr diff --git a/projects/compiler-rt/lib/arm/udivmodsi4.S b/projects/compiler-rt/lib/arm/udivmodsi4.S new file mode 100644 index 00000000..aee27766 --- /dev/null +++ b/projects/compiler-rt/lib/arm/udivmodsi4.S @@ -0,0 +1,96 @@ +/*===-- udivmodsi4.S - 32-bit unsigned integer divide and modulus ---------===// + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------===// + * + * This file implements the __udivmodsi4 (32-bit unsigned integer divide and + * modulus) function for the ARM architecture. A naive digit-by-digit + * computation is employed for simplicity. + * + *===----------------------------------------------------------------------===*/ + +#include "../assembly.h" + +#define ESTABLISH_FRAME \ + push {r4, r7, lr} ;\ + add r7, sp, #4 +#define CLEAR_FRAME_AND_RETURN \ + pop {r4, r7, pc} + +#define a r0 +#define b r1 +#define i r3 +#define r r4 +#define q ip +#define one lr + +.syntax unified +.align 3 +DEFINE_COMPILERRT_FUNCTION(__udivmodsi4) +#if __ARM_ARCH_EXT_IDIV__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + mov r3, r0 + udiv r0, r3, r1 + mls r1, r0, r1, r3 + str r1, [r2] + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else +// We use a simple digit by digit algorithm; before we get into the actual +// divide loop, we must calculate the left-shift amount necessary to align +// the MSB of the divisor with that of the dividend (If this shift is +// negative, then the result is zero, and we early out). We also conjure a +// bit mask of 1 to use in constructing the quotient, and initialize the +// quotient to zero. + ESTABLISH_FRAME + clz r4, a + tst b, b // detect divide-by-zero + clz r3, b + mov q, #0 + beq LOCAL_LABEL(return) // return 0 if b is zero. + mov one, #1 + subs i, r3, r4 + blt LOCAL_LABEL(return) // return 0 if MSB(a) < MSB(b) + +LOCAL_LABEL(mainLoop): +// This loop basically implements the following: +// +// do { +// if (a >= b << i) { +// a -= b << i; +// q |= 1 << i; +// if (a == 0) break; +// } +// } while (--i) +// +// Note that this does not perform the final iteration (i == 0); by doing it +// this way, we can merge the two branches which is a substantial win for +// such a tight loop on current ARM architectures. + subs r, a, b, lsl i + itt hs + orrhs q, q,one, lsl i + movhs a, r + it ne + subsne i, i, #1 + bhi LOCAL_LABEL(mainLoop) + +// Do the final test subtraction and update of quotient (i == 0), as it is +// not performed in the main loop. + subs r, a, b + itt hs + orrhs q, #1 + movhs a, r + +LOCAL_LABEL(return): +// Store the remainder, and move the quotient to r0, then return. + str a, [r2] + mov r0, q + CLEAR_FRAME_AND_RETURN +#endif diff --git a/projects/compiler-rt/lib/arm/udivsi3.S b/projects/compiler-rt/lib/arm/udivsi3.S new file mode 100644 index 00000000..2bb14123 --- /dev/null +++ b/projects/compiler-rt/lib/arm/udivsi3.S @@ -0,0 +1,93 @@ +/*===-- udivsi3.S - 32-bit unsigned integer divide ------------------------===// + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------===// + * + * This file implements the __udivsi3 (32-bit unsigned integer divide) + * function for the ARM architecture. A naive digit-by-digit computation is + * employed for simplicity. + * + *===----------------------------------------------------------------------===*/ + +#include "../assembly.h" + +#define ESTABLISH_FRAME \ + push {r7, lr} ;\ + mov r7, sp +#define CLEAR_FRAME_AND_RETURN \ + pop {r7, pc} + +#define a r0 +#define b r1 +#define r r2 +#define i r3 +#define q ip +#define one lr + +.syntax unified +.align 3 +// Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine. +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3) +DEFINE_COMPILERRT_FUNCTION(__udivsi3) +#if __ARM_ARCH_EXT_IDIV__ + tst r1,r1 + beq LOCAL_LABEL(divzero) + udiv r0, r0, r1 + bx lr + LOCAL_LABEL(divzero): + mov r0,#0 + bx lr +#else +// We use a simple digit by digit algorithm; before we get into the actual +// divide loop, we must calculate the left-shift amount necessary to align +// the MSB of the divisor with that of the dividend (If this shift is +// negative, then the result is zero, and we early out). We also conjure a +// bit mask of 1 to use in constructing the quotient, and initialize the +// quotient to zero. + ESTABLISH_FRAME + clz r2, a + tst b, b // detect divide-by-zero + clz r3, b + mov q, #0 + beq LOCAL_LABEL(return) // return 0 if b is zero. + mov one, #1 + subs i, r3, r2 + blt LOCAL_LABEL(return) // return 0 if MSB(a) < MSB(b) + +LOCAL_LABEL(mainLoop): +// This loop basically implements the following: +// +// do { +// if (a >= b << i) { +// a -= b << i; +// q |= 1 << i; +// if (a == 0) break; +// } +// } while (--i) +// +// Note that this does not perform the final iteration (i == 0); by doing it +// this way, we can merge the two branches which is a substantial win for +// such a tight loop on current ARM architectures. + subs r, a, b, lsl i + itt hs + orrhs q, q,one, lsl i + movhs a, r + it ne + subsne i, i, #1 + bhi LOCAL_LABEL(mainLoop) + +// Do the final test subtraction and update of quotient (i == 0), as it is +// not performed in the main loop. + subs r, a, b + it hs + orrhs q, #1 + +LOCAL_LABEL(return): +// Move the quotient to r0 and return. + mov r0, q + CLEAR_FRAME_AND_RETURN +#endif diff --git a/projects/compiler-rt/lib/arm/umodsi3.S b/projects/compiler-rt/lib/arm/umodsi3.S new file mode 100644 index 00000000..092a4f1a --- /dev/null +++ b/projects/compiler-rt/lib/arm/umodsi3.S @@ -0,0 +1,72 @@ +/*===-- umodsi3.S - 32-bit unsigned integer modulus -----------------------===// + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------===// + * + * This file implements the __umodsi3 (32-bit unsigned integer modulus) + * function for the ARM architecture. A naive digit-by-digit computation is + * employed for simplicity. + * + *===----------------------------------------------------------------------===*/ + +#include "../assembly.h" + +#define a r0 +#define b r1 +#define r r2 +#define i r3 + +.syntax unified +.align 3 +DEFINE_COMPILERRT_FUNCTION(__umodsi3) +#if __ARM_ARCH_EXT_IDIV__ + tst r1, r1 + beq LOCAL_LABEL(divzero) + udiv r2, r0, r1 + mls r0, r2, r1, r0 + bx lr +LOCAL_LABEL(divzero): + mov r0, #0 + bx lr +#else +// We use a simple digit by digit algorithm; before we get into the actual +// divide loop, we must calculate the left-shift amount necessary to align +// the MSB of the divisor with that of the dividend. + clz r2, a + tst b, b // detect b == 0 + clz r3, b + bxeq lr // return a if b == 0 + subs i, r3, r2 + bxlt lr // return a if MSB(a) < MSB(b) + +LOCAL_LABEL(mainLoop): +// This loop basically implements the following: +// +// do { +// if (a >= b << i) { +// a -= b << i; +// if (a == 0) break; +// } +// } while (--i) +// +// Note that this does not perform the final iteration (i == 0); by doing it +// this way, we can merge the two branches which is a substantial win for +// such a tight loop on current ARM architectures. + subs r, a, b, lsl i + it hs + movhs a, r + it ne + subsne i, i, #1 + bhi LOCAL_LABEL(mainLoop) + +// Do the final test subtraction and update of remainder (i == 0), as it is +// not performed in the main loop. + subs r, a, b + it hs + movhs a, r + bx lr +#endif diff --git a/projects/compiler-rt/lib/arm/unorddf2vfp.S b/projects/compiler-rt/lib/arm/unorddf2vfp.S new file mode 100644 index 00000000..c49e55f2 --- /dev/null +++ b/projects/compiler-rt/lib/arm/unorddf2vfp.S @@ -0,0 +1,28 @@ +//===-- unorddf2vfp.S - Implement unorddf2vfp ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __unorddf2vfp(double a, double b); +// +// Returns one iff a or b is NaN +// Uses Darwin calling convention where double precision arguments are passsed +// like in GPR pairs. +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__unorddf2vfp) + vmov d6, r0, r1 // load r0/r1 pair in double register + vmov d7, r2, r3 // load r2/r3 pair in double register + vcmp.f64 d6, d7 + vmrs apsr_nzcv, fpscr + movvs r0, #1 // set result register to 1 if "overflow" (any NaNs) + movvc r0, #0 + bx lr diff --git a/projects/compiler-rt/lib/arm/unordsf2vfp.S b/projects/compiler-rt/lib/arm/unordsf2vfp.S new file mode 100644 index 00000000..0ab27edf --- /dev/null +++ b/projects/compiler-rt/lib/arm/unordsf2vfp.S @@ -0,0 +1,29 @@ +//===-- unordsf2vfp.S - Implement unordsf2vfp -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// extern int __unordsf2vfp(float a, float b); +// +// Returns one iff a or b is NaN +// Uses Darwin calling convention where single precision arguments are passsed +// like 32-bit ints +// + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__unordsf2vfp) + vmov s14, r0 // move from GPR 0 to float register + vmov s15, r1 // move from GPR 1 to float register + vcmp.f32 s14, s15 + vmrs apsr_nzcv, fpscr + movvs r0, #1 // set result register to 1 if "overflow" (any NaNs) + movvc r0, #0 + bx lr + diff --git a/projects/compiler-rt/lib/asan/CMakeLists.txt b/projects/compiler-rt/lib/asan/CMakeLists.txt new file mode 100644 index 00000000..ad3f0548 --- /dev/null +++ b/projects/compiler-rt/lib/asan/CMakeLists.txt @@ -0,0 +1,154 @@ +# Build for the AddressSanitizer runtime support library. + +set(ASAN_SOURCES + asan_allocator2.cc + asan_fake_stack.cc + asan_globals.cc + asan_interceptors.cc + asan_linux.cc + asan_mac.cc + asan_malloc_linux.cc + asan_malloc_mac.cc + asan_malloc_win.cc + asan_new_delete.cc + asan_poisoning.cc + asan_posix.cc + asan_preinit.cc + asan_report.cc + asan_rtl.cc + asan_stack.cc + asan_stats.cc + asan_thread.cc + asan_win.cc) + +include_directories(..) + +if (NOT MSVC) + set(ASAN_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti) +else() + set(ASAN_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + /GR-) +endif() + +set(ASAN_COMMON_DEFINITIONS + ASAN_HAS_EXCEPTIONS=1) + +if(ANDROID) + list(APPEND ASAN_COMMON_DEFINITIONS + ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 + ASAN_NEEDS_SEGV=0 + ASAN_LOW_MEMORY=1) +elseif(MSVC) + list(APPEND ASAN_COMMON_DEFINITIONS + ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 + ASAN_NEEDS_SEGV=0) +else() + list(APPEND ASAN_COMMON_DEFINITIONS + ASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 + ASAN_NEEDS_SEGV=1) +endif() + +# Architectures supported by ASan. +filter_available_targets(ASAN_SUPPORTED_ARCH + x86_64 i386 powerpc64) + +# Compile ASan sources into an object library. +if(APPLE) + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTAsan ${os} + ARCH ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_SOURCES} + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + endforeach() +elseif(ANDROID) + add_library(RTAsan.arm.android OBJECT ${ASAN_SOURCES}) + set_target_compile_flags(RTAsan.arm.android ${ASAN_CFLAGS}) + set_property(TARGET RTAsan.arm.android APPEND PROPERTY + COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS}) +else() + foreach(arch ${ASAN_SUPPORTED_ARCH}) + add_compiler_rt_object_library(RTAsan ${arch} + SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + endforeach() +endif() + +# Build ASan runtimes shipped with Clang. +set(ASAN_RUNTIME_LIBRARIES) +if(APPLE) + foreach (os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + # Dynamic lookup is needed because shadow scale and offset are + # provided by the instrumented modules. + set(ASAN_RUNTIME_LDFLAGS + "-undefined dynamic_lookup") + add_compiler_rt_darwin_dynamic_runtime(clang_rt.asan_${os}_dynamic ${os} + ARCH ${ASAN_SUPPORTED_ARCH} + SOURCES $ + $ + $ + $ + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS} + LINKFLAGS ${ASAN_RUNTIME_LDFLAGS}) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_${os}_dynamic) + endforeach() + +elseif(ANDROID) + add_library(clang_rt.asan-arm-android SHARED + $ + $ + $) + set_target_compile_flags(clang_rt.asan-arm-android + ${ASAN_CFLAGS}) + set_property(TARGET clang_rt.asan-arm-android APPEND PROPERTY + COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS}) + target_link_libraries(clang_rt.asan-arm-android dl) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-arm-android) +else() + # Build separate libraries for each target. + foreach(arch ${ASAN_SUPPORTED_ARCH}) + set(ASAN_RUNTIME_OBJECTS + $ + $ + $ + $) + if (NOT WIN32) + # We can't build Leak Sanitizer on Windows yet. + list(APPEND ASAN_RUNTIME_OBJECTS $) + endif() + + add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch} + SOURCES ${ASAN_RUNTIME_OBJECTS} + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch}) + if (UNIX AND NOT ${arch} STREQUAL "i386") + add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch}-symbols) + endif() + + if (WIN32) + add_compiler_rt_static_runtime(clang_rt.asan_dll_thunk-${arch} ${arch} + SOURCES asan_dll_thunk.cc + CFLAGS ${ASAN_CFLAGS} -DASAN_DLL_THUNK + DEFS ${ASAN_COMMON_DEFINITIONS}) + list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_dll_thunk-${arch}) + endif() + endforeach() +endif() + +add_compiler_rt_resource_file(asan_blacklist asan_blacklist.txt) + +# All ASan runtime dependencies. +add_custom_target(asan_runtime_libraries + DEPENDS asan_blacklist ${ASAN_RUNTIME_LIBRARIES}) + +if(LLVM_INCLUDE_TESTS) + add_subdirectory(tests) +endif() + +add_subdirectory(lit_tests) diff --git a/projects/compiler-rt/lib/asan/Makefile.mk b/projects/compiler-rt/lib/asan/Makefile.mk new file mode 100644 index 00000000..97da64be --- /dev/null +++ b/projects/compiler-rt/lib/asan/Makefile.mk @@ -0,0 +1,24 @@ +#===- lib/asan/Makefile.mk ---------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := asan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../interception/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the asan functions. +AsanFunctions := $(Sources:%.cc=%) diff --git a/projects/compiler-rt/lib/asan/README.txt b/projects/compiler-rt/lib/asan/README.txt new file mode 100644 index 00000000..e4f4961c --- /dev/null +++ b/projects/compiler-rt/lib/asan/README.txt @@ -0,0 +1,29 @@ +AddressSanitizer RT +================================ +This directory contains sources of the AddressSanitizer (asan) run-time library. +We are in the process of integrating AddressSanitizer with LLVM, stay tuned. + +Directory structre: +README.txt : This file. +Makefile.mk : File for make-based build. +CMakeLists.txt : File for cmake-based build. +asan_*.{cc,h} : Sources of the asan run-time lirbary. +scripts/* : Helper scripts. +tests/* : ASan unit tests. +lit_tests/* : ASan output tests. + +Also ASan runtime needs the following libraries: +lib/interception/ : Machinery used to intercept function calls. +lib/sanitizer_common/ : Code shared between ASan and TSan. + +Currently ASan runtime can be built by both make and cmake build systems. +(see compiler-rt/make and files Makefile.mk for make-based build and +files CMakeLists.txt for cmake-based build). + +ASan unit and output tests work only with cmake. You may run this +command from the root of your cmake build tree: + +make check-asan + +For more instructions see: +http://code.google.com/p/address-sanitizer/wiki/HowToBuild diff --git a/projects/compiler-rt/lib/asan/asan.syms.extra b/projects/compiler-rt/lib/asan/asan.syms.extra new file mode 100644 index 00000000..007aafe3 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan.syms.extra @@ -0,0 +1,3 @@ +__asan_* +__lsan_* +__ubsan_* diff --git a/projects/compiler-rt/lib/asan/asan_allocator.h b/projects/compiler-rt/lib/asan/asan_allocator.h new file mode 100644 index 00000000..c5fcbbb5 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_allocator.h @@ -0,0 +1,127 @@ +//===-- asan_allocator.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_allocator2.cc. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_ALLOCATOR_H +#define ASAN_ALLOCATOR_H + +#include "asan_internal.h" +#include "asan_interceptors.h" +#include "sanitizer_common/sanitizer_list.h" + +namespace __asan { + +enum AllocType { + FROM_MALLOC = 1, // Memory block came from malloc, calloc, realloc, etc. + FROM_NEW = 2, // Memory block came from operator new. + FROM_NEW_BR = 3 // Memory block came from operator new [ ] +}; + +static const uptr kNumberOfSizeClasses = 255; +struct AsanChunk; + +void InitializeAllocator(); + +class AsanChunkView { + public: + explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} + bool IsValid(); // Checks if AsanChunkView points to a valid allocated + // or quarantined chunk. + uptr Beg(); // First byte of user memory. + uptr End(); // Last byte of user memory. + uptr UsedSize(); // Size requested by the user. + uptr AllocTid(); + uptr FreeTid(); + void GetAllocStack(StackTrace *stack); + void GetFreeStack(StackTrace *stack); + bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) { + if (addr >= Beg() && (addr + access_size) <= End()) { + *offset = addr - Beg(); + return true; + } + return false; + } + bool AddrIsAtLeft(uptr addr, uptr access_size, sptr *offset) { + (void)access_size; + if (addr < Beg()) { + *offset = Beg() - addr; + return true; + } + return false; + } + bool AddrIsAtRight(uptr addr, uptr access_size, sptr *offset) { + if (addr + access_size > End()) { + *offset = addr - End(); + return true; + } + return false; + } + + private: + AsanChunk *const chunk_; +}; + +AsanChunkView FindHeapChunkByAddress(uptr address); + +// List of AsanChunks with total size. +class AsanChunkFifoList: public IntrusiveList { + public: + explicit AsanChunkFifoList(LinkerInitialized) { } + AsanChunkFifoList() { clear(); } + void Push(AsanChunk *n); + void PushList(AsanChunkFifoList *q); + AsanChunk *Pop(); + uptr size() { return size_; } + void clear() { + IntrusiveList::clear(); + size_ = 0; + } + private: + uptr size_; +}; + +struct AsanThreadLocalMallocStorage { + explicit AsanThreadLocalMallocStorage(LinkerInitialized x) + { } + AsanThreadLocalMallocStorage() { + CHECK(REAL(memset)); + REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage)); + } + + uptr quarantine_cache[16]; + uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque. + void CommitBack(); +}; + +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type); +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type); + +void *asan_malloc(uptr size, StackTrace *stack); +void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack); +void *asan_realloc(void *p, uptr size, StackTrace *stack); +void *asan_valloc(uptr size, StackTrace *stack); +void *asan_pvalloc(uptr size, StackTrace *stack); + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + StackTrace *stack); +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp); + +uptr asan_mz_size(const void *ptr); +void asan_mz_force_lock(); +void asan_mz_force_unlock(); + +void PrintInternalAllocatorStats(); + +} // namespace __asan +#endif // ASAN_ALLOCATOR_H diff --git a/projects/compiler-rt/lib/asan/asan_allocator2.cc b/projects/compiler-rt/lib/asan/asan_allocator2.cc new file mode 100644 index 00000000..7a29975d --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_allocator2.cc @@ -0,0 +1,813 @@ +//===-- asan_allocator2.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Implementation of ASan's memory allocator, 2-nd version. +// This variant uses the allocator from sanitizer_common, i.e. the one shared +// with ThreadSanitizer and MemorySanitizer. +// +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" + +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_list.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_quarantine.h" +#include "lsan/lsan_common.h" + +namespace __asan { + +struct AsanMapUnmapCallback { + void OnMap(uptr p, uptr size) const { + PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.mmaps++; + thread_stats.mmaped += size; + } + void OnUnmap(uptr p, uptr size) const { + PoisonShadow(p, size, 0); + // We are about to unmap a chunk of user memory. + // Mark the corresponding shadow memory as not needed. + // Since asan's mapping is compacting, the shadow chunk may be + // not page-aligned, so we only flush the page-aligned portion. + uptr page_size = GetPageSizeCached(); + uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size); + uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size); + FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.munmaps++; + thread_stats.munmaped += size; + } +}; + +#if SANITIZER_WORDSIZE == 64 +#if defined(__powerpc64__) +const uptr kAllocatorSpace = 0xa0000000000ULL; +const uptr kAllocatorSize = 0x20000000000ULL; // 2T. +#else +const uptr kAllocatorSpace = 0x600000000000ULL; +const uptr kAllocatorSize = 0x40000000000ULL; // 4T. +#endif +typedef DefaultSizeClassMap SizeClassMap; +typedef SizeClassAllocator64 PrimaryAllocator; +#elif SANITIZER_WORDSIZE == 32 +static const u64 kAddressSpaceSize = 1ULL << 32; +typedef CompactSizeClassMap SizeClassMap; +static const uptr kRegionSizeLog = 20; +static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog; +typedef SizeClassAllocator32<0, kAddressSpaceSize, 16, + SizeClassMap, kRegionSizeLog, + FlatByteMap, + AsanMapUnmapCallback> PrimaryAllocator; +#endif + +typedef SizeClassAllocatorLocalCache AllocatorCache; +typedef LargeMmapAllocator SecondaryAllocator; +typedef CombinedAllocator Allocator; + +// We can not use THREADLOCAL because it is not supported on some of the +// platforms we care about (OSX 10.6, Android). +// static THREADLOCAL AllocatorCache cache; +AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator2_cache)); + return reinterpret_cast(ms->allocator2_cache); +} + +static Allocator allocator; + +static const uptr kMaxAllowedMallocSize = + FIRST_32_SECOND_64(3UL << 30, 64UL << 30); + +static const uptr kMaxThreadLocalQuarantine = + FIRST_32_SECOND_64(1 << 18, 1 << 20); + +// Every chunk of memory allocated by this allocator can be in one of 3 states: +// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. +// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. +// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. +enum { + CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it. + CHUNK_ALLOCATED = 2, + CHUNK_QUARANTINE = 3 +}; + +// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. +// We use adaptive redzones: for larger allocation larger redzones are used. +static u32 RZLog2Size(u32 rz_log) { + CHECK_LT(rz_log, 8); + return 16 << rz_log; +} + +static u32 RZSize2Log(u32 rz_size) { + CHECK_GE(rz_size, 16); + CHECK_LE(rz_size, 2048); + CHECK(IsPowerOfTwo(rz_size)); + u32 res = Log2(rz_size) - 4; + CHECK_EQ(rz_size, RZLog2Size(res)); + return res; +} + +static uptr ComputeRZLog(uptr user_requested_size) { + u32 rz_log = + user_requested_size <= 64 - 16 ? 0 : + user_requested_size <= 128 - 32 ? 1 : + user_requested_size <= 512 - 64 ? 2 : + user_requested_size <= 4096 - 128 ? 3 : + user_requested_size <= (1 << 14) - 256 ? 4 : + user_requested_size <= (1 << 15) - 512 ? 5 : + user_requested_size <= (1 << 16) - 1024 ? 6 : 7; + return Max(rz_log, RZSize2Log(flags()->redzone)); +} + +// The memory chunk allocated from the underlying allocator looks like this: +// L L L L L L H H U U U U U U R R +// L -- left redzone words (0 or more bytes) +// H -- ChunkHeader (16 bytes), which is also a part of the left redzone. +// U -- user memory. +// R -- right redzone (0 or more bytes) +// ChunkBase consists of ChunkHeader and other bytes that overlap with user +// memory. + +// If the left redzone is greater than the ChunkHeader size we store a magic +// value in the first uptr word of the memory block and store the address of +// ChunkBase in the next uptr. +// M B L L L L L L L L L H H U U U U U U +// | ^ +// ---------------------| +// M -- magic value kAllocBegMagic +// B -- address of ChunkHeader pointing to the first 'H' +static const uptr kAllocBegMagic = 0xCC6E96B9; + +struct ChunkHeader { + // 1-st 8 bytes. + u32 chunk_state : 8; // Must be first. + u32 alloc_tid : 24; + + u32 free_tid : 24; + u32 from_memalign : 1; + u32 alloc_type : 2; + u32 rz_log : 3; + u32 lsan_tag : 2; + // 2-nd 8 bytes + // This field is used for small sizes. For large sizes it is equal to + // SizeClassMap::kMaxSize and the actual size is stored in the + // SecondaryAllocator's metadata. + u32 user_requested_size; + u32 alloc_context_id; +}; + +struct ChunkBase : ChunkHeader { + // Header2, intersects with user memory. + u32 free_context_id; +}; + +static const uptr kChunkHeaderSize = sizeof(ChunkHeader); +static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; +COMPILER_CHECK(kChunkHeaderSize == 16); +COMPILER_CHECK(kChunkHeader2Size <= 16); + +struct AsanChunk: ChunkBase { + uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } + uptr UsedSize(bool locked_version = false) { + if (user_requested_size != SizeClassMap::kMaxSize) + return user_requested_size; + return *reinterpret_cast( + allocator.GetMetaData(AllocBeg(locked_version))); + } + void *AllocBeg(bool locked_version = false) { + if (from_memalign) { + if (locked_version) + return allocator.GetBlockBeginFastLocked( + reinterpret_cast(this)); + return allocator.GetBlockBegin(reinterpret_cast(this)); + } + return reinterpret_cast(Beg() - RZLog2Size(rz_log)); + } + // If we don't use stack depot, we store the alloc/free stack traces + // in the chunk itself. + u32 *AllocStackBeg() { + return (u32*)(Beg() - RZLog2Size(rz_log)); + } + uptr AllocStackSize() { + CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize); + return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32); + } + u32 *FreeStackBeg() { + return (u32*)(Beg() + kChunkHeader2Size); + } + uptr FreeStackSize() { + if (user_requested_size < kChunkHeader2Size) return 0; + uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY); + return (available - kChunkHeader2Size) / sizeof(u32); + } + bool AddrIsInside(uptr addr, bool locked_version = false) { + return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); + } +}; + +bool AsanChunkView::IsValid() { + return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; +} +uptr AsanChunkView::Beg() { return chunk_->Beg(); } +uptr AsanChunkView::End() { return Beg() + UsedSize(); } +uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); } +uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } +uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } + +static void GetStackTraceFromId(u32 id, StackTrace *stack) { + CHECK(id); + uptr size = 0; + const uptr *trace = StackDepotGet(id, &size); + CHECK(trace); + stack->CopyFrom(trace, size); +} + +void AsanChunkView::GetAllocStack(StackTrace *stack) { + GetStackTraceFromId(chunk_->alloc_context_id, stack); +} + +void AsanChunkView::GetFreeStack(StackTrace *stack) { + GetStackTraceFromId(chunk_->free_context_id, stack); +} + +struct QuarantineCallback; +typedef Quarantine AsanQuarantine; +typedef AsanQuarantine::Cache QuarantineCache; +static AsanQuarantine quarantine(LINKER_INITIALIZED); +static QuarantineCache fallback_quarantine_cache(LINKER_INITIALIZED); +static AllocatorCache fallback_allocator_cache; +static SpinMutex fallback_mutex; + +QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); + return reinterpret_cast(ms->quarantine_cache); +} + +struct QuarantineCallback { + explicit QuarantineCallback(AllocatorCache *cache) + : cache_(cache) { + } + + void Recycle(AsanChunk *m) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); + CHECK_NE(m->alloc_tid, kInvalidTid); + CHECK_NE(m->free_tid, kInvalidTid); + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapLeftRedzoneMagic); + void *p = reinterpret_cast(m->AllocBeg()); + if (p != m) { + uptr *alloc_magic = reinterpret_cast(p); + CHECK_EQ(alloc_magic[0], kAllocBegMagic); + // Clear the magic value, as allocator internals may overwrite the + // contents of deallocated chunk, confusing GetAsanChunk lookup. + alloc_magic[0] = 0; + CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); + } + + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.real_frees++; + thread_stats.really_freed += m->UsedSize(); + + allocator.Deallocate(cache_, p); + } + + void *Allocate(uptr size) { + return allocator.Allocate(cache_, size, 1, false); + } + + void Deallocate(void *p) { + allocator.Deallocate(cache_, p); + } + + AllocatorCache *cache_; +}; + +void InitializeAllocator() { + allocator.Init(); + quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); +} + +static void *Allocate(uptr size, uptr alignment, StackTrace *stack, + AllocType alloc_type, bool can_fill) { + if (!asan_inited) + __asan_init(); + Flags &fl = *flags(); + CHECK(stack); + const uptr min_alignment = SHADOW_GRANULARITY; + if (alignment < min_alignment) + alignment = min_alignment; + if (size == 0) { + // We'd be happy to avoid allocating memory for zero-size requests, but + // some programs/tests depend on this behavior and assume that malloc would + // not return NULL even for zero-size allocations. Moreover, it looks like + // operator new should never return NULL, and results of consecutive "new" + // calls must be different even if the allocated size is zero. + size = 1; + } + CHECK(IsPowerOfTwo(alignment)); + uptr rz_log = ComputeRZLog(size); + uptr rz_size = RZLog2Size(rz_log); + uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); + uptr needed_size = rounded_size + rz_size; + if (alignment > min_alignment) + needed_size += alignment; + bool using_primary_allocator = true; + // If we are allocating from the secondary allocator, there will be no + // automatic right redzone, so add the right redzone manually. + if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { + needed_size += rz_size; + using_primary_allocator = false; + } + CHECK(IsAligned(needed_size, min_alignment)); + if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { + Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", + (void*)size); + return AllocatorReturnNull(); + } + + AsanThread *t = GetCurrentThread(); + void *allocated; + if (t) { + AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); + allocated = allocator.Allocate(cache, needed_size, 8, false); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *cache = &fallback_allocator_cache; + allocated = allocator.Allocate(cache, needed_size, 8, false); + } + uptr alloc_beg = reinterpret_cast(allocated); + uptr alloc_end = alloc_beg + needed_size; + uptr beg_plus_redzone = alloc_beg + rz_size; + uptr user_beg = beg_plus_redzone; + if (!IsAligned(user_beg, alignment)) + user_beg = RoundUpTo(user_beg, alignment); + uptr user_end = user_beg + size; + CHECK_LE(user_end, alloc_end); + uptr chunk_beg = user_beg - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + m->alloc_type = alloc_type; + m->rz_log = rz_log; + u32 alloc_tid = t ? t->tid() : 0; + m->alloc_tid = alloc_tid; + CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? + m->free_tid = kInvalidTid; + m->from_memalign = user_beg != beg_plus_redzone; + if (alloc_beg != chunk_beg) { + CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); + reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; + reinterpret_cast(alloc_beg)[1] = chunk_beg; + } + if (using_primary_allocator) { + CHECK(size); + m->user_requested_size = size; + CHECK(allocator.FromPrimary(allocated)); + } else { + CHECK(!allocator.FromPrimary(allocated)); + m->user_requested_size = SizeClassMap::kMaxSize; + uptr *meta = reinterpret_cast(allocator.GetMetaData(allocated)); + meta[0] = size; + meta[1] = chunk_beg; + } + + m->alloc_context_id = StackDepotPut(stack->trace, stack->size); + + uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY); + // Unpoison the bulk of the memory region. + if (size_rounded_down_to_granularity) + PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); + // Deal with the end of the region if size is not aligned to granularity. + if (size != size_rounded_down_to_granularity && fl.poison_heap) { + u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); + *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; + } + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.mallocs++; + thread_stats.malloced += size; + thread_stats.malloced_redzones += needed_size - size; + uptr class_id = Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size)); + thread_stats.malloced_by_size[class_id]++; + if (needed_size > SizeClassMap::kMaxSize) + thread_stats.malloc_large++; + + void *res = reinterpret_cast(user_beg); + if (can_fill && fl.max_malloc_fill_size) { + uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); + REAL(memset)(res, fl.malloc_fill_byte, fill_size); + } +#if CAN_SANITIZE_LEAKS + m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored + : __lsan::kDirectlyLeaked; +#endif + // Must be the last mutation of metadata in this function. + atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); + ASAN_MALLOC_HOOK(res, size); + return res; +} + +static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) { + if (chunk_state == CHUNK_QUARANTINE) + ReportDoubleFree((uptr)ptr, stack); + else + ReportFreeNotMalloced((uptr)ptr, stack); +} + +static void AtomicallySetQuarantineFlag(AsanChunk *m, + void *ptr, StackTrace *stack) { + u8 old_chunk_state = CHUNK_ALLOCATED; + // Flip the chunk_state atomically to avoid race on double-free. + if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, + CHUNK_QUARANTINE, memory_order_acquire)) + ReportInvalidFree(ptr, old_chunk_state, stack); + CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); +} + +// Expects the chunk to already be marked as quarantined by using +// AtomicallySetQuarantineFlag. +static void QuarantineChunk(AsanChunk *m, void *ptr, + StackTrace *stack, AllocType alloc_type) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + + if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) + ReportAllocTypeMismatch((uptr)ptr, stack, + (AllocType)m->alloc_type, (AllocType)alloc_type); + + CHECK_GE(m->alloc_tid, 0); + if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. + CHECK_EQ(m->free_tid, kInvalidTid); + AsanThread *t = GetCurrentThread(); + m->free_tid = t ? t->tid() : 0; + m->free_context_id = StackDepotPut(stack->trace, stack->size); + // Poison the region. + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapFreeMagic); + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.frees++; + thread_stats.freed += m->UsedSize(); + + // Push into quarantine. + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + AllocatorCache *ac = GetAllocatorCache(ms); + quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac), + m, m->UsedSize()); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *ac = &fallback_allocator_cache; + quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), + m, m->UsedSize()); + } +} + +static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { + uptr p = reinterpret_cast(ptr); + if (p == 0) return; + + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + ASAN_FREE_HOOK(ptr); + // Must mark the chunk as quarantined before any changes to its metadata. + AtomicallySetQuarantineFlag(m, ptr, stack); + QuarantineChunk(m, ptr, stack, alloc_type); +} + +static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) { + CHECK(old_ptr && new_size); + uptr p = reinterpret_cast(old_ptr); + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.reallocs++; + thread_stats.realloced += new_size; + + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); + if (new_ptr) { + u8 chunk_state = m->chunk_state; + if (chunk_state != CHUNK_ALLOCATED) + ReportInvalidFree(old_ptr, chunk_state, stack); + CHECK_NE(REAL(memcpy), (void*)0); + uptr memcpy_size = Min(new_size, m->UsedSize()); + // If realloc() races with free(), we may start copying freed memory. + // However, we will report racy double-free later anyway. + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + Deallocate(old_ptr, stack, FROM_MALLOC); + } + return new_ptr; +} + +// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). +static AsanChunk *GetAsanChunk(void *alloc_beg) { + if (!alloc_beg) return 0; + if (!allocator.FromPrimary(alloc_beg)) { + uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); + AsanChunk *m = reinterpret_cast(meta[1]); + return m; + } + uptr *alloc_magic = reinterpret_cast(alloc_beg); + if (alloc_magic[0] == kAllocBegMagic) + return reinterpret_cast(alloc_magic[1]); + return reinterpret_cast(alloc_beg); +} + +static AsanChunk *GetAsanChunkByAddr(uptr p) { + void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); +} + +// Allocator must be locked when this function is called. +static AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { + void *alloc_beg = + allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); +} + +static uptr AllocationSize(uptr p) { + AsanChunk *m = GetAsanChunkByAddr(p); + if (!m) return 0; + if (m->chunk_state != CHUNK_ALLOCATED) return 0; + if (m->Beg() != p) return 0; + return m->UsedSize(); +} + +// We have an address between two chunks, and we want to report just one. +AsanChunk *ChooseChunk(uptr addr, + AsanChunk *left_chunk, AsanChunk *right_chunk) { + // Prefer an allocated chunk over freed chunk and freed chunk + // over available chunk. + if (left_chunk->chunk_state != right_chunk->chunk_state) { + if (left_chunk->chunk_state == CHUNK_ALLOCATED) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_ALLOCATED) + return right_chunk; + if (left_chunk->chunk_state == CHUNK_QUARANTINE) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_QUARANTINE) + return right_chunk; + } + // Same chunk_state: choose based on offset. + sptr l_offset = 0, r_offset = 0; + CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); + CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); + if (l_offset < r_offset) + return left_chunk; + return right_chunk; +} + +AsanChunkView FindHeapChunkByAddress(uptr addr) { + AsanChunk *m1 = GetAsanChunkByAddr(addr); + if (!m1) return AsanChunkView(m1); + sptr offset = 0; + if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { + // The address is in the chunk's left redzone, so maybe it is actually + // a right buffer overflow from the other chunk to the left. + // Search a bit to the left to see if there is another chunk. + AsanChunk *m2 = 0; + for (uptr l = 1; l < GetPageSizeCached(); l++) { + m2 = GetAsanChunkByAddr(addr - l); + if (m2 == m1) continue; // Still the same chunk. + break; + } + if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) + m1 = ChooseChunk(addr, m2, m1); + } + return AsanChunkView(m1); +} + +void AsanThreadLocalMallocStorage::CommitBack() { + AllocatorCache *ac = GetAllocatorCache(this); + quarantine.Drain(GetQuarantineCache(this), QuarantineCallback(ac)); + allocator.SwallowCache(GetAllocatorCache(this)); +} + +void PrintInternalAllocatorStats() { + allocator.PrintStats(); +} + +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { + return Allocate(size, alignment, stack, alloc_type, true); +} + +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { + Deallocate(ptr, stack, alloc_type); +} + +void *asan_malloc(uptr size, StackTrace *stack) { + return Allocate(size, 8, stack, FROM_MALLOC, true); +} + +void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return AllocatorReturnNull(); + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); + // If the memory comes from the secondary allocator no need to clear it + // as it comes directly from mmap. + if (ptr && allocator.FromPrimary(ptr)) + REAL(memset)(ptr, 0, nmemb * size); + return ptr; +} + +void *asan_realloc(void *p, uptr size, StackTrace *stack) { + if (p == 0) + return Allocate(size, 8, stack, FROM_MALLOC, true); + if (size == 0) { + Deallocate(p, stack, FROM_MALLOC); + return 0; + } + return Reallocate(p, size, stack); +} + +void *asan_valloc(uptr size, StackTrace *stack) { + return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); +} + +void *asan_pvalloc(uptr size, StackTrace *stack) { + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + return Allocate(size, PageSize, stack, FROM_MALLOC, true); +} + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + StackTrace *stack) { + void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true); + CHECK(IsAligned((uptr)ptr, alignment)); + *memptr = ptr; + return 0; +} + +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { + if (ptr == 0) return 0; + uptr usable_size = AllocationSize(reinterpret_cast(ptr)); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + GET_STACK_TRACE_FATAL(pc, bp); + ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); + } + return usable_size; +} + +uptr asan_mz_size(const void *ptr) { + return AllocationSize(reinterpret_cast(ptr)); +} + +void asan_mz_force_lock() { + allocator.ForceLock(); + fallback_mutex.Lock(); +} + +void asan_mz_force_unlock() { + fallback_mutex.Unlock(); + allocator.ForceUnlock(); +} + +} // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +void LockAllocator() { + __asan::allocator.ForceLock(); +} + +void UnlockAllocator() { + __asan::allocator.ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&__asan::allocator; + *end = *begin + sizeof(__asan::allocator); +} + +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr); + if (!m) return 0; + uptr chunk = m->Beg(); + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && + m->AddrIsInside(addr, /*locked_version=*/true)) + return chunk; + return 0; +} + +uptr GetUserBegin(uptr chunk) { + __asan::AsanChunk *m = + __asan::GetAsanChunkByAddrFastLocked(chunk); + CHECK(m); + return m->Beg(); +} + +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); +} + +bool LsanMetadata::allocated() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->chunk_state == __asan::CHUNK_ALLOCATED; +} + +ChunkTag LsanMetadata::tag() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return static_cast(m->lsan_tag); +} + +void LsanMetadata::set_tag(ChunkTag value) { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + m->lsan_tag = value; +} + +uptr LsanMetadata::requested_size() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->UsedSize(/*locked_version=*/true); +} + +u32 LsanMetadata::stack_trace_id() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->alloc_context_id; +} + +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + __asan::allocator.ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr); + if (!m) return kIgnoreObjectInvalid; + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { + if (m->lsan_tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->lsan_tag = __lsan::kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} +} // namespace __lsan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// ASan allocator doesn't reserve extra bytes, so normally we would +// just return "size". We don't want to expose our redzone sizes, etc here. +uptr __asan_get_estimated_allocated_size(uptr size) { + return size; +} + +bool __asan_get_ownership(const void *p) { + uptr ptr = reinterpret_cast(p); + return (AllocationSize(ptr) > 0); +} + +uptr __asan_get_allocated_size(const void *p) { + if (p == 0) return 0; + uptr ptr = reinterpret_cast(p); + uptr allocated_size = AllocationSize(ptr); + // Die if p is not malloced or if it is already freed. + if (allocated_size == 0) { + GET_STACK_TRACE_FATAL_HERE; + ReportAsanGetAllocatedSizeNotOwned(ptr, &stack); + } + return allocated_size; +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +// Provide default (no-op) implementation of malloc hooks. +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __asan_malloc_hook(void *ptr, uptr size) { + (void)ptr; + (void)size; +} +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __asan_free_hook(void *ptr) { + (void)ptr; +} +} // extern "C" +#endif diff --git a/projects/compiler-rt/lib/asan/asan_blacklist.txt b/projects/compiler-rt/lib/asan/asan_blacklist.txt new file mode 100644 index 00000000..63b3c315 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_blacklist.txt @@ -0,0 +1,10 @@ +# Blacklist for AddressSanitizer. Turns off instrumentation of particular +# functions or sources. Use with care. You may set location of blacklist +# at compile-time using -fsanitize-blacklist= flag. + +# Example usage: +# fun:*bad_function_name* +# src:file_with_tricky_code.cc +# global:*global_with_bad_access_or_initialization* +# global:*global_with_initialization_issues*=init +# type:*Namespace::ClassName*=init diff --git a/projects/compiler-rt/lib/asan/asan_dll_thunk.cc b/projects/compiler-rt/lib/asan/asan_dll_thunk.cc new file mode 100644 index 00000000..cedd60d3 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_dll_thunk.cc @@ -0,0 +1,196 @@ +//===-- asan_dll_thunk.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file defines a family of thunks that should be statically linked into +// the DLLs that have ASan instrumentation in order to delegate the calls to the +// shared runtime that lives in the main binary. +// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the +// details. +//===----------------------------------------------------------------------===// + +// Only compile this code when buidling asan_dll_thunk.lib +// Using #ifdef rather than relying on Makefiles etc. +// simplifies the build procedure. +#ifdef ASAN_DLL_THUNK + +// ----------------- Helper functions and macros --------------------- {{{1 +extern "C" { +void *__stdcall GetModuleHandleA(const char *module_name); +void *__stdcall GetProcAddress(void *module, const char *proc_name); +void abort(); +} + +static void *getRealProcAddressOrDie(const char *name) { + void *ret = GetProcAddress(GetModuleHandleA(0), name); + if (!ret) + abort(); + return ret; +} + +#define WRAP_V_V(name) \ + extern "C" void name() { \ + typedef void (*fntype)(); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(); \ + } + +#define WRAP_V_W(name) \ + extern "C" void name(void *arg) { \ + typedef void (*fntype)(void *arg); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg); \ + } + +#define WRAP_V_WW(name) \ + extern "C" void name(void *arg1, void *arg2) { \ + typedef void (*fntype)(void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg1, arg2); \ + } + +#define WRAP_V_WWW(name) \ + extern "C" void name(void *arg1, void *arg2, void *arg3) { \ + typedef void *(*fntype)(void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg1, arg2, arg3); \ + } + +#define WRAP_W_V(name) \ + extern "C" void *name() { \ + typedef void *(*fntype)(); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(); \ + } + +#define WRAP_W_W(name) \ + extern "C" void *name(void *arg) { \ + typedef void *(*fntype)(void *arg); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg); \ + } + +#define WRAP_W_WW(name) \ + extern "C" void *name(void *arg1, void *arg2) { \ + typedef void *(*fntype)(void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2); \ + } + +#define WRAP_W_WWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3) { \ + typedef void *(*fntype)(void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3); \ + } + +#define WRAP_W_WWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \ + typedef void *(*fntype)(void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4); \ + } + +#define WRAP_W_WWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5) { \ + typedef void *(*fntype)(void *, void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5); \ + } + +#define WRAP_W_WWWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5, void *arg6) { \ + typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5, arg6); \ + } +// }}} + +// ----------------- ASan own interface functions -------------------- +WRAP_W_V(__asan_should_detect_stack_use_after_return) + +extern "C" { + int __asan_option_detect_stack_use_after_return; + + // Manually wrap __asan_init as we need to initialize + // __asan_option_detect_stack_use_after_return afterwards. + void __asan_init_v3() { + typedef void (*fntype)(); + static fntype fn = (fntype)getRealProcAddressOrDie("__asan_init_v3"); + fn(); + __asan_option_detect_stack_use_after_return = + (__asan_should_detect_stack_use_after_return() != 0); + } +} + +WRAP_V_W(__asan_report_store1) +WRAP_V_W(__asan_report_store2) +WRAP_V_W(__asan_report_store4) +WRAP_V_W(__asan_report_store8) +WRAP_V_W(__asan_report_store16) +WRAP_V_WW(__asan_report_store_n) + +WRAP_V_W(__asan_report_load1) +WRAP_V_W(__asan_report_load2) +WRAP_V_W(__asan_report_load4) +WRAP_V_W(__asan_report_load8) +WRAP_V_W(__asan_report_load16) +WRAP_V_WW(__asan_report_load_n) + +WRAP_V_WW(__asan_register_globals) +WRAP_V_WW(__asan_unregister_globals) + +WRAP_W_WW(__asan_stack_malloc_0) +WRAP_W_WW(__asan_stack_malloc_1) +WRAP_W_WW(__asan_stack_malloc_2) +WRAP_W_WW(__asan_stack_malloc_3) +WRAP_W_WW(__asan_stack_malloc_4) +WRAP_W_WW(__asan_stack_malloc_5) +WRAP_W_WW(__asan_stack_malloc_6) +WRAP_W_WW(__asan_stack_malloc_7) +WRAP_W_WW(__asan_stack_malloc_8) +WRAP_W_WW(__asan_stack_malloc_9) +WRAP_W_WW(__asan_stack_malloc_10) + +WRAP_V_WWW(__asan_stack_free_0) +WRAP_V_WWW(__asan_stack_free_1) +WRAP_V_WWW(__asan_stack_free_2) +WRAP_V_WWW(__asan_stack_free_4) +WRAP_V_WWW(__asan_stack_free_5) +WRAP_V_WWW(__asan_stack_free_6) +WRAP_V_WWW(__asan_stack_free_7) +WRAP_V_WWW(__asan_stack_free_8) +WRAP_V_WWW(__asan_stack_free_9) +WRAP_V_WWW(__asan_stack_free_10) + +// TODO(timurrrr): Add more interface functions on the as-needed basis. + +// ----------------- Memory allocation functions --------------------- +WRAP_V_W(free) +WRAP_V_WW(_free_dbg) + +WRAP_W_W(malloc) +WRAP_W_WWWW(_malloc_dbg) + +WRAP_W_WW(calloc) +WRAP_W_WWWWW(_calloc_dbg) +WRAP_W_WWW(_calloc_impl) + +WRAP_W_WW(realloc) +WRAP_W_WWW(_realloc_dbg) +WRAP_W_WWW(_recalloc) + +WRAP_W_W(_msize) + +// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc). + +#endif // ASAN_DLL_THUNK diff --git a/projects/compiler-rt/lib/asan/asan_fake_stack.cc b/projects/compiler-rt/lib/asan/asan_fake_stack.cc new file mode 100644 index 00000000..d3e55bff --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_fake_stack.cc @@ -0,0 +1,220 @@ +//===-- asan_fake_stack.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// FakeStack is used to detect use-after-return bugs. +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" +#include "asan_poisoning.h" +#include "asan_thread.h" + +namespace __asan { + +static const u64 kMagic1 = kAsanStackAfterReturnMagic; +static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; +static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; +static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; + +// For small size classes inline PoisonShadow for better performance. +ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { + CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. + u64 *shadow = reinterpret_cast(MemToShadow(ptr)); + if (class_id <= 6) { + for (uptr i = 0; i < (1U << class_id); i++) + shadow[i] = magic; + } else { + // The size class is too big, it's cheaper to poison only size bytes. + PoisonShadow(ptr, size, static_cast(magic)); + } +} + +FakeStack *FakeStack::Create(uptr stack_size_log) { + static uptr kMinStackSizeLog = 16; + static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28); + if (stack_size_log < kMinStackSizeLog) + stack_size_log = kMinStackSizeLog; + if (stack_size_log > kMaxStackSizeLog) + stack_size_log = kMaxStackSizeLog; + FakeStack *res = reinterpret_cast( + MmapOrDie(RequiredSize(stack_size_log), "FakeStack")); + res->stack_size_log_ = stack_size_log; + if (common_flags()->verbosity) { + u8 *p = reinterpret_cast(res); + Report("T%d: FakeStack created: %p -- %p stack_size_log: %zd \n", + GetCurrentTidOrInvalid(), p, + p + FakeStack::RequiredSize(stack_size_log), stack_size_log); + } + return res; +} + +void FakeStack::Destroy() { + PoisonAll(0); + UnmapOrDie(this, RequiredSize(stack_size_log_)); +} + +void FakeStack::PoisonAll(u8 magic) { + PoisonShadow(reinterpret_cast(this), RequiredSize(stack_size_log()), + magic); +} + +ALWAYS_INLINE USED +FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, + uptr real_stack) { + CHECK_LT(class_id, kNumberOfSizeClasses); + if (needs_gc_) + GC(real_stack); + uptr &hint_position = hint_position_[class_id]; + const int num_iter = NumberOfFrames(stack_size_log, class_id); + u8 *flags = GetFlags(stack_size_log, class_id); + for (int i = 0; i < num_iter; i++) { + uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++); + // This part is tricky. On one hand, checking and setting flags[pos] + // should be atomic to ensure async-signal safety. But on the other hand, + // if the signal arrives between checking and setting flags[pos], the + // signal handler's fake stack will start from a different hint_position + // and so will not touch this particular byte. So, it is safe to do this + // with regular non-atimic load and store (at least I was not able to make + // this code crash). + if (flags[pos]) continue; + flags[pos] = 1; + FakeFrame *res = reinterpret_cast( + GetFrame(stack_size_log, class_id, pos)); + res->real_stack = real_stack; + *SavedFlagPtr(reinterpret_cast(res), class_id) = &flags[pos]; + return res; + } + return 0; // We are out of fake stack. +} + +uptr FakeStack::AddrIsInFakeStack(uptr ptr) { + uptr stack_size_log = this->stack_size_log(); + uptr beg = reinterpret_cast(GetFrame(stack_size_log, 0, 0)); + uptr end = reinterpret_cast(this) + RequiredSize(stack_size_log); + if (ptr < beg || ptr >= end) return 0; + uptr class_id = (ptr - beg) >> stack_size_log; + uptr base = beg + (class_id << stack_size_log); + CHECK_LE(base, ptr); + CHECK_LT(ptr, base + (1UL << stack_size_log)); + uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id); + return base + pos * BytesInSizeClass(class_id); +} + +void FakeStack::HandleNoReturn() { + needs_gc_ = true; +} + +// When throw, longjmp or some such happens we don't call OnFree() and +// as the result may leak one or more fake frames, but the good news is that +// we are notified about all such events by HandleNoReturn(). +// If we recently had such no-return event we need to collect garbage frames. +// We do it based on their 'real_stack' values -- everything that is lower +// than the current real_stack is garbage. +NOINLINE void FakeStack::GC(uptr real_stack) { + uptr collected = 0; + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast( + GetFrame(stack_size_log(), class_id, i)); + if (ff->real_stack < real_stack) { + flags[i] = 0; + collected++; + } + } + } + needs_gc_ = false; +} + +void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) { + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast( + GetFrame(stack_size_log(), class_id, i)); + uptr begin = reinterpret_cast(ff); + callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg); + } + } +} + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +static THREADLOCAL FakeStack *fake_stack_tls; + +FakeStack *GetTLSFakeStack() { + return fake_stack_tls; +} +void SetTLSFakeStack(FakeStack *fs) { + fake_stack_tls = fs; +} +#else +FakeStack *GetTLSFakeStack() { return 0; } +void SetTLSFakeStack(FakeStack *fs) { } +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + +static FakeStack *GetFakeStack() { + AsanThread *t = GetCurrentThread(); + if (!t) return 0; + return t->fake_stack(); +} + +static FakeStack *GetFakeStackFast() { + if (FakeStack *fs = GetTLSFakeStack()) + return fs; + if (!__asan_option_detect_stack_use_after_return) + return 0; + return GetFakeStack(); +} + +ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { + FakeStack *fs = GetFakeStackFast(); + if (!fs) return real_stack; + FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack); + if (!ff) + return real_stack; // Out of fake stack, return the real one. + uptr ptr = reinterpret_cast(ff); + SetShadow(ptr, size, class_id, 0); + return ptr; +} + +ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { + if (ptr == real_stack) + return; + FakeStack::Deallocate(ptr, class_id); + SetShadow(ptr, size, class_id, kMagic8); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ + __asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \ + return __asan::OnMalloc(class_id, size, real_stack); \ + } \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ + uptr ptr, uptr size, uptr real_stack) { \ + __asan::OnFree(ptr, class_id, size, real_stack); \ + } + +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) diff --git a/projects/compiler-rt/lib/asan/asan_fake_stack.h b/projects/compiler-rt/lib/asan/asan_fake_stack.h new file mode 100644 index 00000000..f17ee026 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_fake_stack.h @@ -0,0 +1,171 @@ +//===-- asan_fake_stack.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_fake_stack.cc, implements FakeStack. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_FAKE_STACK_H +#define ASAN_FAKE_STACK_H + +#include "sanitizer_common/sanitizer_common.h" + +namespace __asan { + +// Fake stack frame contains local variables of one function. +struct FakeFrame { + uptr magic; // Modified by the instrumented code. + uptr descr; // Modified by the instrumented code. + uptr pc; // Modified by the instrumented code. + uptr real_stack; +}; + +// For each thread we create a fake stack and place stack objects on this fake +// stack instead of the real stack. The fake stack is not really a stack but +// a fast malloc-like allocator so that when a function exits the fake stack +// is not popped but remains there for quite some time until gets used again. +// So, we poison the objects on the fake stack when function returns. +// It helps us find use-after-return bugs. +// +// The FakeStack objects is allocated by a single mmap call and has no other +// pointers. The size of the fake stack depends on the actual thread stack size +// and thus can not be a constant. +// stack_size is a power of two greater or equal to the thread's stack size; +// we store it as its logarithm (stack_size_log). +// FakeStack has kNumberOfSizeClasses (11) size classes, each size class +// is a power of two, starting from 64 bytes. Each size class occupies +// stack_size bytes and thus can allocate +// NumberOfFrames=(stack_size/BytesInSizeClass) fake frames (also a power of 2). +// For each size class we have NumberOfFrames allocation flags, +// each flag indicates whether the given frame is currently allocated. +// All flags for size classes 0 .. 10 are stored in a single contiguous region +// followed by another contiguous region which contains the actual memory for +// size classes. The addresses are computed by GetFlags and GetFrame without +// any memory accesses solely based on 'this' and stack_size_log. +// Allocate() flips the appropriate allocation flag atomically, thus achieving +// async-signal safety. +// This allocator does not have quarantine per se, but it tries to allocate the +// frames in round robin fasion to maximize the delay between a deallocation +// and the next allocation. +class FakeStack { + static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B. + static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. + + public: + static const uptr kNumberOfSizeClasses = + kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; + + // CTOR: create the FakeStack as a single mmap-ed object. + static FakeStack *Create(uptr stack_size_log); + + void Destroy(); + + // stack_size_log is at least 15 (stack_size >= 32K). + static uptr SizeRequiredForFlags(uptr stack_size_log) { + return 1UL << (stack_size_log + 1 - kMinStackFrameSizeLog); + } + + // Each size class occupies stack_size bytes. + static uptr SizeRequiredForFrames(uptr stack_size_log) { + return (1ULL << stack_size_log) * kNumberOfSizeClasses; + } + + // Number of bytes requires for the whole object. + static uptr RequiredSize(uptr stack_size_log) { + return kFlagsOffset + SizeRequiredForFlags(stack_size_log) + + SizeRequiredForFrames(stack_size_log); + } + + // Offset of the given flag from the first flag. + // The flags for class 0 begin at offset 000000000 + // The flags for class 1 begin at offset 100000000 + // ....................2................ 110000000 + // ....................3................ 111000000 + // and so on. + static uptr FlagsOffset(uptr stack_size_log, uptr class_id) { + uptr t = kNumberOfSizeClasses - 1 - class_id; + const uptr all_ones = (1 << (kNumberOfSizeClasses - 1)) - 1; + return ((all_ones >> t) << t) << (stack_size_log - 15); + } + + static uptr NumberOfFrames(uptr stack_size_log, uptr class_id) { + return 1UL << (stack_size_log - kMinStackFrameSizeLog - class_id); + } + + // Divide n by the numbe of frames in size class. + static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) { + return n & (NumberOfFrames(stack_size_log, class_id) - 1); + } + + // The the pointer to the flags of the given class_id. + u8 *GetFlags(uptr stack_size_log, uptr class_id) { + return reinterpret_cast(this) + kFlagsOffset + + FlagsOffset(stack_size_log, class_id); + } + + // Get frame by class_id and pos. + u8 *GetFrame(uptr stack_size_log, uptr class_id, uptr pos) { + return reinterpret_cast(this) + kFlagsOffset + + SizeRequiredForFlags(stack_size_log) + + (1 << stack_size_log) * class_id + BytesInSizeClass(class_id) * pos; + } + + // Allocate the fake frame. + FakeFrame *Allocate(uptr stack_size_log, uptr class_id, uptr real_stack); + + // Deallocate the fake frame: read the saved flag address and write 0 there. + static void Deallocate(uptr x, uptr class_id) { + **SavedFlagPtr(x, class_id) = 0; + } + + // Poison the entire FakeStack's shadow with the magic value. + void PoisonAll(u8 magic); + + // Return the beginning of the FakeFrame or 0 if the address is not ours. + uptr AddrIsInFakeStack(uptr addr); + + // Number of bytes in a fake frame of this size class. + static uptr BytesInSizeClass(uptr class_id) { + return 1UL << (class_id + kMinStackFrameSizeLog); + } + + // The fake frame is guaranteed to have a right redzone. + // We use the last word of that redzone to store the address of the flag + // that corresponds to the current frame to make faster deallocation. + static u8 **SavedFlagPtr(uptr x, uptr class_id) { + return reinterpret_cast(x + BytesInSizeClass(class_id) - sizeof(x)); + } + + uptr stack_size_log() const { return stack_size_log_; } + + void HandleNoReturn(); + void GC(uptr real_stack); + + void ForEachFakeFrame(RangeIteratorCallback callback, void *arg); + + private: + FakeStack() { } + static const uptr kFlagsOffset = 4096; // This is were the flags begin. + // Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID + COMPILER_CHECK(kNumberOfSizeClasses == 11); + static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; + + uptr hint_position_[kNumberOfSizeClasses]; + uptr stack_size_log_; + // a bit is set if something was allocated from the corresponding size class. + bool needs_gc_; +}; + +FakeStack *GetTLSFakeStack(); +void SetTLSFakeStack(FakeStack *fs); + +} // namespace __asan + +#endif // ASAN_FAKE_STACK_H diff --git a/projects/compiler-rt/lib/asan/asan_flags.h b/projects/compiler-rt/lib/asan/asan_flags.h new file mode 100644 index 00000000..89662f28 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_flags.h @@ -0,0 +1,123 @@ +//===-- asan_flags.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan runtime flags. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_FLAGS_H +#define ASAN_FLAGS_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +// ASan flag values can be defined in four ways: +// 1) initialized with default values at startup. +// 2) overriden during compilation of ASan runtime by providing +// compile definition ASAN_DEFAULT_OPTIONS. +// 3) overriden from string returned by user-specified function +// __asan_default_options(). +// 4) overriden from env variable ASAN_OPTIONS. + +namespace __asan { + +struct Flags { + // Size (in bytes) of quarantine used to detect use-after-free errors. + // Lower value may reduce memory usage but increase the chance of + // false negatives. + int quarantine_size; + // Size (in bytes) of redzones around heap objects. + // Requirement: redzone >= 32, is a power of two. + int redzone; + // If set, prints some debugging information and does additional checks. + bool debug; + // Controls the way to handle globals (0 - don't detect buffer overflow + // on globals, 1 - detect buffer overflow, 2 - print data about registered + // globals). + int report_globals; + // If set, attempts to catch initialization order issues. + bool check_initialization_order; + // If set, uses custom wrappers and replacements for libc string functions + // to find more errors. + bool replace_str; + // If set, uses custom wrappers for memset/memcpy/memmove intinsics. + bool replace_intrin; + // Used on Mac only. + bool mac_ignore_invalid_free; + // Enables stack-use-after-return checking at run-time. + bool detect_stack_use_after_return; + // The minimal fake stack size log. + int uar_stack_size_log; + // ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes + // that will be filled with malloc_fill_byte on malloc. + int max_malloc_fill_size, malloc_fill_byte; + // Override exit status if something was reported. + int exitcode; + // If set, user may manually mark memory regions as poisoned or unpoisoned. + bool allow_user_poisoning; + // Number of seconds to sleep between printing an error report and + // terminating application. Useful for debug purposes (when one needs + // to attach gdb, for example). + int sleep_before_dying; + // If set, registers ASan custom segv handler. + bool handle_segv; + // If set, allows user register segv handler even if ASan registers one. + bool allow_user_segv_handler; + // If set, uses alternate stack for signal handling. + bool use_sigaltstack; + // Allow the users to work around the bug in Nvidia drivers prior to 295.*. + bool check_malloc_usable_size; + // If set, explicitly unmaps (huge) shadow at exit. + bool unmap_shadow_on_exit; + // If set, calls abort() instead of _exit() after printing an error report. + bool abort_on_error; + // Print various statistics after printing an error message or if atexit=1. + bool print_stats; + // Print the legend for the shadow bytes. + bool print_legend; + // If set, prints ASan exit stats even after program terminates successfully. + bool atexit; + // If set, coverage information will be dumped at shutdown time if the + // appropriate instrumentation was enabled. + bool coverage; + // By default, disable core dumper on 64-bit - it makes little sense + // to dump 16T+ core. + bool disable_core; + // Allow the tool to re-exec the program. This may interfere badly with the + // debugger. + bool allow_reexec; + // If set, prints not only thread creation stacks for threads in error report, + // but also thread creation stacks for threads that created those threads, + // etc. up to main thread. + bool print_full_thread_history; + // Poison (or not) the heap memory on [de]allocation. Zero value is useful + // for benchmarking the allocator or instrumentator. + bool poison_heap; + // If true, poison partially addressable 8-byte aligned words (default=true). + // This flag affects heap and global buffers, but not stack buffers. + bool poison_partial; + // Report errors on malloc/delete, new/free, new/delete[], etc. + bool alloc_dealloc_mismatch; + // If true, assume that memcmp(p1, p2, n) always reads n bytes before + // comparing p1 and p2. + bool strict_memcmp; + // If true, assume that dynamic initializers can never access globals from + // other modules, even if the latter are already initialized. + bool strict_init_order; +}; + +extern Flags asan_flags_dont_use_directly; +inline Flags *flags() { + return &asan_flags_dont_use_directly; +} +void InitializeFlags(Flags *f, const char *env); + +} // namespace __asan + +#endif // ASAN_FLAGS_H diff --git a/projects/compiler-rt/lib/asan/asan_globals.cc b/projects/compiler-rt/lib/asan/asan_globals.cc new file mode 100644 index 00000000..81699676 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_globals.cc @@ -0,0 +1,210 @@ +//===-- asan_globals.cc ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Handle globals. +//===----------------------------------------------------------------------===// +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_placement_new.h" + +namespace __asan { + +typedef __asan_global Global; + +struct ListOfGlobals { + const Global *g; + ListOfGlobals *next; +}; + +static BlockingMutex mu_for_globals(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_globals; +static ListOfGlobals *list_of_all_globals; + +static const int kDynamicInitGlobalsInitialCapacity = 512; +struct DynInitGlobal { + Global g; + bool initialized; +}; +typedef InternalMmapVector VectorOfGlobals; +// Lazy-initialized and never deleted. +static VectorOfGlobals *dynamic_init_globals; + +ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { + FastPoisonShadow(g->beg, g->size_with_redzone, value); +} + +ALWAYS_INLINE void PoisonRedZones(const Global &g) { + uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); + FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, + kAsanGlobalRedzoneMagic); + if (g.size != aligned_size) { + FastPoisonShadowPartialRightRedzone( + g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), + g.size % SHADOW_GRANULARITY, + SHADOW_GRANULARITY, + kAsanGlobalRedzoneMagic); + } +} + +static void ReportGlobal(const Global &g, const char *prefix) { + Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", + prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name, + g.module_name, g.has_dynamic_init); +} + +bool DescribeAddressIfGlobal(uptr addr, uptr size) { + if (!flags()->report_globals) return false; + BlockingMutexLock lock(&mu_for_globals); + bool res = false; + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + const Global &g = *l->g; + if (flags()->report_globals >= 2) + ReportGlobal(g, "Search"); + res |= DescribeAddressRelativeToGlobal(addr, size, g); + } + return res; +} + +// Register a global variable. +// This function may be called more than once for every global +// so we store the globals in a map. +static void RegisterGlobal(const Global *g) { + CHECK(asan_inited); + if (flags()->report_globals >= 2) + ReportGlobal(*g, "Added"); + CHECK(flags()->report_globals); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (flags()->poison_heap) + PoisonRedZones(*g); + ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; + l->g = g; + l->next = list_of_all_globals; + list_of_all_globals = l; + if (g->has_dynamic_init) { + if (dynamic_init_globals == 0) { + dynamic_init_globals = new(allocator_for_globals) + VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); + } + DynInitGlobal dyn_global = { *g, false }; + dynamic_init_globals->push_back(dyn_global); + } +} + +static void UnregisterGlobal(const Global *g) { + CHECK(asan_inited); + CHECK(flags()->report_globals); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (flags()->poison_heap) + PoisonShadowForGlobal(g, 0); + // We unpoison the shadow memory for the global but we do not remove it from + // the list because that would require O(n^2) time with the current list + // implementation. It might not be worth doing anyway. +} + +void StopInitOrderChecking() { + BlockingMutexLock lock(&mu_for_globals); + if (!flags()->check_initialization_order || !dynamic_init_globals) + return; + flags()->check_initialization_order = false; + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// Register an array of globals. +void __asan_register_globals(__asan_global *globals, uptr n) { + if (!flags()->report_globals) return; + BlockingMutexLock lock(&mu_for_globals); + for (uptr i = 0; i < n; i++) { + RegisterGlobal(&globals[i]); + } +} + +// Unregister an array of globals. +// We must do this when a shared objects gets dlclosed. +void __asan_unregister_globals(__asan_global *globals, uptr n) { + if (!flags()->report_globals) return; + BlockingMutexLock lock(&mu_for_globals); + for (uptr i = 0; i < n; i++) { + UnregisterGlobal(&globals[i]); + } +} + +// This method runs immediately prior to dynamic initialization in each TU, +// when all dynamically initialized globals are unpoisoned. This method +// poisons all global variables not defined in this TU, so that a dynamic +// initializer can only touch global variables in the same TU. +void __asan_before_dynamic_init(const char *module_name) { + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + bool strict_init_order = flags()->strict_init_order; + CHECK(dynamic_init_globals); + CHECK(module_name); + CHECK(asan_inited); + BlockingMutexLock lock(&mu_for_globals); + if (flags()->report_globals >= 3) + Printf("DynInitPoison module: %s\n", module_name); + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; + if (g->module_name != module_name) + PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); + else if (!strict_init_order) + dyn_g.initialized = true; + } +} + +// This method runs immediately after dynamic initialization in each TU, when +// all dynamically initialized globals except for those defined in the current +// TU are poisoned. It simply unpoisons all dynamically initialized globals. +void __asan_after_dynamic_init() { + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + CHECK(asan_inited); + BlockingMutexLock lock(&mu_for_globals); + // FIXME: Optionally report that we're unpoisoning globals from a module. + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (!dyn_g.initialized) { + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } + } +} diff --git a/projects/compiler-rt/lib/asan/asan_intercepted_functions.h b/projects/compiler-rt/lib/asan/asan_intercepted_functions.h new file mode 100644 index 00000000..de42cd6d --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_intercepted_functions.h @@ -0,0 +1,79 @@ +//===-- asan_intercepted_functions.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header containing prototypes for wrapper functions and wrappers +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERCEPTED_FUNCTIONS_H +#define ASAN_INTERCEPTED_FUNCTIONS_H + +#include "sanitizer_common/sanitizer_platform_interceptors.h" + +// Use macro to describe if specific function should be +// intercepted on a given platform. +#if !SANITIZER_WINDOWS +# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 +# define ASAN_INTERCEPT__LONGJMP 1 +# define ASAN_INTERCEPT_STRDUP 1 +# define ASAN_INTERCEPT_INDEX 1 +# define ASAN_INTERCEPT_PTHREAD_CREATE 1 +# define ASAN_INTERCEPT_MLOCKX 1 +#else +# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 +# define ASAN_INTERCEPT__LONGJMP 0 +# define ASAN_INTERCEPT_STRDUP 0 +# define ASAN_INTERCEPT_INDEX 0 +# define ASAN_INTERCEPT_PTHREAD_CREATE 0 +# define ASAN_INTERCEPT_MLOCKX 0 +#endif + +#if SANITIZER_LINUX +# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1 +#else +# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 +#endif + +#if !SANITIZER_MAC +# define ASAN_INTERCEPT_STRNLEN 1 +#else +# define ASAN_INTERCEPT_STRNLEN 0 +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +# define ASAN_INTERCEPT_SWAPCONTEXT 1 +#else +# define ASAN_INTERCEPT_SWAPCONTEXT 0 +#endif + +#if !SANITIZER_ANDROID && !SANITIZER_WINDOWS +# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 +#else +# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 +#endif + +#if !SANITIZER_WINDOWS +# define ASAN_INTERCEPT_SIGLONGJMP 1 +#else +# define ASAN_INTERCEPT_SIGLONGJMP 0 +#endif + +#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS +# define ASAN_INTERCEPT___CXA_THROW 1 +#else +# define ASAN_INTERCEPT___CXA_THROW 0 +#endif + +#if !SANITIZER_WINDOWS +# define ASAN_INTERCEPT___CXA_ATEXIT 1 +#else +# define ASAN_INTERCEPT___CXA_ATEXIT 0 +#endif + +#endif // ASAN_INTERCEPTED_FUNCTIONS_H diff --git a/projects/compiler-rt/lib/asan/asan_interceptors.cc b/projects/compiler-rt/lib/asan/asan_interceptors.cc new file mode 100644 index 00000000..a25827b6 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_interceptors.cc @@ -0,0 +1,795 @@ +//===-- asan_interceptors.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Intercept various libc functions. +//===----------------------------------------------------------------------===// +#include "asan_interceptors.h" + +#include "asan_allocator.h" +#include "asan_intercepted_functions.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_libc.h" + +namespace __asan { + +// Return true if we can quickly decide that the region is unpoisoned. +static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { + if (size == 0) return true; + if (size <= 32) + return !AddressIsPoisoned(beg) && + !AddressIsPoisoned(beg + size - 1) && + !AddressIsPoisoned(beg + size / 2); + return false; +} + +// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, +// and ASAN_WRITE_RANGE as macro instead of function so +// that no extra frames are created, and stack trace contains +// relevant information only. +// We check all shadow bytes. +#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ + uptr __offset = (uptr)(offset); \ + uptr __size = (uptr)(size); \ + uptr __bad = 0; \ + if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ + (__bad = __asan_region_is_poisoned(__offset, __size))) { \ + GET_CURRENT_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ + } \ + } while (0) + +#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false) +#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true) + +// Behavior of functions like "memcpy" or "strcpy" is undefined +// if memory intervals overlap. We report error in this case. +// Macro is used to avoid creation of new frames. +static inline bool RangesOverlap(const char *offset1, uptr length1, + const char *offset2, uptr length2) { + return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); +} +#define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ + const char *offset1 = (const char*)_offset1; \ + const char *offset2 = (const char*)_offset2; \ + if (RangesOverlap(offset1, length1, offset2, length2)) { \ + GET_STACK_TRACE_FATAL_HERE; \ + ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \ + offset2, length2, &stack); \ + } \ +} while (0) + +#define ENSURE_ASAN_INITED() do { \ + CHECK(!asan_init_is_running); \ + if (!asan_inited) { \ + __asan_init(); \ + } \ +} while (0) + +static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { +#if ASAN_INTERCEPT_STRNLEN + if (REAL(strnlen) != 0) { + return REAL(strnlen)(s, maxlen); + } +#endif + return internal_strnlen(s, maxlen); +} + +void SetThreadName(const char *name) { + AsanThread *t = GetCurrentThread(); + if (t) + asanThreadRegistry().SetThreadName(t->tid(), name); +} + +int OnExit() { + // FIXME: ask frontend whether we need to return failure. + return 0; +} + +} // namespace __asan + +// ---------------------- Wrappers ---------------- {{{1 +using namespace __asan; // NOLINT + +DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) + +#if !SANITIZER_MAC +#define ASAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \ + common_flags()->verbosity > 0) \ + Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ + } while (0) +#else +// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. +#define ASAN_INTERCEPT_FUNC(name) +#endif // SANITIZER_MAC + +#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + ASAN_WRITE_RANGE(ptr, size) +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size) +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + do { \ + if (asan_init_is_running) return REAL(func)(__VA_ARGS__); \ + ctx = 0; \ + (void) ctx; \ + if (SANITIZER_MAC && !asan_inited) return REAL(func)(__VA_ARGS__); \ + ENSURE_ASAN_INITED(); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) +// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name) +// But asan does not remember UserId's for threads (pthread_t); +// and remembers all ever existed threads, so the linear search by UserId +// can be slow. +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() +#include "sanitizer_common/sanitizer_common_interceptors.inc" + +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#include "sanitizer_common/sanitizer_common_syscalls.inc" + +static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { + AsanThread *t = (AsanThread*)arg; + SetCurrentThread(t); + return t->ThreadStart(GetTid()); +} + +#if ASAN_INTERCEPT_PTHREAD_CREATE +INTERCEPTOR(int, pthread_create, void *thread, + void *attr, void *(*start_routine)(void*), void *arg) { + EnsureMainThreadIDIsCorrect(); + // Strict init-order checking in thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); + GET_STACK_TRACE_THREAD; + int detached = 0; + if (attr != 0) + REAL(pthread_attr_getdetachstate)(attr, &detached); + + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg); + CreateThreadContextArgs args = { t, &stack }; + asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args); + return REAL(pthread_create)(thread, attr, asan_thread_start, t); +} +#endif // ASAN_INTERCEPT_PTHREAD_CREATE + +#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION +INTERCEPTOR(void*, signal, int signum, void *handler) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { + return REAL(signal)(signum, handler); + } + return 0; +} + +INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { + return REAL(sigaction)(signum, act, oldact); + } + return 0; +} +#elif SANITIZER_POSIX +// We need to have defined REAL(sigaction) on posix systems. +DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) +#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION + +#if ASAN_INTERCEPT_SWAPCONTEXT +static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { + // Align to page size. + uptr PageSize = GetPageSizeCached(); + uptr bottom = stack & ~(PageSize - 1); + ssize += stack - bottom; + ssize = RoundUpTo(ssize, PageSize); + static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb + if (ssize && ssize <= kMaxSaneContextStackSize) { + PoisonShadow(bottom, ssize, 0); + } +} + +INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, + struct ucontext_t *ucp) { + static bool reported_warning = false; + if (!reported_warning) { + Report("WARNING: ASan doesn't fully support makecontext/swapcontext " + "functions and may produce false positives in some cases!\n"); + reported_warning = true; + } + // Clear shadow memory for new context (it may share stack + // with current context). + uptr stack, ssize; + ReadContextStack(ucp, &stack, &ssize); + ClearShadowMemoryForContextStack(stack, ssize); + int res = REAL(swapcontext)(oucp, ucp); + // swapcontext technically does not return, but program may swap context to + // "oucp" later, that would look as if swapcontext() returned 0. + // We need to clear shadow for ucp once again, as it may be in arbitrary + // state. + ClearShadowMemoryForContextStack(stack, ssize); + return res; +} +#endif // ASAN_INTERCEPT_SWAPCONTEXT + +INTERCEPTOR(void, longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(longjmp)(env, val); +} + +#if ASAN_INTERCEPT__LONGJMP +INTERCEPTOR(void, _longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(_longjmp)(env, val); +} +#endif + +#if ASAN_INTERCEPT_SIGLONGJMP +INTERCEPTOR(void, siglongjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(siglongjmp)(env, val); +} +#endif + +#if ASAN_INTERCEPT___CXA_THROW +INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { + CHECK(REAL(__cxa_throw)); + __asan_handle_no_return(); + REAL(__cxa_throw)(a, b, c); +} +#endif + +// intercept mlock and friends. +// Since asan maps 16T of RAM, mlock is completely unfriendly to asan. +// All functions return 0 (success). +static void MlockIsUnsupported() { + static bool printed = false; + if (printed) return; + printed = true; + if (common_flags()->verbosity > 0) { + Printf("INFO: AddressSanitizer ignores " + "mlock/mlockall/munlock/munlockall\n"); + } +} + +INTERCEPTOR(int, mlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, mlockall, int flags) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlockall, void) { + MlockIsUnsupported(); + return 0; +} + +static inline int CharCmp(unsigned char c1, unsigned char c2) { + return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; +} + +INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { + if (!asan_inited) return internal_memcmp(a1, a2, size); + ENSURE_ASAN_INITED(); + if (flags()->replace_intrin) { + if (flags()->strict_memcmp) { + // Check the entire regions even if the first bytes of the buffers are + // different. + ASAN_READ_RANGE(a1, size); + ASAN_READ_RANGE(a2, size); + // Fallthrough to REAL(memcmp) below. + } else { + unsigned char c1 = 0, c2 = 0; + const unsigned char *s1 = (const unsigned char*)a1; + const unsigned char *s2 = (const unsigned char*)a2; + uptr i; + for (i = 0; i < size; i++) { + c1 = s1[i]; + c2 = s2[i]; + if (c1 != c2) break; + } + ASAN_READ_RANGE(s1, Min(i + 1, size)); + ASAN_READ_RANGE(s2, Min(i + 1, size)); + return CharCmp(c1, c2); + } + } + return REAL(memcmp(a1, a2, size)); +} + +#define MEMMOVE_BODY { \ + if (!asan_inited) return internal_memmove(to, from, size); \ + if (asan_init_is_running) { \ + return REAL(memmove)(to, from, size); \ + } \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + ASAN_READ_RANGE(from, size); \ + ASAN_WRITE_RANGE(to, size); \ + } \ + return internal_memmove(to, from, size); \ +} + +INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) MEMMOVE_BODY + +INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { +#if !SANITIZER_MAC + if (!asan_inited) return internal_memcpy(to, from, size); + // memcpy is called during __asan_init() from the internals + // of printf(...). + if (asan_init_is_running) { + return REAL(memcpy)(to, from, size); + } + ENSURE_ASAN_INITED(); + if (flags()->replace_intrin) { + if (to != from) { + // We do not treat memcpy with to==from as a bug. + // See http://llvm.org/bugs/show_bug.cgi?id=11763. + CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); + } + ASAN_READ_RANGE(from, size); + ASAN_WRITE_RANGE(to, size); + } + // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8, so + // calling REAL(memcpy) here leads to infinite recursion. + // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. + return internal_memcpy(to, from, size); +#else + // At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced + // with WRAP(memcpy). As a result, false positives are reported for memmove() + // calls. If we just disable error reporting with + // ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with + // internal_memcpy(), which may lead to crashes, see + // http://llvm.org/bugs/show_bug.cgi?id=16362. + MEMMOVE_BODY +#endif // !SANITIZER_MAC +} + +INTERCEPTOR(void*, memset, void *block, int c, uptr size) { + if (!asan_inited) return internal_memset(block, c, size); + // memset is called inside Printf. + if (asan_init_is_running) { + return REAL(memset)(block, c, size); + } + ENSURE_ASAN_INITED(); + if (flags()->replace_intrin) { + ASAN_WRITE_RANGE(block, size); + } + return REAL(memset)(block, c, size); +} + +INTERCEPTOR(char*, strchr, const char *str, int c) { + if (!asan_inited) return internal_strchr(str, c); + // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is + // used. + if (asan_init_is_running) { + return REAL(strchr)(str, c); + } + ENSURE_ASAN_INITED(); + char *result = REAL(strchr)(str, c); + if (flags()->replace_str) { + uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1; + ASAN_READ_RANGE(str, bytes_read); + } + return result; +} + +#if ASAN_INTERCEPT_INDEX +# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX +INTERCEPTOR(char*, index, const char *string, int c) + ALIAS(WRAPPER_NAME(strchr)); +# else +# if SANITIZER_MAC +DECLARE_REAL(char*, index, const char *string, int c) +OVERRIDE_FUNCTION(index, strchr); +# else +DEFINE_REAL(char*, index, const char *string, int c) +# endif +# endif +#endif // ASAN_INTERCEPT_INDEX + +// For both strcat() and strncat() we need to check the validity of |to| +// argument irrespective of the |from| length. +INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = REAL(strlen)(from); + ASAN_READ_RANGE(from, from_length + 1); + uptr to_length = REAL(strlen)(to); + ASAN_READ_RANGE(to, to_length); + ASAN_WRITE_RANGE(to + to_length, from_length + 1); + // If the copying actually happens, the |from| string should not overlap + // with the resulting string starting at |to|, which has a length of + // to_length + from_length + 1. + if (from_length > 0) { + CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1, + from, from_length + 1); + } + } + return REAL(strcat)(to, from); // NOLINT +} + +INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = MaybeRealStrnlen(from, size); + uptr copy_length = Min(size, from_length + 1); + ASAN_READ_RANGE(from, copy_length); + uptr to_length = REAL(strlen)(to); + ASAN_READ_RANGE(to, to_length); + ASAN_WRITE_RANGE(to + to_length, from_length + 1); + if (from_length > 0) { + CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, + from, copy_length); + } + } + return REAL(strncat)(to, from, size); +} + +INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT +#if SANITIZER_MAC + if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT +#endif + // strcpy is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (asan_init_is_running) { + return REAL(strcpy)(to, from); // NOLINT + } + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = REAL(strlen)(from) + 1; + CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); + ASAN_READ_RANGE(from, from_size); + ASAN_WRITE_RANGE(to, from_size); + } + return REAL(strcpy)(to, from); // NOLINT +} + +#if ASAN_INTERCEPT_STRDUP +INTERCEPTOR(char*, strdup, const char *s) { + if (!asan_inited) return internal_strdup(s); + ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); + if (flags()->replace_str) { + ASAN_READ_RANGE(s, length + 1); + } + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast(new_mem); +} +#endif + +INTERCEPTOR(uptr, strlen, const char *s) { + if (!asan_inited) return internal_strlen(s); + // strlen is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (asan_init_is_running) { + return REAL(strlen)(s); + } + ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); + if (flags()->replace_str) { + ASAN_READ_RANGE(s, length + 1); + } + return length; +} + +INTERCEPTOR(uptr, wcslen, const wchar_t *s) { + uptr length = REAL(wcslen)(s); + if (!asan_init_is_running) { + ENSURE_ASAN_INITED(); + ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t)); + } + return length; +} + +INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); + CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); + ASAN_READ_RANGE(from, from_size); + ASAN_WRITE_RANGE(to, size); + } + return REAL(strncpy)(to, from, size); +} + +#if ASAN_INTERCEPT_STRNLEN +INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) { + ENSURE_ASAN_INITED(); + uptr length = REAL(strnlen)(s, maxlen); + if (flags()->replace_str) { + ASAN_READ_RANGE(s, Min(length + 1, maxlen)); + } + return length; +} +#endif // ASAN_INTERCEPT_STRNLEN + +static inline bool IsValidStrtolBase(int base) { + return (base == 0) || (2 <= base && base <= 36); +} + +static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { + CHECK(endptr); + if (nptr == *endptr) { + // No digits were found at strtol call, we need to find out the last + // symbol accessed by strtoll on our own. + // We get this symbol by skipping leading blanks and optional +/- sign. + while (IsSpace(*nptr)) nptr++; + if (*nptr == '+' || *nptr == '-') nptr++; + *endptr = (char*)nptr; + } + CHECK(*endptr >= nptr); +} + +INTERCEPTOR(long, strtol, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtol)(nptr, endptr, base); + } + char *real_endptr; + long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT + if (endptr != 0) { + *endptr = real_endptr; + } + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} + +INTERCEPTOR(int, atoi, const char *nptr) { +#if SANITIZER_MAC + if (!asan_inited) return REAL(atoi)(nptr); +#endif + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atoi)(nptr); + } + char *real_endptr; + // "man atoi" tells that behavior of atoi(nptr) is the same as + // strtol(nptr, 0, 10), i.e. it sets errno to ERANGE if the + // parsed integer can't be stored in *long* type (even if it's + // different from int). So, we just imitate this behavior. + int result = REAL(strtol)(nptr, &real_endptr, 10); + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} + +INTERCEPTOR(long, atol, const char *nptr) { // NOLINT +#if SANITIZER_MAC + if (!asan_inited) return REAL(atol)(nptr); +#endif + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atol)(nptr); + } + char *real_endptr; + long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} + +#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL +INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtoll)(nptr, endptr, base); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT + if (endptr != 0) { + *endptr = real_endptr; + } + // If base has unsupported value, strtoll can exit with EINVAL + // without reading any characters. So do additional checks only + // if base is valid. + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} + +INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atoll)(nptr); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} +#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL + +static void AtCxaAtexit(void *unused) { + (void)unused; + StopInitOrderChecking(); +} + +#if ASAN_INTERCEPT___CXA_ATEXIT +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { +#if SANITIZER_MAC + if (!asan_inited) return REAL(__cxa_atexit)(func, arg, dso_handle); +#endif + ENSURE_ASAN_INITED(); + int res = REAL(__cxa_atexit)(func, arg, dso_handle); + REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); + return res; +} +#endif // ASAN_INTERCEPT___CXA_ATEXIT + +#if SANITIZER_WINDOWS +INTERCEPTOR_WINAPI(DWORD, CreateThread, + void* security, uptr stack_size, + DWORD (__stdcall *start_routine)(void*), void* arg, + DWORD thr_flags, void* tid) { + // Strict init-order checking in thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); + GET_STACK_TRACE_THREAD; + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg); + CreateThreadContextArgs args = { t, &stack }; + bool detached = false; // FIXME: how can we determine it on Windows? + asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args); + return REAL(CreateThread)(security, stack_size, + asan_thread_start, t, thr_flags, tid); +} + +namespace __asan { +void InitializeWindowsInterceptors() { + ASAN_INTERCEPT_FUNC(CreateThread); +} + +} // namespace __asan +#endif + +// ---------------------- InitializeAsanInterceptors ---------------- {{{1 +namespace __asan { +void InitializeAsanInterceptors() { + static bool was_called_once; + CHECK(was_called_once == false); + was_called_once = true; + SANITIZER_COMMON_INTERCEPTORS_INIT; + + // Intercept mem* functions. + ASAN_INTERCEPT_FUNC(memcmp); + ASAN_INTERCEPT_FUNC(memmove); + ASAN_INTERCEPT_FUNC(memset); + if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { + ASAN_INTERCEPT_FUNC(memcpy); + } + + // Intercept str* functions. + ASAN_INTERCEPT_FUNC(strcat); // NOLINT + ASAN_INTERCEPT_FUNC(strchr); + ASAN_INTERCEPT_FUNC(strcpy); // NOLINT + ASAN_INTERCEPT_FUNC(strlen); + ASAN_INTERCEPT_FUNC(wcslen); + ASAN_INTERCEPT_FUNC(strncat); + ASAN_INTERCEPT_FUNC(strncpy); +#if ASAN_INTERCEPT_STRDUP + ASAN_INTERCEPT_FUNC(strdup); +#endif +#if ASAN_INTERCEPT_STRNLEN + ASAN_INTERCEPT_FUNC(strnlen); +#endif +#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX + ASAN_INTERCEPT_FUNC(index); +#endif + + ASAN_INTERCEPT_FUNC(atoi); + ASAN_INTERCEPT_FUNC(atol); + ASAN_INTERCEPT_FUNC(strtol); +#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL + ASAN_INTERCEPT_FUNC(atoll); + ASAN_INTERCEPT_FUNC(strtoll); +#endif + +#if ASAN_INTERCEPT_MLOCKX + // Intercept mlock/munlock. + ASAN_INTERCEPT_FUNC(mlock); + ASAN_INTERCEPT_FUNC(munlock); + ASAN_INTERCEPT_FUNC(mlockall); + ASAN_INTERCEPT_FUNC(munlockall); +#endif + + // Intecept signal- and jump-related functions. + ASAN_INTERCEPT_FUNC(longjmp); +#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION + ASAN_INTERCEPT_FUNC(sigaction); + ASAN_INTERCEPT_FUNC(signal); +#endif +#if ASAN_INTERCEPT_SWAPCONTEXT + ASAN_INTERCEPT_FUNC(swapcontext); +#endif +#if ASAN_INTERCEPT__LONGJMP + ASAN_INTERCEPT_FUNC(_longjmp); +#endif +#if ASAN_INTERCEPT_SIGLONGJMP + ASAN_INTERCEPT_FUNC(siglongjmp); +#endif + + // Intercept exception handling functions. +#if ASAN_INTERCEPT___CXA_THROW + INTERCEPT_FUNCTION(__cxa_throw); +#endif + + // Intercept threading-related functions +#if ASAN_INTERCEPT_PTHREAD_CREATE + ASAN_INTERCEPT_FUNC(pthread_create); +#endif + + // Intercept atexit function. +#if ASAN_INTERCEPT___CXA_ATEXIT + ASAN_INTERCEPT_FUNC(__cxa_atexit); +#endif + + // Some Windows-specific interceptors. +#if SANITIZER_WINDOWS + InitializeWindowsInterceptors(); +#endif + + if (common_flags()->verbosity > 0) { + Report("AddressSanitizer: libc interceptors initialized\n"); + } +} + +} // namespace __asan diff --git a/projects/compiler-rt/lib/asan/asan_interceptors.h b/projects/compiler-rt/lib/asan/asan_interceptors.h new file mode 100644 index 00000000..91830aa1 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_interceptors.h @@ -0,0 +1,38 @@ +//===-- asan_interceptors.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_interceptors.cc +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERCEPTORS_H +#define ASAN_INTERCEPTORS_H + +#include "asan_internal.h" +#include "interception/interception.h" + +DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) +DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size) +DECLARE_REAL(void*, memset, void *block, int c, uptr size) +DECLARE_REAL(char*, strchr, const char *str, int c) +DECLARE_REAL(uptr, strlen, const char *s) +DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size) +DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) +DECLARE_REAL(char*, strstr, const char *s1, const char *s2) +struct sigaction; +DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) + +namespace __asan { + +void InitializeAsanInterceptors(); + +} // namespace __asan + +#endif // ASAN_INTERCEPTORS_H diff --git a/projects/compiler-rt/lib/asan/asan_interface_internal.h b/projects/compiler-rt/lib/asan/asan_interface_internal.h new file mode 100644 index 00000000..5c1d0252 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_interface_internal.h @@ -0,0 +1,130 @@ +//===-- asan_interface_internal.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This header can be included by the instrumented program to fetch +// data (mostly allocator statistics) from ASan runtime library. +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERFACE_INTERNAL_H +#define ASAN_INTERFACE_INTERNAL_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +using __sanitizer::uptr; + +extern "C" { + // This function should be called at the very beginning of the process, + // before any instrumented code is executed and before any call to malloc. + // Everytime the asan ABI changes we also change the version number in this + // name. Objects build with incompatible asan ABI version + // will not link with run-time. + // Changes between ABI versions: + // v1=>v2: added 'module_name' to __asan_global + // v2=>v3: stack frame description (created by the compiler) + // contains the function PC as the 3-rd field (see + // DescribeAddressIfStack). + SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3(); + #define __asan_init __asan_init_v3 + + // This structure describes an instrumented global variable. + struct __asan_global { + uptr beg; // The address of the global. + uptr size; // The original size of the global. + uptr size_with_redzone; // The size with the redzone. + const char *name; // Name as a C string. + const char *module_name; // Module name as a C string. This pointer is a + // unique identifier of a module. + uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. + }; + + // These two functions should be called by the instrumented code. + // 'globals' is an array of structures describing 'n' globals. + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_register_globals(__asan_global *globals, uptr n); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unregister_globals(__asan_global *globals, uptr n); + + // These two functions should be called before and after dynamic initializers + // of a single module run, respectively. + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_before_dynamic_init(const char *module_name); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_after_dynamic_init(); + + // These two functions are used by instrumented code in the + // use-after-scope mode. They mark memory for local variables as + // unaddressable when they leave scope and addressable before the + // function exits. + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_stack_memory(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_stack_memory(uptr addr, uptr size); + + // Performs cleanup before a NoReturn function. Must be called before things + // like _exit and execl to avoid false positives on stack. + SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_no_return(); + + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_memory_region(void const volatile *addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_memory_region(void const volatile *addr, uptr size); + + SANITIZER_INTERFACE_ATTRIBUTE + bool __asan_address_is_poisoned(void const volatile *addr); + + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_region_is_poisoned(uptr beg, uptr size); + + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_describe_address(uptr addr); + + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size); + + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_set_error_exit_code(int exit_code); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_death_callback(void (*callback)(void)); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_error_report_callback(void (*callback)(const char*)); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_on_error(); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer, + int out_size); + + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_estimated_allocated_size(uptr size); + + SANITIZER_INTERFACE_ATTRIBUTE bool __asan_get_ownership(const void *p); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_allocated_size(const void *p); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_current_allocated_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_heap_size(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_unmapped_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats(); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ const char* __asan_default_options(); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_free_hook(void *ptr); + + // Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return + SANITIZER_INTERFACE_ATTRIBUTE + extern int __asan_option_detect_stack_use_after_return; +} // extern "C" + +#endif // ASAN_INTERFACE_INTERNAL_H diff --git a/projects/compiler-rt/lib/asan/asan_internal.h b/projects/compiler-rt/lib/asan/asan_internal.h new file mode 100644 index 00000000..70e55ea0 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_internal.h @@ -0,0 +1,148 @@ +//===-- asan_internal.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header which defines various general utilities. +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERNAL_H +#define ASAN_INTERNAL_H + +#include "asan_flags.h" +#include "asan_interface_internal.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_libc.h" + +#define ASAN_DEFAULT_FAILURE_EXITCODE 1 + +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +# error "The AddressSanitizer run-time should not be" + " instrumented by AddressSanitizer" +#endif + +// Build-time configuration options. + +// If set, asan will install its own SEGV signal handler. +#ifndef ASAN_NEEDS_SEGV +# if SANITIZER_ANDROID == 1 +# define ASAN_NEEDS_SEGV 0 +# else +# define ASAN_NEEDS_SEGV 1 +# endif +#endif + +// If set, asan will intercept C++ exception api call(s). +#ifndef ASAN_HAS_EXCEPTIONS +# define ASAN_HAS_EXCEPTIONS 1 +#endif + +// If set, asan uses the values of SHADOW_SCALE and SHADOW_OFFSET +// provided by the instrumented objects. Otherwise constants are used. +#ifndef ASAN_FLEXIBLE_MAPPING_AND_OFFSET +# define ASAN_FLEXIBLE_MAPPING_AND_OFFSET 0 +#endif + +// If set, values like allocator chunk size, as well as defaults for some flags +// will be changed towards less memory overhead. +#ifndef ASAN_LOW_MEMORY +#if SANITIZER_WORDSIZE == 32 +# define ASAN_LOW_MEMORY 1 +#else +# define ASAN_LOW_MEMORY 0 +# endif +#endif + +#ifndef ASAN_USE_PREINIT_ARRAY +# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID) +#endif + +// All internal functions in asan reside inside the __asan namespace +// to avoid namespace collisions with the user programs. +// Seperate namespace also makes it simpler to distinguish the asan run-time +// functions from the instrumented user code in a profile. +namespace __asan { + +class AsanThread; +using __sanitizer::StackTrace; + +// asan_rtl.cc +void NORETURN ShowStatsAndAbort(); + +void ReplaceOperatorsNewAndDelete(); +// asan_malloc_linux.cc / asan_malloc_mac.cc +void ReplaceSystemMalloc(); + +// asan_linux.cc / asan_mac.cc / asan_win.cc +void *AsanDoesNotSupportStaticLinkage(); + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); + +void MaybeReexec(); +bool AsanInterceptsSignal(int signum); +void SetAlternateSignalStack(); +void UnsetAlternateSignalStack(); +void InstallSignalHandlers(); +void ReadContextStack(void *context, uptr *stack, uptr *ssize); +void AsanPlatformThreadInit(); +void StopInitOrderChecking(); + +// Wrapper for TLS/TSD. +void AsanTSDInit(void (*destructor)(void *tsd)); +void *AsanTSDGet(); +void AsanTSDSet(void *tsd); +void PlatformTSDDtor(void *tsd); + +void AppendToErrorMessageBuffer(const char *buffer); + +// Platfrom-specific options. +#if SANITIZER_MAC +bool PlatformHasDifferentMemcpyAndMemmove(); +# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ + (PlatformHasDifferentMemcpyAndMemmove()) +#else +# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true +#endif // SANITIZER_MAC + +// Add convenient macro for interface functions that may be represented as +// weak hooks. +#define ASAN_MALLOC_HOOK(ptr, size) \ + if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size) +#define ASAN_FREE_HOOK(ptr) \ + if (&__asan_free_hook) __asan_free_hook(ptr) +#define ASAN_ON_ERROR() \ + if (&__asan_on_error) __asan_on_error() + +extern int asan_inited; +// Used to avoid infinite recursion in __asan_init(). +extern bool asan_init_is_running; +extern void (*death_callback)(void); + +// These magic values are written to shadow for better error reporting. +const int kAsanHeapLeftRedzoneMagic = 0xfa; +const int kAsanHeapRightRedzoneMagic = 0xfb; +const int kAsanHeapFreeMagic = 0xfd; +const int kAsanStackLeftRedzoneMagic = 0xf1; +const int kAsanStackMidRedzoneMagic = 0xf2; +const int kAsanStackRightRedzoneMagic = 0xf3; +const int kAsanStackPartialRedzoneMagic = 0xf4; +const int kAsanStackAfterReturnMagic = 0xf5; +const int kAsanInitializationOrderMagic = 0xf6; +const int kAsanUserPoisonedMemoryMagic = 0xf7; +const int kAsanStackUseAfterScopeMagic = 0xf8; +const int kAsanGlobalRedzoneMagic = 0xf9; +const int kAsanInternalHeapMagic = 0xfe; + +static const uptr kCurrentStackFrameMagic = 0x41B58AB3; +static const uptr kRetiredStackFrameMagic = 0x45E0360E; + +} // namespace __asan + +#endif // ASAN_INTERNAL_H diff --git a/projects/compiler-rt/lib/asan/asan_linux.cc b/projects/compiler-rt/lib/asan/asan_linux.cc new file mode 100644 index 00000000..39eec3bf --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_linux.cc @@ -0,0 +1,130 @@ +//===-- asan_linux.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !SANITIZER_ANDROID +// FIXME: where to get ucontext on Android? +#include +#endif + +extern "C" void* _DYNAMIC; + +namespace __asan { + +void MaybeReexec() { + // No need to re-exec on Linux. +} + +void *AsanDoesNotSupportStaticLinkage() { + // This will fail to link with -static. + return &_DYNAMIC; // defined in link.h +} + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { +#if SANITIZER_ANDROID + *pc = *sp = *bp = 0; +#elif defined(__arm__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.arm_pc; + *bp = ucontext->uc_mcontext.arm_fp; + *sp = ucontext->uc_mcontext.arm_sp; +# elif defined(__hppa__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.sc_iaoq[0]; + /* GCC uses %r3 whenever a frame pointer is needed. */ + *bp = ucontext->uc_mcontext.sc_gr[3]; + *sp = ucontext->uc_mcontext.sc_gr[30]; +# elif defined(__x86_64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_RIP]; + *bp = ucontext->uc_mcontext.gregs[REG_RBP]; + *sp = ucontext->uc_mcontext.gregs[REG_RSP]; +# elif defined(__i386__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_EIP]; + *bp = ucontext->uc_mcontext.gregs[REG_EBP]; + *sp = ucontext->uc_mcontext.gregs[REG_ESP]; +# elif defined(__powerpc__) || defined(__powerpc64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.regs->nip; + *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; + // The powerpc{,64}-linux ABIs do not specify r31 as the frame + // pointer, but GCC always uses r31 when we need a frame pointer. + *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; +# elif defined(__sparc__) + ucontext_t *ucontext = (ucontext_t*)context; + uptr *stk_ptr; +# if defined (__arch64__) + *pc = ucontext->uc_mcontext.mc_gregs[MC_PC]; + *sp = ucontext->uc_mcontext.mc_gregs[MC_O6]; + stk_ptr = (uptr *) (*sp + 2047); + *bp = stk_ptr[15]; +# else + *pc = ucontext->uc_mcontext.gregs[REG_PC]; + *sp = ucontext->uc_mcontext.gregs[REG_O6]; + stk_ptr = (uptr *) *sp; + *bp = stk_ptr[15]; +# endif +# elif defined(__mips__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[31]; + *bp = ucontext->uc_mcontext.gregs[30]; + *sp = ucontext->uc_mcontext.gregs[29]; +#else +# error "Unsupported arch" +#endif +} + +bool AsanInterceptsSignal(int signum) { + return signum == SIGSEGV && flags()->handle_segv; +} + +void AsanPlatformThreadInit() { + // Nothing here for now. +} + +#if !SANITIZER_ANDROID +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + ucontext_t *ucp = (ucontext_t*)context; + *stack = (uptr)ucp->uc_stack.ss_sp; + *ssize = ucp->uc_stack.ss_size; +} +#else +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); +} +#endif + +} // namespace __asan + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/asan/asan_lock.h b/projects/compiler-rt/lib/asan/asan_lock.h new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/asan/asan_mac.cc b/projects/compiler-rt/lib/asan/asan_mac.cc new file mode 100644 index 00000000..e27d70ad --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_mac.cc @@ -0,0 +1,442 @@ +//===-- asan_mac.cc -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mac.h" +#include "asan_mapping.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_libc.h" + +#include // for _NSGetArgv +#include // for dladdr() +#include +#include +#include +#include +#include +#include +#include +#include +#include // for free() +#include +#include + +namespace __asan { + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { + ucontext_t *ucontext = (ucontext_t*)context; +# if SANITIZER_WORDSIZE == 64 + *pc = ucontext->uc_mcontext->__ss.__rip; + *bp = ucontext->uc_mcontext->__ss.__rbp; + *sp = ucontext->uc_mcontext->__ss.__rsp; +# else + *pc = ucontext->uc_mcontext->__ss.__eip; + *bp = ucontext->uc_mcontext->__ss.__ebp; + *sp = ucontext->uc_mcontext->__ss.__esp; +# endif // SANITIZER_WORDSIZE +} + +MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; + +MacosVersion GetMacosVersionInternal() { + int mib[2] = { CTL_KERN, KERN_OSRELEASE }; + char version[100]; + uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); + for (uptr i = 0; i < maxlen; i++) version[i] = '\0'; + // Get the version length. + CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1); + CHECK_LT(len, maxlen); + CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1); + switch (version[0]) { + case '9': return MACOS_VERSION_LEOPARD; + case '1': { + switch (version[1]) { + case '0': return MACOS_VERSION_SNOW_LEOPARD; + case '1': return MACOS_VERSION_LION; + case '2': return MACOS_VERSION_MOUNTAIN_LION; + case '3': return MACOS_VERSION_MAVERICKS; + default: return MACOS_VERSION_UNKNOWN; + } + } + default: return MACOS_VERSION_UNKNOWN; + } +} + +MacosVersion GetMacosVersion() { + atomic_uint32_t *cache = + reinterpret_cast(&cached_macos_version); + MacosVersion result = + static_cast(atomic_load(cache, memory_order_acquire)); + if (result == MACOS_VERSION_UNINITIALIZED) { + result = GetMacosVersionInternal(); + atomic_store(cache, result, memory_order_release); + } + return result; +} + +bool PlatformHasDifferentMemcpyAndMemmove() { + // On OS X 10.7 memcpy() and memmove() are both resolved + // into memmove$VARIANT$sse42. + // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34. + // TODO(glider): need to check dynamically that memcpy() and memmove() are + // actually the same function. + return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; +} + +extern "C" +void __asan_init(); + +static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; +LowLevelAllocator allocator_for_env; + +// Change the value of the env var |name|, leaking the original value. +// If |name_value| is NULL, the variable is deleted from the environment, +// otherwise the corresponding "NAME=value" string is replaced with +// |name_value|. +void LeakyResetEnv(const char *name, const char *name_value) { + char ***env_ptr = _NSGetEnviron(); + CHECK(env_ptr); + char **environ = *env_ptr; + CHECK(environ); + uptr name_len = internal_strlen(name); + while (*environ != 0) { + uptr len = internal_strlen(*environ); + if (len > name_len) { + const char *p = *environ; + if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { + // Match. + if (name_value) { + // Replace the old value with the new one. + *environ = const_cast(name_value); + } else { + // Shift the subsequent pointers back. + char **del = environ; + do { + del[0] = del[1]; + } while (*del++); + } + } + } + environ++; + } +} + +void MaybeReexec() { + if (!flags()->allow_reexec) return; + // Make sure the dynamic ASan runtime library is preloaded so that the + // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec + // ourselves. + Dl_info info; + CHECK(dladdr((void*)((uptr)__asan_init), &info)); + char *dyld_insert_libraries = + const_cast(GetEnv(kDyldInsertLibraries)); + uptr old_env_len = dyld_insert_libraries ? + internal_strlen(dyld_insert_libraries) : 0; + uptr fname_len = internal_strlen(info.dli_fname); + if (!dyld_insert_libraries || + !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) { + // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime + // library. + char program_name[1024]; + uint32_t buf_size = sizeof(program_name); + _NSGetExecutablePath(program_name, &buf_size); + char *new_env = const_cast(info.dli_fname); + if (dyld_insert_libraries) { + // Append the runtime dylib name to the existing value of + // DYLD_INSERT_LIBRARIES. + new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); + internal_strncpy(new_env, dyld_insert_libraries, old_env_len); + new_env[old_env_len] = ':'; + // Copy fname_len and add a trailing zero. + internal_strncpy(new_env + old_env_len + 1, info.dli_fname, + fname_len + 1); + // Ok to use setenv() since the wrappers don't depend on the value of + // asan_inited. + setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); + } else { + // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. + setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); + } + if (common_flags()->verbosity >= 1) { + Report("exec()-ing the program with\n"); + Report("%s=%s\n", kDyldInsertLibraries, new_env); + Report("to enable ASan wrappers.\n"); + Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n"); + } + execv(program_name, *_NSGetArgv()); + } else { + // DYLD_INSERT_LIBRARIES is set and contains the runtime library. + if (old_env_len == fname_len) { + // It's just the runtime library name - fine to unset the variable. + LeakyResetEnv(kDyldInsertLibraries, NULL); + } else { + uptr env_name_len = internal_strlen(kDyldInsertLibraries); + // Allocate memory to hold the previous env var name, its value, the '=' + // sign and the '\0' char. + char *new_env = (char*)allocator_for_env.Allocate( + old_env_len + 2 + env_name_len); + CHECK(new_env); + internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); + internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); + new_env[env_name_len] = '='; + char *new_env_pos = new_env + env_name_len + 1; + + // Iterate over colon-separated pieces of |dyld_insert_libraries|. + char *piece_start = dyld_insert_libraries; + char *piece_end = NULL; + char *old_env_end = dyld_insert_libraries + old_env_len; + do { + if (piece_start[0] == ':') piece_start++; + piece_end = REAL(strchr)(piece_start, ':'); + if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; + if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; + uptr piece_len = piece_end - piece_start; + + // If the current piece isn't the runtime library name, + // append it to new_env. + if ((piece_len != fname_len) || + (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) { + if (new_env_pos != new_env + env_name_len + 1) { + new_env_pos[0] = ':'; + new_env_pos++; + } + internal_strncpy(new_env_pos, piece_start, piece_len); + } + // Move on to the next piece. + new_env_pos += piece_len; + piece_start = piece_end; + } while (piece_start < old_env_end); + + // Can't use setenv() here, because it requires the allocator to be + // initialized. + // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in + // a separate function called after InitializeAllocator(). + LeakyResetEnv(kDyldInsertLibraries, new_env); + } + } +} + +// No-op. Mac does not support static linkage anyway. +void *AsanDoesNotSupportStaticLinkage() { + return 0; +} + +bool AsanInterceptsSignal(int signum) { + return (signum == SIGSEGV || signum == SIGBUS) && flags()->handle_segv; +} + +void AsanPlatformThreadInit() { +} + +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); +} + +// Support for the following functions from libdispatch on Mac OS: +// dispatch_async_f() +// dispatch_async() +// dispatch_sync_f() +// dispatch_sync() +// dispatch_after_f() +// dispatch_after() +// dispatch_group_async_f() +// dispatch_group_async() +// TODO(glider): libdispatch API contains other functions that we don't support +// yet. +// +// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are +// they can cause jobs to run on a thread different from the current one. +// TODO(glider): if so, we need a test for this (otherwise we should remove +// them). +// +// The following functions use dispatch_barrier_async_f() (which isn't a library +// function but is exported) and are thus supported: +// dispatch_source_set_cancel_handler_f() +// dispatch_source_set_cancel_handler() +// dispatch_source_set_event_handler_f() +// dispatch_source_set_event_handler() +// +// The reference manual for Grand Central Dispatch is available at +// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html +// The implementation details are at +// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c + +typedef void* dispatch_group_t; +typedef void* dispatch_queue_t; +typedef void* dispatch_source_t; +typedef u64 dispatch_time_t; +typedef void (*dispatch_function_t)(void *block); +typedef void* (*worker_t)(void *block); + +// A wrapper for the ObjC blocks used to support libdispatch. +typedef struct { + void *block; + dispatch_function_t func; + u32 parent_tid; +} asan_block_context_t; + +ALWAYS_INLINE +void asan_register_worker_thread(int parent_tid, StackTrace *stack) { + AsanThread *t = GetCurrentThread(); + if (!t) { + t = AsanThread::Create(0, 0); + CreateThreadContextArgs args = { t, stack }; + asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args); + t->Init(); + asanThreadRegistry().StartThread(t->tid(), 0, 0); + SetCurrentThread(t); + } +} + +// For use by only those functions that allocated the context via +// alloc_asan_context(). +extern "C" +void asan_dispatch_call_block_and_release(void *block) { + GET_STACK_TRACE_THREAD; + asan_block_context_t *context = (asan_block_context_t*)block; + if (common_flags()->verbosity >= 2) { + Report("asan_dispatch_call_block_and_release(): " + "context: %p, pthread_self: %p\n", + block, pthread_self()); + } + asan_register_worker_thread(context->parent_tid, &stack); + // Call the original dispatcher for the block. + context->func(context->block); + asan_free(context, &stack, FROM_MALLOC); +} + +} // namespace __asan + +using namespace __asan; // NOLINT + +// Wrap |ctxt| and |func| into an asan_block_context_t. +// The caller retains control of the allocated context. +extern "C" +asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, + StackTrace *stack) { + asan_block_context_t *asan_ctxt = + (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); + asan_ctxt->block = ctxt; + asan_ctxt->func = func; + asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); + return asan_ctxt; +} + +// Define interceptor for dispatch_*_f function with the three most common +// parameters: dispatch_queue_t, context, dispatch_function_t. +#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ + INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ + dispatch_function_t func) { \ + GET_STACK_TRACE_THREAD; \ + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ + if (common_flags()->verbosity >= 2) { \ + Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ + asan_ctxt, pthread_self()); \ + PRINT_CURRENT_STACK(); \ + } \ + return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ + asan_dispatch_call_block_and_release); \ + } + +INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) +INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) +INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) + +INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, + dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_THREAD; + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); + if (common_flags()->verbosity >= 2) { + Report("dispatch_after_f: %p\n", asan_ctxt); + PRINT_CURRENT_STACK(); + } + return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); +} + +INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, + dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_THREAD; + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); + if (common_flags()->verbosity >= 2) { + Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", + asan_ctxt, pthread_self()); + PRINT_CURRENT_STACK(); + } + REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); +} + +#if !defined(MISSING_BLOCKS_SUPPORT) +extern "C" { +// FIXME: consolidate these declarations with asan_intercepted_functions.h. +void dispatch_async(dispatch_queue_t dq, void(^work)(void)); +void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, + void(^work)(void)); +void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, + void(^work)(void)); +void dispatch_source_set_cancel_handler(dispatch_source_t ds, + void(^work)(void)); +void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); +} + +#define GET_ASAN_BLOCK(work) \ + void (^asan_block)(void); \ + int parent_tid = GetCurrentTidOrInvalid(); \ + asan_block = ^(void) { \ + GET_STACK_TRACE_THREAD; \ + asan_register_worker_thread(parent_tid, &stack); \ + work(); \ + } + +INTERCEPTOR(void, dispatch_async, + dispatch_queue_t dq, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_async)(dq, asan_block); +} + +INTERCEPTOR(void, dispatch_group_async, + dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_group_async)(dg, dq, asan_block); +} + +INTERCEPTOR(void, dispatch_after, + dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_after)(when, queue, asan_block); +} + +INTERCEPTOR(void, dispatch_source_set_cancel_handler, + dispatch_source_t ds, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_source_set_cancel_handler)(ds, asan_block); +} + +INTERCEPTOR(void, dispatch_source_set_event_handler, + dispatch_source_t ds, void(^work)(void)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_source_set_event_handler)(ds, asan_block); +} +#endif + +#endif // SANITIZER_MAC diff --git a/projects/compiler-rt/lib/asan/asan_mac.h b/projects/compiler-rt/lib/asan/asan_mac.h new file mode 100644 index 00000000..827b8b00 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_mac.h @@ -0,0 +1,59 @@ +//===-- asan_mac.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific ASan definitions. +//===----------------------------------------------------------------------===// +#ifndef ASAN_MAC_H +#define ASAN_MAC_H + +// CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal +// and subject to change in further CoreFoundation versions. Apple does not +// guarantee any binary compatibility from release to release. + +// See http://opensource.apple.com/source/CF/CF-635.15/CFInternal.h +#if defined(__BIG_ENDIAN__) +#define CF_RC_BITS 0 +#endif + +#if defined(__LITTLE_ENDIAN__) +#define CF_RC_BITS 3 +#endif + +// See http://opensource.apple.com/source/CF/CF-635.15/CFRuntime.h +typedef struct __CFRuntimeBase { + uptr _cfisa; + u8 _cfinfo[4]; +#if __LP64__ + u32 _rc; +#endif +} CFRuntimeBase; + +enum MacosVersion { + MACOS_VERSION_UNINITIALIZED = 0, + MACOS_VERSION_UNKNOWN, + MACOS_VERSION_LEOPARD, + MACOS_VERSION_SNOW_LEOPARD, + MACOS_VERSION_LION, + MACOS_VERSION_MOUNTAIN_LION, + MACOS_VERSION_MAVERICKS +}; + +// Used by asan_malloc_mac.cc and asan_mac.cc +extern "C" void __CFInitialize(); + +namespace __asan { + +MacosVersion GetMacosVersion(); +void MaybeReplaceCFAllocator(); + +} // namespace __asan + +#endif // ASAN_MAC_H diff --git a/projects/compiler-rt/lib/asan/asan_malloc_linux.cc b/projects/compiler-rt/lib/asan/asan_malloc_linux.cc new file mode 100644 index 00000000..24b7f697 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_malloc_linux.cc @@ -0,0 +1,151 @@ +//===-- asan_malloc_linux.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific malloc interception. +// We simply define functions like malloc, free, realloc, etc. +// They will replace the corresponding libc functions automagically. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" + +#if SANITIZER_ANDROID +DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) +DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size) + +struct MallocDebug { + void* (*malloc)(uptr bytes); + void (*free)(void* mem); + void* (*calloc)(uptr n_elements, uptr elem_size); + void* (*realloc)(void* oldMem, uptr bytes); + void* (*memalign)(uptr alignment, uptr bytes); +}; + +const MallocDebug asan_malloc_dispatch ALIGNED(32) = { + WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign) +}; + +extern "C" const MallocDebug* __libc_malloc_dispatch; + +namespace __asan { +void ReplaceSystemMalloc() { + __libc_malloc_dispatch = &asan_malloc_dispatch; +} +} // namespace __asan + +#else // ANDROID + +namespace __asan { +void ReplaceSystemMalloc() { +} +} // namespace __asan +#endif // ANDROID + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +INTERCEPTOR(void, free, void *ptr) { + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); +} + +INTERCEPTOR(void, cfree, void *ptr) { + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); +} + +INTERCEPTOR(void*, malloc, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_malloc(size, &stack); +} + +INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (!asan_inited) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const uptr kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr allocated; + uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + GET_STACK_TRACE_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +INTERCEPTOR(void*, realloc, void *ptr, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_memalign(boundary, size, &stack, FROM_MALLOC); +} + +INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s) + ALIAS("memalign"); + +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); +} + +// We avoid including malloc.h for portability reasons. +// man mallinfo says the fields are "long", but the implementation uses int. +// It doesn't matter much -- we just need to make sure that the libc's mallinfo +// is not called. +struct fake_mallinfo { + int x[10]; +}; + +INTERCEPTOR(struct fake_mallinfo, mallinfo, void) { + struct fake_mallinfo res; + REAL(memset)(&res, 0, sizeof(res)); + return res; +} + +INTERCEPTOR(int, mallopt, int cmd, int value) { + return -1; +} + +INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { + GET_STACK_TRACE_MALLOC; + // Printf("posix_memalign: %zx %zu\n", alignment, size); + return asan_posix_memalign(memptr, alignment, size, &stack); +} + +INTERCEPTOR(void*, valloc, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_valloc(size, &stack); +} + +INTERCEPTOR(void*, pvalloc, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_pvalloc(size, &stack); +} + +INTERCEPTOR(void, malloc_stats, void) { + __asan_print_accumulated_stats(); +} + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/asan/asan_malloc_mac.cc b/projects/compiler-rt/lib/asan/asan_malloc_mac.cc new file mode 100644 index 00000000..f9f08f02 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_malloc_mac.cc @@ -0,0 +1,359 @@ +//===-- asan_malloc_mac.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific malloc interception. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include +#include +#include +#include +#include + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mac.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" + +// Similar code is used in Google Perftools, +// http://code.google.com/p/google-perftools. + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +// TODO(glider): do we need both zones? +static malloc_zone_t *system_malloc_zone = 0; +static malloc_zone_t asan_zone; + +INTERCEPTOR(malloc_zone_t *, malloc_create_zone, + vm_size_t start_size, unsigned zone_flags) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + uptr page_size = GetPageSizeCached(); + uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); + malloc_zone_t *new_zone = + (malloc_zone_t*)asan_memalign(page_size, allocated_size, + &stack, FROM_MALLOC); + internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); + new_zone->zone_name = NULL; // The name will be changed anyway. + if (GetMacosVersion() >= MACOS_VERSION_LION) { + // Prevent the client app from overwriting the zone contents. + // Library functions that need to modify the zone will set PROT_WRITE on it. + // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher. + mprotect(new_zone, allocated_size, PROT_READ); + } + return new_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { + if (!asan_inited) __asan_init(); + return &asan_zone; +} + +INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { + // FIXME: ASan should support purgeable allocations. + // https://code.google.com/p/address-sanitizer/issues/detail?id=139 + if (!asan_inited) __asan_init(); + return &asan_zone; +} + +INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { + // FIXME: ASan should support purgeable allocations. Ignoring them is fine + // for now. + if (!asan_inited) __asan_init(); +} + +INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { + // FIXME: ASan should support purgeable allocations. Ignoring them is fine + // for now. + if (!asan_inited) __asan_init(); + // Must return 0 if the contents were not purged since the last call to + // malloc_make_purgeable(). + return 0; +} + +INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { + if (!asan_inited) __asan_init(); + // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes. + size_t buflen = 6 + (name ? internal_strlen(name) : 0); + InternalScopedBuffer new_name(buflen); + if (name && zone->introspect == asan_zone.introspect) { + internal_snprintf(new_name.data(), buflen, "asan-%s", name); + name = new_name.data(); + } + + // Call the system malloc's implementation for both external and our zones, + // since that appropriately changes VM region protections on the zone. + REAL(malloc_set_zone_name)(zone, name); +} + +INTERCEPTOR(void *, malloc, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + void *res = asan_malloc(size, &stack); + return res; +} + +INTERCEPTOR(void, free, void *ptr) { + if (!asan_inited) __asan_init(); + if (!ptr) return; + GET_STACK_TRACE_FREE; + asan_free(ptr, &stack, FROM_MALLOC); +} + +INTERCEPTOR(void *, realloc, void *ptr, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +INTERCEPTOR(void *, valloc, size_t size) { + if (!asan_inited) __asan_init(); + GET_STACK_TRACE_MALLOC; + return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); +} + +INTERCEPTOR(size_t, malloc_good_size, size_t size) { + if (!asan_inited) __asan_init(); + return asan_zone.introspect->good_size(&asan_zone, size); +} + +INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { + if (!asan_inited) __asan_init(); + CHECK(memptr); + GET_STACK_TRACE_MALLOC; + void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC); + if (result) { + *memptr = result; + return 0; + } + return -1; +} + +namespace { + +// TODO(glider): the mz_* functions should be united with the Linux wrappers, +// as they are basically copied from there. +size_t mz_size(malloc_zone_t* zone, const void* ptr) { + return asan_mz_size(ptr); +} + +void *mz_malloc(malloc_zone_t *zone, size_t size) { + if (!asan_inited) { + CHECK(system_malloc_zone); + return malloc_zone_malloc(system_malloc_zone, size); + } + GET_STACK_TRACE_MALLOC; + return asan_malloc(size, &stack); +} + +void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { + if (!asan_inited) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const size_t kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static size_t allocated; + size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + GET_STACK_TRACE_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +void *mz_valloc(malloc_zone_t *zone, size_t size) { + if (!asan_inited) { + CHECK(system_malloc_zone); + return malloc_zone_valloc(system_malloc_zone, size); + } + GET_STACK_TRACE_MALLOC; + return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); +} + +#define GET_ZONE_FOR_PTR(ptr) \ + malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ + const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name + +void ALWAYS_INLINE free_common(void *context, void *ptr) { + if (!ptr) return; + GET_STACK_TRACE_FREE; + // FIXME: need to retire this flag. + if (!flags()->mac_ignore_invalid_free) { + asan_free(ptr, &stack, FROM_MALLOC); + } else { + GET_ZONE_FOR_PTR(ptr); + WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); + return; + } +} + +// TODO(glider): the allocation callbacks need to be refactored. +void mz_free(malloc_zone_t *zone, void *ptr) { + free_common(zone, ptr); +} + +void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { + if (!ptr) { + GET_STACK_TRACE_MALLOC; + return asan_malloc(size, &stack); + } else { + if (asan_mz_size(ptr)) { + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack); + } else { + // We can't recover from reallocating an unknown address, because + // this would require reading at most |size| bytes from + // potentially unaccessible memory. + GET_STACK_TRACE_FREE; + GET_ZONE_FOR_PTR(ptr); + ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); + } + } +} + +void mz_destroy(malloc_zone_t* zone) { + // A no-op -- we will not be destroyed! + Report("mz_destroy() called -- ignoring\n"); +} + + // from AvailabilityMacros.h +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { + if (!asan_inited) { + CHECK(system_malloc_zone); + return malloc_zone_memalign(system_malloc_zone, align, size); + } + GET_STACK_TRACE_MALLOC; + return asan_memalign(align, size, &stack, FROM_MALLOC); +} + +// This function is currently unused, and we build with -Werror. +#if 0 +void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) { + // TODO(glider): check that |size| is valid. + UNIMPLEMENTED(); +} +#endif +#endif + +kern_return_t mi_enumerator(task_t task, void *, + unsigned type_mask, vm_address_t zone_address, + memory_reader_t reader, + vm_range_recorder_t recorder) { + // Should enumerate all the pointers we have. Seems like a lot of work. + return KERN_FAILURE; +} + +size_t mi_good_size(malloc_zone_t *zone, size_t size) { + // I think it's always safe to return size, but we maybe could do better. + return size; +} + +boolean_t mi_check(malloc_zone_t *zone) { + UNIMPLEMENTED(); +} + +void mi_print(malloc_zone_t *zone, boolean_t verbose) { + UNIMPLEMENTED(); +} + +void mi_log(malloc_zone_t *zone, void *address) { + // I don't think we support anything like this +} + +void mi_force_lock(malloc_zone_t *zone) { + asan_mz_force_lock(); +} + +void mi_force_unlock(malloc_zone_t *zone) { + asan_mz_force_unlock(); +} + +void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { + AsanMallocStats malloc_stats; + FillMallocStatistics(&malloc_stats); + CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); + internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); +} + +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +boolean_t mi_zone_locked(malloc_zone_t *zone) { + // UNIMPLEMENTED(); + return false; +} +#endif + +} // unnamed namespace + +namespace __asan { + +void ReplaceSystemMalloc() { + static malloc_introspection_t asan_introspection; + // Ok to use internal_memset, these places are not performance-critical. + internal_memset(&asan_introspection, 0, sizeof(asan_introspection)); + + asan_introspection.enumerator = &mi_enumerator; + asan_introspection.good_size = &mi_good_size; + asan_introspection.check = &mi_check; + asan_introspection.print = &mi_print; + asan_introspection.log = &mi_log; + asan_introspection.force_lock = &mi_force_lock; + asan_introspection.force_unlock = &mi_force_unlock; + asan_introspection.statistics = &mi_statistics; + + internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); + + // Start with a version 4 zone which is used for OS X 10.4 and 10.5. + asan_zone.version = 4; + asan_zone.zone_name = "asan"; + asan_zone.size = &mz_size; + asan_zone.malloc = &mz_malloc; + asan_zone.calloc = &mz_calloc; + asan_zone.valloc = &mz_valloc; + asan_zone.free = &mz_free; + asan_zone.realloc = &mz_realloc; + asan_zone.destroy = &mz_destroy; + asan_zone.batch_malloc = 0; + asan_zone.batch_free = 0; + asan_zone.introspect = &asan_introspection; + + // from AvailabilityMacros.h +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + // Switch to version 6 on OSX 10.6 to support memalign. + asan_zone.version = 6; + asan_zone.free_definite_size = 0; + asan_zone.memalign = &mz_memalign; + asan_introspection.zone_locked = &mi_zone_locked; +#endif + + // Register the ASan zone. + malloc_zone_register(&asan_zone); +} +} // namespace __asan + +#endif // SANITIZER_MAC diff --git a/projects/compiler-rt/lib/asan/asan_malloc_win.cc b/projects/compiler-rt/lib/asan/asan_malloc_win.cc new file mode 100644 index 00000000..73e4c825 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_malloc_win.cc @@ -0,0 +1,156 @@ +//===-- asan_malloc_win.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific malloc interception. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" +#include "interception/interception.h" + +#include + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +// FIXME: Simply defining functions with the same signature in *.obj +// files overrides the standard functions in *.lib +// This works well for simple helloworld-like tests but might need to be +// revisited in the future. + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void free(void *ptr) { + GET_STACK_TRACE_FREE; + return asan_free(ptr, &stack, FROM_MALLOC); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void _free_dbg(void* ptr, int) { + free(ptr); +} + +void cfree(void *ptr) { + CHECK(!"cfree() should not be used on Windows?"); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *malloc(size_t size) { + GET_STACK_TRACE_MALLOC; + return asan_malloc(size, &stack); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* _malloc_dbg(size_t size, int , const char*, int) { + return malloc(size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *calloc(size_t nmemb, size_t size) { + GET_STACK_TRACE_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { + return calloc(n, size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { + return calloc(nmemb, size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *realloc(void *ptr, size_t size) { + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *_realloc_dbg(void *ptr, size_t size, int) { + CHECK(!"_realloc_dbg should not exist!"); + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void* _recalloc(void* p, size_t n, size_t elem_size) { + if (!p) + return calloc(n, elem_size); + const size_t size = n * elem_size; + if (elem_size != 0 && size / elem_size != n) + return 0; + return realloc(p, size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +size_t _msize(void *ptr) { + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); +} + +int _CrtDbgReport(int, const char*, int, + const char*, const char*, ...) { + ShowStatsAndAbort(); +} + +int _CrtDbgReportW(int reportType, const wchar_t*, int, + const wchar_t*, const wchar_t*, ...) { + ShowStatsAndAbort(); +} + +int _CrtSetReportMode(int, int) { + return 0; +} +} // extern "C" + +using __interception::GetRealFunctionAddress; + +// We don't want to include "windows.h" in this file to avoid extra attributes +// set on malloc/free etc (e.g. dllimport), so declare a few things manually: +extern "C" int __stdcall VirtualProtect(void* addr, size_t size, + DWORD prot, DWORD *old_prot); +const int PAGE_EXECUTE_READWRITE = 0x40; + +namespace __asan { +void ReplaceSystemMalloc() { +#if defined(_DLL) +# ifdef _WIN64 +# error ReplaceSystemMalloc was not tested on x64 +# endif + char *crt_malloc; + if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) { + // Replace malloc in the CRT dll with a jump to our malloc. + DWORD old_prot, unused; + CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot)); + REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case. + + ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5; + crt_malloc[0] = 0xE9; // jmp, should be followed by an offset. + REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset)); + + CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused)); + + // FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64. + } + + // FIXME: investigate whether anything else is needed. +#endif +} +} // namespace __asan + +#endif // _WIN32 diff --git a/projects/compiler-rt/lib/asan/asan_mapping.h b/projects/compiler-rt/lib/asan/asan_mapping.h new file mode 100644 index 00000000..1fecaeb3 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_mapping.h @@ -0,0 +1,238 @@ +//===-- asan_mapping.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Defines ASan memory mapping. +//===----------------------------------------------------------------------===// +#ifndef ASAN_MAPPING_H +#define ASAN_MAPPING_H + +#include "asan_internal.h" + +// The full explanation of the memory mapping could be found here: +// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm +// +// Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000: +// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || +// || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow || +// || `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap || +// || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow || +// || `[0x000000000000, 0x00007fff7fff]` || LowMem || +// +// When SHADOW_OFFSET is zero (-pie): +// || `[0x100000000000, 0x7fffffffffff]` || HighMem || +// || `[0x020000000000, 0x0fffffffffff]` || HighShadow || +// || `[0x000000040000, 0x01ffffffffff]` || ShadowGap || +// +// Special case when something is already mapped between +// 0x003000000000 and 0x005000000000 (e.g. when prelink is installed): +// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || +// || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow || +// || `[0x005000000000, 0x02008fff6fff]` || ShadowGap3 || +// || `[0x003000000000, 0x004fffffffff]` || MidMem || +// || `[0x000a7fff8000, 0x002fffffffff]` || ShadowGap2 || +// || `[0x00067fff8000, 0x000a7fff7fff]` || MidShadow || +// || `[0x00008fff7000, 0x00067fff7fff]` || ShadowGap || +// || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow || +// || `[0x000000000000, 0x00007fff7fff]` || LowMem || +// +// Default Linux/i386 mapping: +// || `[0x40000000, 0xffffffff]` || HighMem || +// || `[0x28000000, 0x3fffffff]` || HighShadow || +// || `[0x24000000, 0x27ffffff]` || ShadowGap || +// || `[0x20000000, 0x23ffffff]` || LowShadow || +// || `[0x00000000, 0x1fffffff]` || LowMem || +// +// Default Linux/MIPS mapping: +// || `[0x2aaa8000, 0xffffffff]` || HighMem || +// || `[0x0fffd000, 0x2aaa7fff]` || HighShadow || +// || `[0x0bffd000, 0x0fffcfff]` || ShadowGap || +// || `[0x0aaa8000, 0x0bffcfff]` || LowShadow || +// || `[0x00000000, 0x0aaa7fff]` || LowMem || + +static const u64 kDefaultShadowScale = 3; +static const u64 kDefaultShadowOffset32 = 1ULL << 29; +static const u64 kDefaultShadowOffset64 = 1ULL << 44; +static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. +static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; +static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000; + +#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 +extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale; +extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; +# define SHADOW_SCALE (__asan_mapping_scale) +# define SHADOW_OFFSET (__asan_mapping_offset) +#else +# define SHADOW_SCALE kDefaultShadowScale +# if SANITIZER_ANDROID +# define SHADOW_OFFSET (0) +# else +# if SANITIZER_WORDSIZE == 32 +# if defined(__mips__) +# define SHADOW_OFFSET kMIPS32_ShadowOffset32 +# else +# define SHADOW_OFFSET kDefaultShadowOffset32 +# endif +# else +# if defined(__powerpc64__) +# define SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif SANITIZER_MAC +# define SHADOW_OFFSET kDefaultShadowOffset64 +# else +# define SHADOW_OFFSET kDefaultShort64bitShadowOffset +# endif +# endif +# endif +#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET + +#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) +#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET)) +#define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE) + +#define kLowMemBeg 0 +#define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0) + +#define kLowShadowBeg SHADOW_OFFSET +#define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd) + +#define kHighMemBeg (MEM_TO_SHADOW(kHighMemEnd) + 1) + +#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg) +#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd) + +# define kMidShadowBeg MEM_TO_SHADOW(kMidMemBeg) +# define kMidShadowEnd MEM_TO_SHADOW(kMidMemEnd) + +// With the zero shadow base we can not actually map pages starting from 0. +// This constant is somewhat arbitrary. +#define kZeroBaseShadowStart (1 << 18) + +#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \ + : kZeroBaseShadowStart) +#define kShadowGapEnd ((kMidMemBeg ? kMidShadowBeg : kHighShadowBeg) - 1) + +#define kShadowGap2Beg (kMidMemBeg ? kMidShadowEnd + 1 : 0) +#define kShadowGap2End (kMidMemBeg ? kMidMemBeg - 1 : 0) + +#define kShadowGap3Beg (kMidMemBeg ? kMidMemEnd + 1 : 0) +#define kShadowGap3End (kMidMemBeg ? kHighShadowBeg - 1 : 0) + +#define DO_ASAN_MAPPING_PROFILE 0 // Set to 1 to profile the functions below. + +#if DO_ASAN_MAPPING_PROFILE +# define PROFILE_ASAN_MAPPING() AsanMappingProfile[__LINE__]++; +#else +# define PROFILE_ASAN_MAPPING() +#endif + +// If 1, all shadow boundaries are constants. +// Don't set to 1 other than for testing. +#define ASAN_FIXED_MAPPING 0 + +namespace __asan { + +extern uptr AsanMappingProfile[]; + +#if ASAN_FIXED_MAPPING +// Fixed mapping for 64-bit Linux. Mostly used for performance comparison +// with non-fixed mapping. As of r175253 (Feb 2013) the performance +// difference between fixed and non-fixed mapping is below the noise level. +static uptr kHighMemEnd = 0x7fffffffffffULL; +static uptr kMidMemBeg = 0x3000000000ULL; +static uptr kMidMemEnd = 0x4fffffffffULL; +#else +extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. +#endif + +static inline bool AddrIsInLowMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return a < kLowMemEnd; +} + +static inline bool AddrIsInLowShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return a >= kLowShadowBeg && a <= kLowShadowEnd; +} + +static inline bool AddrIsInHighMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return a >= kHighMemBeg && a <= kHighMemEnd; +} + +static inline bool AddrIsInMidMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd; +} + +static inline bool AddrIsInMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a); +} + +static inline uptr MemToShadow(uptr p) { + PROFILE_ASAN_MAPPING(); + CHECK(AddrIsInMem(p)); + return MEM_TO_SHADOW(p); +} + +static inline bool AddrIsInHighShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return a >= kHighShadowBeg && a <= kHighMemEnd; +} + +static inline bool AddrIsInMidShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return kMidMemBeg && a >= kMidShadowBeg && a <= kMidMemEnd; +} + +static inline bool AddrIsInShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a); +} + +static inline bool AddrIsInShadowGap(uptr a) { + PROFILE_ASAN_MAPPING(); + if (kMidMemBeg) { + if (a <= kShadowGapEnd) + return SHADOW_OFFSET == 0 || a >= kShadowGapBeg; + return (a >= kShadowGap2Beg && a <= kShadowGap2End) || + (a >= kShadowGap3Beg && a <= kShadowGap3End); + } + // In zero-based shadow mode we treat addresses near zero as addresses + // in shadow gap as well. + if (SHADOW_OFFSET == 0) + return a <= kShadowGapEnd; + return a >= kShadowGapBeg && a <= kShadowGapEnd; +} + +static inline bool AddrIsAlignedByGranularity(uptr a) { + PROFILE_ASAN_MAPPING(); + return (a & (SHADOW_GRANULARITY - 1)) == 0; +} + +static inline bool AddressIsPoisoned(uptr a) { + PROFILE_ASAN_MAPPING(); + const uptr kAccessSize = 1; + u8 *shadow_address = (u8*)MEM_TO_SHADOW(a); + s8 shadow_value = *shadow_address; + if (shadow_value) { + u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1)) + + kAccessSize - 1; + return (last_accessed_byte >= shadow_value); + } + return false; +} + +// Must be after all calls to PROFILE_ASAN_MAPPING(). +static const uptr kAsanMappingProfileSize = __LINE__; + +} // namespace __asan + +#endif // ASAN_MAPPING_H diff --git a/projects/compiler-rt/lib/asan/asan_new_delete.cc b/projects/compiler-rt/lib/asan/asan_new_delete.cc new file mode 100644 index 00000000..d5eb6eca --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_new_delete.cc @@ -0,0 +1,108 @@ +//===-- asan_interceptors.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Interceptors for operators new and delete. +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_stack.h" + +#include + +namespace __asan { +// This function is a no-op. We need it to make sure that object file +// with our replacements will actually be loaded from static ASan +// run-time library at link-time. +void ReplaceOperatorsNewAndDelete() { } +} + +using namespace __asan; // NOLINT + +// On Android new() goes through malloc interceptors. +// See also https://code.google.com/p/address-sanitizer/issues/detail?id=131. +#if !SANITIZER_ANDROID + +// Fake std::nothrow_t to avoid including . +namespace std { +struct nothrow_t {}; +} // namespace std + +#define OPERATOR_NEW_BODY(type) \ + GET_STACK_TRACE_MALLOC;\ + return asan_memalign(0, size, &stack, type); + +// On OS X it's not enough to just provide our own 'operator new' and +// 'operator delete' implementations, because they're going to be in the +// runtime dylib, and the main executable will depend on both the runtime +// dylib and libstdc++, each of those'll have its implementation of new and +// delete. +// To make sure that C++ allocation/deallocation operators are overridden on +// OS X we need to intercept them using their mangled names. +#if !SANITIZER_MAC +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); } +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW_BR); } + +#else // SANITIZER_MAC +INTERCEPTOR(void *, _Znwm, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _Znam, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +#endif + +#define OPERATOR_DELETE_BODY(type) \ + GET_STACK_TRACE_FREE;\ + asan_free(ptr, &stack, type); + +#if !SANITIZER_MAC +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } + +#else // SANITIZER_MAC +INTERCEPTOR(void, _ZdlPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +#endif + +#endif diff --git a/projects/compiler-rt/lib/asan/asan_poisoning.cc b/projects/compiler-rt/lib/asan/asan_poisoning.cc new file mode 100644 index 00000000..280aaeb9 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_poisoning.cc @@ -0,0 +1,299 @@ +//===-- asan_poisoning.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Shadow memory poisoning by ASan RTL and by user application. +//===----------------------------------------------------------------------===// + +#include "asan_poisoning.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_flags.h" + +namespace __asan { + +void PoisonShadow(uptr addr, uptr size, u8 value) { + if (!flags()->poison_heap) return; + CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsInMem(addr)); + CHECK(AddrIsAlignedByGranularity(addr + size)); + CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); + CHECK(REAL(memset)); + FastPoisonShadow(addr, size, value); +} + +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value) { + if (!flags()->poison_heap) return; + CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsInMem(addr)); + FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); +} + +struct ShadowSegmentEndpoint { + u8 *chunk; + s8 offset; // in [0, SHADOW_GRANULARITY) + s8 value; // = *chunk; + + explicit ShadowSegmentEndpoint(uptr address) { + chunk = (u8*)MemToShadow(address); + offset = address & (SHADOW_GRANULARITY - 1); + value = *chunk; + } +}; + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// Current implementation of __asan_(un)poison_memory_region doesn't check +// that user program (un)poisons the memory it owns. It poisons memory +// conservatively, and unpoisons progressively to make sure asan shadow +// mapping invariant is preserved (see detailed mapping description here: +// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm). +// +// * if user asks to poison region [left, right), the program poisons +// at least [left, AlignDown(right)). +// * if user asks to unpoison region [left, right), the program unpoisons +// at most [AlignDown(left), right). +void __asan_poison_memory_region(void const volatile *addr, uptr size) { + if (!flags()->allow_user_poisoning || size == 0) return; + uptr beg_addr = (uptr)addr; + uptr end_addr = beg_addr + size; + if (common_flags()->verbosity >= 1) { + Printf("Trying to poison memory region [%p, %p)\n", + (void*)beg_addr, (void*)end_addr); + } + ShadowSegmentEndpoint beg(beg_addr); + ShadowSegmentEndpoint end(end_addr); + if (beg.chunk == end.chunk) { + CHECK(beg.offset < end.offset); + s8 value = beg.value; + CHECK(value == end.value); + // We can only poison memory if the byte in end.offset is unaddressable. + // No need to re-poison memory if it is poisoned already. + if (value > 0 && value <= end.offset) { + if (beg.offset > 0) { + *beg.chunk = Min(value, beg.offset); + } else { + *beg.chunk = kAsanUserPoisonedMemoryMagic; + } + } + return; + } + CHECK(beg.chunk < end.chunk); + if (beg.offset > 0) { + // Mark bytes from beg.offset as unaddressable. + if (beg.value == 0) { + *beg.chunk = beg.offset; + } else { + *beg.chunk = Min(beg.value, beg.offset); + } + beg.chunk++; + } + REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk); + // Poison if byte in end.offset is unaddressable. + if (end.value > 0 && end.value <= end.offset) { + *end.chunk = kAsanUserPoisonedMemoryMagic; + } +} + +void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { + if (!flags()->allow_user_poisoning || size == 0) return; + uptr beg_addr = (uptr)addr; + uptr end_addr = beg_addr + size; + if (common_flags()->verbosity >= 1) { + Printf("Trying to unpoison memory region [%p, %p)\n", + (void*)beg_addr, (void*)end_addr); + } + ShadowSegmentEndpoint beg(beg_addr); + ShadowSegmentEndpoint end(end_addr); + if (beg.chunk == end.chunk) { + CHECK(beg.offset < end.offset); + s8 value = beg.value; + CHECK(value == end.value); + // We unpoison memory bytes up to enbytes up to end.offset if it is not + // unpoisoned already. + if (value != 0) { + *beg.chunk = Max(value, end.offset); + } + return; + } + CHECK(beg.chunk < end.chunk); + if (beg.offset > 0) { + *beg.chunk = 0; + beg.chunk++; + } + REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk); + if (end.offset > 0 && end.value != 0) { + *end.chunk = Max(end.value, end.offset); + } +} + +bool __asan_address_is_poisoned(void const volatile *addr) { + return __asan::AddressIsPoisoned((uptr)addr); +} + +uptr __asan_region_is_poisoned(uptr beg, uptr size) { + if (!size) return 0; + uptr end = beg + size; + if (!AddrIsInMem(beg)) return beg; + if (!AddrIsInMem(end)) return end; + uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY); + uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY); + uptr shadow_beg = MemToShadow(aligned_b); + uptr shadow_end = MemToShadow(aligned_e); + // First check the first and the last application bytes, + // then check the SHADOW_GRANULARITY-aligned region by calling + // mem_is_zero on the corresponding shadow. + if (!__asan::AddressIsPoisoned(beg) && + !__asan::AddressIsPoisoned(end - 1) && + (shadow_end <= shadow_beg || + __sanitizer::mem_is_zero((const char *)shadow_beg, + shadow_end - shadow_beg))) + return 0; + // The fast check failed, so we have a poisoned byte somewhere. + // Find it slowly. + for (; beg < end; beg++) + if (__asan::AddressIsPoisoned(beg)) + return beg; + UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found"); + return 0; +} + +#define CHECK_SMALL_REGION(p, size, isWrite) \ + do { \ + uptr __p = reinterpret_cast(p); \ + uptr __size = size; \ + if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ + __asan::AddressIsPoisoned(__p + __size - 1))) { \ + GET_CURRENT_PC_BP_SP; \ + uptr __bad = __asan_region_is_poisoned(__p, __size); \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\ + } \ + } while (false); \ + + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const uu16 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *p, u16 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *p, u32 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(uu64 *p, u64 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +// This is a simplified version of __asan_(un)poison_memory_region, which +// assumes that left border of region to be poisoned is properly aligned. +static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { + if (size == 0) return; + uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1); + PoisonShadow(addr, aligned_size, + do_poison ? kAsanStackUseAfterScopeMagic : 0); + if (size == aligned_size) + return; + s8 end_offset = (s8)(size - aligned_size); + s8* shadow_end = (s8*)MemToShadow(addr + aligned_size); + s8 end_value = *shadow_end; + if (do_poison) { + // If possible, mark all the bytes mapping to last shadow byte as + // unaddressable. + if (end_value > 0 && end_value <= end_offset) + *shadow_end = (s8)kAsanStackUseAfterScopeMagic; + } else { + // If necessary, mark few first bytes mapping to last shadow byte + // as addressable + if (end_value != 0) + *shadow_end = Max(end_value, end_offset); + } +} + +void __asan_poison_stack_memory(uptr addr, uptr size) { + if (common_flags()->verbosity > 0) + Report("poisoning: %p %zx\n", (void*)addr, size); + PoisonAlignedStackMemory(addr, size, true); +} + +void __asan_unpoison_stack_memory(uptr addr, uptr size) { + if (common_flags()->verbosity > 0) + Report("unpoisoning: %p %zx\n", (void*)addr, size); + PoisonAlignedStackMemory(addr, size, false); +} + +void __sanitizer_annotate_contiguous_container(void *beg_p, void *end_p, + void *old_mid_p, + void *new_mid_p) { + uptr beg = reinterpret_cast(beg_p); + uptr end= reinterpret_cast(end_p); + uptr old_mid = reinterpret_cast(old_mid_p); + uptr new_mid = reinterpret_cast(new_mid_p); + uptr granularity = SHADOW_GRANULARITY; + CHECK(beg <= end && beg <= old_mid && beg <= new_mid && old_mid <= end && + new_mid <= end && IsAligned(beg, granularity)); + CHECK_LE(end - beg, + FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check. + + uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); + uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); + uptr b = new_mid; + uptr b1 = RoundDownTo(b, granularity); + uptr b2 = RoundUpTo(b, granularity); + uptr d = old_mid; + uptr d1 = RoundDownTo(d, granularity); + uptr d2 = RoundUpTo(d, granularity); + // Currently we should be in this state: + // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good. + // Make a quick sanity check that we are indeed in this state. + if (d1 != d2) + CHECK_EQ(*(u8*)MemToShadow(d1), d - d1); + if (a + granularity <= d1) + CHECK_EQ(*(u8*)MemToShadow(a), 0); + if (d2 + granularity <= c && c <= end) + CHECK_EQ(*(u8 *)MemToShadow(c - granularity), kAsanUserPoisonedMemoryMagic); + + // New state: + // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good. + // FIXME: we may want to have a separate poison magic value. + PoisonShadow(a, b1 - a, 0); + PoisonShadow(b2, c - b2, kAsanUserPoisonedMemoryMagic); + if (b1 != b2) { + CHECK_EQ(b2 - b1, granularity); + *(u8*)MemToShadow(b1) = static_cast(b - b1); + } +} diff --git a/projects/compiler-rt/lib/asan/asan_poisoning.h b/projects/compiler-rt/lib/asan/asan_poisoning.h new file mode 100644 index 00000000..fbac2119 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_poisoning.h @@ -0,0 +1,60 @@ +//===-- asan_poisoning.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Shadow memory poisoning by ASan RTL and by user application. +//===----------------------------------------------------------------------===// + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" + +namespace __asan { + +// Poisons the shadow memory for "size" bytes starting from "addr". +void PoisonShadow(uptr addr, uptr size, u8 value); + +// Poisons the shadow memory for "redzone_size" bytes starting from +// "addr + size". +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value); + +// Fast versions of PoisonShadow and PoisonShadowPartialRightRedzone that +// assume that memory addresses are properly aligned. Use in +// performance-critical code with care. +ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, + u8 value) { + DCHECK(flags()->poison_heap); + uptr shadow_beg = MEM_TO_SHADOW(aligned_beg); + uptr shadow_end = MEM_TO_SHADOW( + aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1; + REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); +} + +ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( + uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { + DCHECK(flags()->poison_heap); + bool poison_partial = flags()->poison_partial; + u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); + for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { + if (i + SHADOW_GRANULARITY <= size) { + *shadow = 0; // fully addressable + } else if (i >= size) { + *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable + } else { + // first size-i bytes are addressable + *shadow = poison_partial ? static_cast(size - i) : 0; + } + } +} + +} // namespace __asan diff --git a/projects/compiler-rt/lib/asan/asan_posix.cc b/projects/compiler-rt/lib/asan/asan_posix.cc new file mode 100644 index 00000000..bcc6b381 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_posix.cc @@ -0,0 +1,130 @@ +//===-- asan_posix.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Posix-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC + +#include "asan_internal.h" +#include "asan_interceptors.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +#include +#include +#include +#include +#include +#include + +static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. + +namespace __asan { + +static void MaybeInstallSigaction(int signum, + void (*handler)(int, siginfo_t *, void *)) { + if (!AsanInterceptsSignal(signum)) + return; + struct sigaction sigact; + REAL(memset)(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = handler; + sigact.sa_flags = SA_SIGINFO; + if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; + CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0)); + if (common_flags()->verbosity >= 1) { + Report("Installed the sigaction for signal %d\n", signum); + } +} + +static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { + uptr addr = (uptr)siginfo->si_addr; + // Write the first message using the bullet-proof write. + if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die(); + uptr pc, sp, bp; + GetPcSpBp(context, &pc, &sp, &bp); + ReportSIGSEGV(pc, sp, bp, addr); +} + +void SetAlternateSignalStack() { + stack_t altstack, oldstack; + CHECK_EQ(0, sigaltstack(0, &oldstack)); + // If the alternate stack is already in place, do nothing. + if ((oldstack.ss_flags & SS_DISABLE) == 0) return; + // TODO(glider): the mapped stack should have the MAP_STACK flag in the + // future. It is not required by man 2 sigaltstack now (they're using + // malloc()). + void* base = MmapOrDie(kAltStackSize, __FUNCTION__); + altstack.ss_sp = base; + altstack.ss_flags = 0; + altstack.ss_size = kAltStackSize; + CHECK_EQ(0, sigaltstack(&altstack, 0)); + if (common_flags()->verbosity > 0) { + Report("Alternative stack for T%d set: [%p,%p)\n", + GetCurrentTidOrInvalid(), + altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); + } +} + +void UnsetAlternateSignalStack() { + stack_t altstack, oldstack; + altstack.ss_sp = 0; + altstack.ss_flags = SS_DISABLE; + altstack.ss_size = 0; + CHECK_EQ(0, sigaltstack(&altstack, &oldstack)); + UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); +} + +void InstallSignalHandlers() { + // Set the alternate signal stack for the main thread. + // This will cause SetAlternateSignalStack to be called twice, but the stack + // will be actually set only once. + if (flags()->use_sigaltstack) SetAlternateSignalStack(); + MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV); + MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV); +} + +// ---------------------- TSD ---------------- {{{1 + +static pthread_key_t tsd_key; +static bool tsd_key_inited = false; +void AsanTSDInit(void (*destructor)(void *tsd)) { + CHECK(!tsd_key_inited); + tsd_key_inited = true; + CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); +} + +void *AsanTSDGet() { + CHECK(tsd_key_inited); + return pthread_getspecific(tsd_key); +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + pthread_setspecific(tsd_key, tsd); +} + +void PlatformTSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (context->destructor_iterations > 1) { + context->destructor_iterations--; + CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); + return; + } + AsanThread::TSDDtor(tsd); +} +} // namespace __asan + +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/projects/compiler-rt/lib/asan/asan_preinit.cc b/projects/compiler-rt/lib/asan/asan_preinit.cc new file mode 100644 index 00000000..586f551c --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_preinit.cc @@ -0,0 +1,31 @@ +//===-- asan_preinit.cc ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Call __asan_init at the very early stage of process startup. +// On Linux we use .preinit_array section (unless PIC macro is defined). +//===----------------------------------------------------------------------===// +#include "asan_internal.h" + +#if ASAN_USE_PREINIT_ARRAY && !defined(PIC) + // On Linux, we force __asan_init to be called before anyone else + // by placing it into .preinit_array section. + // FIXME: do we have anything like this on Mac? + // The symbol is called __local_asan_preinit, because it's not intended to be + // exported. + __attribute__((section(".preinit_array"), used)) + void (*__local_asan_preinit)(void) = __asan_init; +#elif SANITIZER_WINDOWS && defined(_DLL) + // On Windows, when using dynamic CRT (/MD), we can put a pointer + // to __asan_init into the global list of C initializers. + // See crt0dat.c in the CRT sources for the details. + #pragma section(".CRT$XIB", long, read) // NOLINT + __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init; +#endif diff --git a/projects/compiler-rt/lib/asan/asan_report.cc b/projects/compiler-rt/lib/asan/asan_report.cc new file mode 100644 index 00000000..ed4e433c --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_report.cc @@ -0,0 +1,799 @@ +//===-- asan_report.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file contains error reporting code. +//===----------------------------------------------------------------------===// +#include "asan_flags.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +namespace __asan { + +// -------------------- User-specified callbacks ----------------- {{{1 +static void (*error_report_callback)(const char*); +static char *error_message_buffer = 0; +static uptr error_message_buffer_pos = 0; +static uptr error_message_buffer_size = 0; + +void AppendToErrorMessageBuffer(const char *buffer) { + if (error_message_buffer) { + uptr length = internal_strlen(buffer); + CHECK_GE(error_message_buffer_size, error_message_buffer_pos); + uptr remaining = error_message_buffer_size - error_message_buffer_pos; + internal_strncpy(error_message_buffer + error_message_buffer_pos, + buffer, remaining); + error_message_buffer[error_message_buffer_size - 1] = '\0'; + // FIXME: reallocate the buffer instead of truncating the message. + error_message_buffer_pos += remaining > length ? length : remaining; + } +} + +// ---------------------- Decorator ------------------------------ {{{1 +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *EndWarning() { return Default(); } + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Allocation() { return Magenta(); } + const char *EndAllocation() { return Default(); } + + const char *ShadowByte(u8 byte) { + switch (byte) { + case kAsanHeapLeftRedzoneMagic: + case kAsanHeapRightRedzoneMagic: + return Red(); + case kAsanHeapFreeMagic: + return Magenta(); + case kAsanStackLeftRedzoneMagic: + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + case kAsanStackPartialRedzoneMagic: + return Red(); + case kAsanStackAfterReturnMagic: + return Magenta(); + case kAsanInitializationOrderMagic: + return Cyan(); + case kAsanUserPoisonedMemoryMagic: + return Blue(); + case kAsanStackUseAfterScopeMagic: + return Magenta(); + case kAsanGlobalRedzoneMagic: + return Red(); + case kAsanInternalHeapMagic: + return Yellow(); + default: + return Default(); + } + } + const char *EndShadowByte() { return Default(); } +}; + +// ---------------------- Helper functions ----------------------- {{{1 + +static void PrintShadowByte(const char *before, u8 byte, + const char *after = "\n") { + Decorator d; + Printf("%s%s%x%x%s%s", before, + d.ShadowByte(byte), byte >> 4, byte & 15, d.EndShadowByte(), after); +} + +static void PrintShadowBytes(const char *before, u8 *bytes, + u8 *guilty, uptr n) { + Decorator d; + if (before) + Printf("%s%p:", before, bytes); + for (uptr i = 0; i < n; i++) { + u8 *p = bytes + i; + const char *before = p == guilty ? "[" : + (p - 1 == guilty && i != 0) ? "" : " "; + const char *after = p == guilty ? "]" : ""; + PrintShadowByte(before, *p, after); + } + Printf("\n"); +} + +static void PrintLegend() { + Printf("Shadow byte legend (one shadow byte represents %d " + "application bytes):\n", (int)SHADOW_GRANULARITY); + PrintShadowByte(" Addressable: ", 0); + Printf(" Partially addressable: "); + for (u8 i = 1; i < SHADOW_GRANULARITY; i++) + PrintShadowByte("", i, " "); + Printf("\n"); + PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); + PrintShadowByte(" Heap right redzone: ", kAsanHeapRightRedzoneMagic); + PrintShadowByte(" Freed heap region: ", kAsanHeapFreeMagic); + PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic); + PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic); + PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic); + PrintShadowByte(" Stack partial redzone: ", kAsanStackPartialRedzoneMagic); + PrintShadowByte(" Stack after return: ", kAsanStackAfterReturnMagic); + PrintShadowByte(" Stack use after scope: ", kAsanStackUseAfterScopeMagic); + PrintShadowByte(" Global redzone: ", kAsanGlobalRedzoneMagic); + PrintShadowByte(" Global init order: ", kAsanInitializationOrderMagic); + PrintShadowByte(" Poisoned by user: ", kAsanUserPoisonedMemoryMagic); + PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic); +} + +static void PrintShadowMemoryForAddress(uptr addr) { + if (!AddrIsInMem(addr)) + return; + uptr shadow_addr = MemToShadow(addr); + const uptr n_bytes_per_row = 16; + uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); + Printf("Shadow bytes around the buggy address:\n"); + for (int i = -5; i <= 5; i++) { + const char *prefix = (i == 0) ? "=>" : " "; + PrintShadowBytes(prefix, + (u8*)(aligned_shadow + i * n_bytes_per_row), + (u8*)shadow_addr, n_bytes_per_row); + } + if (flags()->print_legend) + PrintLegend(); +} + +static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, + const char *zone_name) { + if (zone_ptr) { + if (zone_name) { + Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n", + ptr, zone_ptr, zone_name); + } else { + Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", + ptr, zone_ptr); + } + } else { + Printf("malloc_zone_from_ptr(%p) = 0\n", ptr); + } +} + +static void DescribeThread(AsanThread *t) { + if (t) + DescribeThread(t->context()); +} + +// ---------------------- Address Descriptions ------------------- {{{1 + +static bool IsASCII(unsigned char c) { + return /*0x00 <= c &&*/ c <= 0x7F; +} + +static const char *MaybeDemangleGlobalName(const char *name) { + // We can spoil names of globals with C linkage, so use an heuristic + // approach to check if the name should be demangled. + return (name[0] == '_' && name[1] == 'Z') + ? Symbolizer::Get()->Demangle(name) + : name; +} + +// Check if the global is a zero-terminated ASCII string. If so, print it. +static void PrintGlobalNameIfASCII(const __asan_global &g) { + for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { + unsigned char c = *(unsigned char*)p; + if (c == '\0' || !IsASCII(c)) return; + } + if (*(char*)(g.beg + g.size - 1) != '\0') return; + Printf(" '%s' is ascii string '%s'\n", + MaybeDemangleGlobalName(g.name), (char*)g.beg); +} + +bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, + const __asan_global &g) { + static const uptr kMinimalDistanceFromAnotherGlobal = 64; + if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; + if (addr >= g.beg + g.size_with_redzone) return false; + Decorator d; + Printf("%s", d.Location()); + if (addr < g.beg) { + Printf("%p is located %zd bytes to the left", (void*)addr, g.beg - addr); + } else if (addr + size > g.beg + g.size) { + if (addr < g.beg + g.size) + addr = g.beg + g.size; + Printf("%p is located %zd bytes to the right", (void*)addr, + addr - (g.beg + g.size)); + } else { + // Can it happen? + Printf("%p is located %zd bytes inside", (void*)addr, addr - g.beg); + } + Printf(" of global variable '%s' from '%s' (0x%zx) of size %zu\n", + MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size); + Printf("%s", d.EndLocation()); + PrintGlobalNameIfASCII(g); + return true; +} + +bool DescribeAddressIfShadow(uptr addr) { + if (AddrIsInMem(addr)) + return false; + static const char kAddrInShadowReport[] = + "Address %p is located in the %s.\n"; + if (AddrIsInShadowGap(addr)) { + Printf(kAddrInShadowReport, addr, "shadow gap area"); + return true; + } + if (AddrIsInHighShadow(addr)) { + Printf(kAddrInShadowReport, addr, "high shadow area"); + return true; + } + if (AddrIsInLowShadow(addr)) { + Printf(kAddrInShadowReport, addr, "low shadow area"); + return true; + } + CHECK(0 && "Address is not in memory and not in shadow?"); + return false; +} + +// Return " (thread_name) " or an empty string if the name is empty. +const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], + uptr buff_len) { + const char *name = t->name; + if (name[0] == '\0') return ""; + buff[0] = 0; + internal_strncat(buff, " (", 3); + internal_strncat(buff, name, buff_len - 4); + internal_strncat(buff, ")", 2); + return buff; +} + +const char *ThreadNameWithParenthesis(u32 tid, char buff[], + uptr buff_len) { + if (tid == kInvalidTid) return ""; + asanThreadRegistry().CheckLocked(); + AsanThreadContext *t = GetThreadContextByTidLocked(tid); + return ThreadNameWithParenthesis(t, buff, buff_len); +} + +void PrintAccessAndVarIntersection(const char *var_name, + uptr var_beg, uptr var_size, + uptr addr, uptr access_size, + uptr prev_var_end, uptr next_var_beg) { + uptr var_end = var_beg + var_size; + uptr addr_end = addr + access_size; + const char *pos_descr = 0; + // If the variable [var_beg, var_end) is the nearest variable to the + // current memory access, indicate it in the log. + if (addr >= var_beg) { + if (addr_end <= var_end) + pos_descr = "is inside"; // May happen if this is a use-after-return. + else if (addr < var_end) + pos_descr = "partially overflows"; + else if (addr_end <= next_var_beg && + next_var_beg - addr_end >= addr - var_end) + pos_descr = "overflows"; + } else { + if (addr_end > var_beg) + pos_descr = "partially underflows"; + else if (addr >= prev_var_end && + addr - prev_var_end >= var_beg - addr_end) + pos_descr = "underflows"; + } + Printf(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name); + if (pos_descr) { + Decorator d; + // FIXME: we may want to also print the size of the access here, + // but in case of accesses generated by memset it may be confusing. + Printf("%s <== Memory access at offset %zd %s this variable%s\n", + d.Location(), addr, pos_descr, d.EndLocation()); + } else { + Printf("\n"); + } +} + +struct StackVarDescr { + uptr beg; + uptr size; + const char *name_pos; + uptr name_len; +}; + +bool DescribeAddressIfStack(uptr addr, uptr access_size) { + AsanThread *t = FindThreadByStackAddress(addr); + if (!t) return false; + const uptr kBufSize = 4095; + char buf[kBufSize]; + uptr offset = 0; + uptr frame_pc = 0; + char tname[128]; + const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc); + +#ifdef __powerpc64__ + // On PowerPC64, the address of a function actually points to a + // three-doubleword data structure with the first field containing + // the address of the function's code. + frame_pc = *reinterpret_cast(frame_pc); +#endif + + // This string is created by the compiler and has the following form: + // "n alloc_1 alloc_2 ... alloc_n" + // where alloc_i looks like "offset size len ObjectName ". + CHECK(frame_descr); + Decorator d; + Printf("%s", d.Location()); + Printf("Address %p is located in stack of thread T%d%s " + "at offset %zu in frame\n", + addr, t->tid(), + ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)), + offset); + // Now we print the frame where the alloca has happened. + // We print this frame as a stack trace with one element. + // The symbolizer may print more than one frame if inlining was involved. + // The frame numbers may be different than those in the stack trace printed + // previously. That's unfortunate, but I have no better solution, + // especially given that the alloca may be from entirely different place + // (e.g. use-after-scope, or different thread's stack). + StackTrace alloca_stack; + alloca_stack.trace[0] = frame_pc + 16; + alloca_stack.size = 1; + Printf("%s", d.EndLocation()); + PrintStack(&alloca_stack); + // Report the number of stack objects. + char *p; + uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); + CHECK_GT(n_objects, 0); + Printf(" This frame has %zu object(s):\n", n_objects); + + // Report all objects in this frame. + InternalScopedBuffer vars(n_objects); + for (uptr i = 0; i < n_objects; i++) { + uptr beg, size; + uptr len; + beg = (uptr)internal_simple_strtoll(p, &p, 10); + size = (uptr)internal_simple_strtoll(p, &p, 10); + len = (uptr)internal_simple_strtoll(p, &p, 10); + if (beg == 0 || size == 0 || *p != ' ') { + Printf("AddressSanitizer can't parse the stack frame " + "descriptor: |%s|\n", frame_descr); + break; + } + p++; + vars[i].beg = beg; + vars[i].size = size; + vars[i].name_pos = p; + vars[i].name_len = len; + p += len; + } + for (uptr i = 0; i < n_objects; i++) { + buf[0] = 0; + internal_strncat(buf, vars[i].name_pos, + static_cast(Min(kBufSize, vars[i].name_len))); + uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; + uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); + PrintAccessAndVarIntersection(buf, vars[i].beg, vars[i].size, + offset, access_size, + prev_var_end, next_var_beg); + } + Printf("HINT: this may be a false positive if your program uses " + "some custom stack unwind mechanism or swapcontext\n" + " (longjmp and C++ exceptions *are* supported)\n"); + DescribeThread(t); + return true; +} + +static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, + uptr access_size) { + sptr offset; + Decorator d; + Printf("%s", d.Location()); + if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { + Printf("%p is located %zd bytes to the left of", (void*)addr, offset); + } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { + if (offset < 0) { + addr -= offset; + offset = 0; + } + Printf("%p is located %zd bytes to the right of", (void*)addr, offset); + } else if (chunk.AddrIsInside(addr, access_size, &offset)) { + Printf("%p is located %zd bytes inside of", (void*)addr, offset); + } else { + Printf("%p is located somewhere around (this is AddressSanitizer bug!)", + (void*)addr); + } + Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(), + (void*)(chunk.Beg()), (void*)(chunk.End())); + Printf("%s", d.EndLocation()); +} + +void DescribeHeapAddress(uptr addr, uptr access_size) { + AsanChunkView chunk = FindHeapChunkByAddress(addr); + if (!chunk.IsValid()) { + Printf("AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); + return; + } + DescribeAccessToHeapChunk(chunk, addr, access_size); + CHECK(chunk.AllocTid() != kInvalidTid); + asanThreadRegistry().CheckLocked(); + AsanThreadContext *alloc_thread = + GetThreadContextByTidLocked(chunk.AllocTid()); + StackTrace alloc_stack; + chunk.GetAllocStack(&alloc_stack); + char tname[128]; + Decorator d; + AsanThreadContext *free_thread = 0; + if (chunk.FreeTid() != kInvalidTid) { + free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); + Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), + free_thread->tid, + ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), + d.EndAllocation()); + StackTrace free_stack; + chunk.GetFreeStack(&free_stack); + PrintStack(&free_stack); + Printf("%spreviously allocated by thread T%d%s here:%s\n", + d.Allocation(), alloc_thread->tid, + ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), + d.EndAllocation()); + } else { + Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), + alloc_thread->tid, + ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), + d.EndAllocation()); + } + PrintStack(&alloc_stack); + DescribeThread(GetCurrentThread()); + if (free_thread) + DescribeThread(free_thread); + DescribeThread(alloc_thread); +} + +void DescribeAddress(uptr addr, uptr access_size) { + // Check if this is shadow or shadow gap. + if (DescribeAddressIfShadow(addr)) + return; + CHECK(AddrIsInMem(addr)); + if (DescribeAddressIfGlobal(addr, access_size)) + return; + if (DescribeAddressIfStack(addr, access_size)) + return; + // Assume it is a heap address. + DescribeHeapAddress(addr, access_size); +} + +// ------------------- Thread description -------------------- {{{1 + +void DescribeThread(AsanThreadContext *context) { + CHECK(context); + asanThreadRegistry().CheckLocked(); + // No need to announce the main thread. + if (context->tid == 0 || context->announced) { + return; + } + context->announced = true; + char tname[128]; + Printf("Thread T%d%s", context->tid, + ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); + Printf(" created by T%d%s here:\n", + context->parent_tid, + ThreadNameWithParenthesis(context->parent_tid, + tname, sizeof(tname))); + uptr stack_size; + const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size); + PrintStack(stack_trace, stack_size); + // Recursively described parent thread if needed. + if (flags()->print_full_thread_history) { + AsanThreadContext *parent_context = + GetThreadContextByTidLocked(context->parent_tid); + DescribeThread(parent_context); + } +} + +// -------------------- Different kinds of reports ----------------- {{{1 + +// Use ScopedInErrorReport to run common actions just before and +// immediately after printing error report. +class ScopedInErrorReport { + public: + ScopedInErrorReport() { + static atomic_uint32_t num_calls; + static u32 reporting_thread_tid; + if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + // Do not print more than one report, otherwise they will mix up. + // Error reporting functions shouldn't return at this situation, as + // they are defined as no-return. + Report("AddressSanitizer: while reporting a bug found another one." + "Ignoring.\n"); + u32 current_tid = GetCurrentTidOrInvalid(); + if (current_tid != reporting_thread_tid) { + // ASan found two bugs in different threads simultaneously. Sleep + // long enough to make sure that the thread which started to print + // an error report will finish doing it. + SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); + } + // If we're still not dead for some reason, use raw _exit() instead of + // Die() to bypass any additional checks. + internal__exit(flags()->exitcode); + } + ASAN_ON_ERROR(); + // Make sure the registry and sanitizer report mutexes are locked while + // we're printing an error report. + // We can lock them only here to avoid self-deadlock in case of + // recursive reports. + asanThreadRegistry().Lock(); + CommonSanitizerReportMutex.Lock(); + reporting_thread_tid = GetCurrentTidOrInvalid(); + Printf("====================================================" + "=============\n"); + } + // Destructor is NORETURN, as functions that report errors are. + NORETURN ~ScopedInErrorReport() { + // Make sure the current thread is announced. + DescribeThread(GetCurrentThread()); + // Print memory stats. + if (flags()->print_stats) + __asan_print_accumulated_stats(); + if (error_report_callback) { + error_report_callback(error_message_buffer); + } + Report("ABORTING\n"); + Die(); + } +}; + +void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: SEGV on unknown address %p" + " (pc %p sp %p bp %p T%d)\n", + (void*)addr, (void*)pc, (void*)sp, (void*)bp, + GetCurrentTidOrInvalid()); + Printf("%s", d.EndWarning()); + GET_STACK_TRACE_FATAL(pc, bp); + PrintStack(&stack); + Printf("AddressSanitizer can not provide additional info.\n"); + ReportErrorSummary("SEGV", &stack); +} + +void ReportDoubleFree(uptr addr, StackTrace *free_stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); + Report("ERROR: AddressSanitizer: attempting double-free on %p in " + "thread T%d%s:\n", + addr, curr_tid, + ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); + Printf("%s", d.EndWarning()); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); + DescribeHeapAddress(addr, 1); + ReportErrorSummary("double-free", &stack); +} + +void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); + Report("ERROR: AddressSanitizer: attempting free on address " + "which was not malloc()-ed: %p in thread T%d%s\n", addr, + curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); + Printf("%s", d.EndWarning()); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); + DescribeHeapAddress(addr, 1); + ReportErrorSummary("bad-free", &stack); +} + +void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack, + AllocType alloc_type, + AllocType dealloc_type) { + static const char *alloc_names[] = + {"INVALID", "malloc", "operator new", "operator new []"}; + static const char *dealloc_names[] = + {"INVALID", "free", "operator delete", "operator delete []"}; + CHECK_NE(alloc_type, dealloc_type); + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", + alloc_names[alloc_type], dealloc_names[dealloc_type], addr); + Printf("%s", d.EndWarning()); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); + DescribeHeapAddress(addr, 1); + ReportErrorSummary("alloc-dealloc-mismatch", &stack); + Report("HINT: if you don't care about these warnings you may set " + "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); +} + +void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: attempting to call " + "malloc_usable_size() for pointer which is " + "not owned: %p\n", addr); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); + ReportErrorSummary("bad-malloc_usable_size", stack); +} + +void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: attempting to call " + "__asan_get_allocated_size() for pointer which is " + "not owned: %p\n", addr); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeHeapAddress(addr, 1); + ReportErrorSummary("bad-__asan_get_allocated_size", stack); +} + +void ReportStringFunctionMemoryRangesOverlap( + const char *function, const char *offset1, uptr length1, + const char *offset2, uptr length2, StackTrace *stack) { + ScopedInErrorReport in_report; + Decorator d; + char bug_type[100]; + internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: %s: " + "memory ranges [%p,%p) and [%p, %p) overlap\n", \ + bug_type, offset1, offset1 + length1, offset2, offset2 + length2); + Printf("%s", d.EndWarning()); + PrintStack(stack); + DescribeAddress((uptr)offset1, length1); + DescribeAddress((uptr)offset2, length2); + ReportErrorSummary(bug_type, stack); +} + +// ----------------------- Mac-specific reports ----------------- {{{1 + +void WarnMacFreeUnallocated( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + // Just print a warning here. + Printf("free_common(%p) -- attempting to free unallocated memory.\n" + "AddressSanitizer is ignoring this error on Mac OS now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportMacMzReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + ScopedInErrorReport in_report; + Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportMacCfReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + ScopedInErrorReport in_report; + Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +} // namespace __asan + +// --------------------------- Interface --------------------- {{{1 +using namespace __asan; // NOLINT + +void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size) { + ScopedInErrorReport in_report; + + // Determine the error type. + const char *bug_descr = "unknown-crash"; + if (AddrIsInMem(addr)) { + u8 *shadow_addr = (u8*)MemToShadow(addr); + // If we are accessing 16 bytes, look at the second shadow byte. + if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) + shadow_addr++; + // If we are in the partial right redzone, look at the next shadow byte. + if (*shadow_addr > 0 && *shadow_addr < 128) + shadow_addr++; + switch (*shadow_addr) { + case kAsanHeapLeftRedzoneMagic: + case kAsanHeapRightRedzoneMagic: + bug_descr = "heap-buffer-overflow"; + break; + case kAsanHeapFreeMagic: + bug_descr = "heap-use-after-free"; + break; + case kAsanStackLeftRedzoneMagic: + bug_descr = "stack-buffer-underflow"; + break; + case kAsanInitializationOrderMagic: + bug_descr = "initialization-order-fiasco"; + break; + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + case kAsanStackPartialRedzoneMagic: + bug_descr = "stack-buffer-overflow"; + break; + case kAsanStackAfterReturnMagic: + bug_descr = "stack-use-after-return"; + break; + case kAsanUserPoisonedMemoryMagic: + bug_descr = "use-after-poison"; + break; + case kAsanStackUseAfterScopeMagic: + bug_descr = "stack-use-after-scope"; + break; + case kAsanGlobalRedzoneMagic: + bug_descr = "global-buffer-overflow"; + break; + } + } + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: %s on address " + "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n", + bug_descr, (void*)addr, pc, bp, sp); + Printf("%s", d.EndWarning()); + + u32 curr_tid = GetCurrentTidOrInvalid(); + char tname[128]; + Printf("%s%s of size %zu at %p thread T%d%s%s\n", + d.Access(), + access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", + access_size, (void*)addr, curr_tid, + ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)), + d.EndAccess()); + + GET_STACK_TRACE_FATAL(pc, bp); + PrintStack(&stack); + + DescribeAddress(addr, access_size); + ReportErrorSummary(bug_descr, &stack); + PrintShadowMemoryForAddress(addr); +} + +void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { + error_report_callback = callback; + if (callback) { + error_message_buffer_size = 1 << 16; + error_message_buffer = + (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__); + error_message_buffer_pos = 0; + } +} + +void __asan_describe_address(uptr addr) { + DescribeAddress(addr, 1); +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +// Provide default implementation of __asan_on_error that does nothing +// and may be overriden by user. +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE +void __asan_on_error() {} +#endif diff --git a/projects/compiler-rt/lib/asan/asan_report.h b/projects/compiler-rt/lib/asan/asan_report.h new file mode 100644 index 00000000..f55b57bd --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_report.h @@ -0,0 +1,57 @@ +//===-- asan_report.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for error reporting functions. +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_thread.h" + +namespace __asan { + +// The following functions prints address description depending +// on the memory type (shadow/heap/stack/global). +void DescribeHeapAddress(uptr addr, uptr access_size); +bool DescribeAddressIfGlobal(uptr addr, uptr access_size); +bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, + const __asan_global &g); +bool DescribeAddressIfShadow(uptr addr); +bool DescribeAddressIfStack(uptr addr, uptr access_size); +// Determines memory type on its own. +void DescribeAddress(uptr addr, uptr access_size); + +void DescribeThread(AsanThreadContext *context); + +// Different kinds of error reports. +void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); +void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack); +void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack); +void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack, + AllocType alloc_type, + AllocType dealloc_type); +void NORETURN ReportMallocUsableSizeNotOwned(uptr addr, + StackTrace *stack); +void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr, + StackTrace *stack); +void NORETURN ReportStringFunctionMemoryRangesOverlap( + const char *function, const char *offset1, uptr length1, + const char *offset2, uptr length2, StackTrace *stack); + +// Mac-specific errors and warnings. +void WarnMacFreeUnallocated( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); +void NORETURN ReportMacMzReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); +void NORETURN ReportMacCfReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); + +} // namespace __asan diff --git a/projects/compiler-rt/lib/asan/asan_rtl.cc b/projects/compiler-rt/lib/asan/asan_rtl.cc new file mode 100644 index 00000000..11f05954 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_rtl.cc @@ -0,0 +1,572 @@ +//===-- asan_rtl.cc -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Main file of the ASan run-time library. +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_interface_internal.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_symbolizer.h" +#include "lsan/lsan_common.h" + +int __asan_option_detect_stack_use_after_return; // Global interface symbol. + +namespace __asan { + +uptr AsanMappingProfile[kAsanMappingProfileSize]; + +static void AsanDie() { + static atomic_uint32_t num_calls; + if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + // Don't die twice - run a busy loop. + while (1) { } + } + if (flags()->sleep_before_dying) { + Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying); + SleepForSeconds(flags()->sleep_before_dying); + } + if (flags()->unmap_shadow_on_exit) { + if (kMidMemBeg) { + UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg); + UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd); + } else { + UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); + } + } + if (death_callback) + death_callback(); + if (flags()->abort_on_error) + Abort(); + internal__exit(flags()->exitcode); +} + +static void AsanCheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", + file, line, cond, (uptr)v1, (uptr)v2); + // FIXME: check for infinite recursion without a thread-local counter here. + PRINT_CURRENT_STACK(); + Die(); +} + +// -------------------------- Flags ------------------------- {{{1 +static const int kDefaultMallocContextSize = 30; + +Flags asan_flags_dont_use_directly; // use via flags(). + +static const char *MaybeCallAsanDefaultOptions() { + return (&__asan_default_options) ? __asan_default_options() : ""; +} + +static const char *MaybeUseAsanDefaultOptionsCompileDefiniton() { +#ifdef ASAN_DEFAULT_OPTIONS +// Stringize the macro value. +# define ASAN_STRINGIZE(x) #x +# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options) + return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS); +#else + return ""; +#endif +} + +static void ParseFlagsFromString(Flags *f, const char *str) { + ParseCommonFlagsFromString(str); + CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax); + + ParseFlag(str, &f->quarantine_size, "quarantine_size"); + ParseFlag(str, &f->redzone, "redzone"); + CHECK_GE(f->redzone, 16); + CHECK(IsPowerOfTwo(f->redzone)); + + ParseFlag(str, &f->debug, "debug"); + ParseFlag(str, &f->report_globals, "report_globals"); + ParseFlag(str, &f->check_initialization_order, "check_initialization_order"); + + ParseFlag(str, &f->replace_str, "replace_str"); + ParseFlag(str, &f->replace_intrin, "replace_intrin"); + ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free"); + ParseFlag(str, &f->detect_stack_use_after_return, + "detect_stack_use_after_return"); + ParseFlag(str, &f->uar_stack_size_log, "uar_stack_size_log"); + ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size"); + ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte"); + ParseFlag(str, &f->exitcode, "exitcode"); + ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning"); + ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying"); + ParseFlag(str, &f->handle_segv, "handle_segv"); + ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler"); + ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack"); + ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size"); + ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit"); + ParseFlag(str, &f->abort_on_error, "abort_on_error"); + ParseFlag(str, &f->print_stats, "print_stats"); + ParseFlag(str, &f->print_legend, "print_legend"); + ParseFlag(str, &f->atexit, "atexit"); + ParseFlag(str, &f->coverage, "coverage"); + ParseFlag(str, &f->disable_core, "disable_core"); + ParseFlag(str, &f->allow_reexec, "allow_reexec"); + ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); + ParseFlag(str, &f->poison_heap, "poison_heap"); + ParseFlag(str, &f->poison_partial, "poison_partial"); + ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); + ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); + ParseFlag(str, &f->strict_init_order, "strict_init_order"); +} + +void InitializeFlags(Flags *f, const char *env) { + CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); + cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); + cf->malloc_context_size = kDefaultMallocContextSize; + + internal_memset(f, 0, sizeof(*f)); + f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; + f->redzone = 16; + f->debug = false; + f->report_globals = 1; + f->check_initialization_order = false; + f->replace_str = true; + f->replace_intrin = true; + f->mac_ignore_invalid_free = false; + f->detect_stack_use_after_return = false; // Also needs the compiler flag. + f->uar_stack_size_log = 0; + f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K. + f->malloc_fill_byte = 0xbe; + f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; + f->allow_user_poisoning = true; + f->sleep_before_dying = 0; + f->handle_segv = ASAN_NEEDS_SEGV; + f->allow_user_segv_handler = false; + f->use_sigaltstack = false; + f->check_malloc_usable_size = true; + f->unmap_shadow_on_exit = false; + f->abort_on_error = false; + f->print_stats = false; + f->print_legend = true; + f->atexit = false; + f->coverage = false; + f->disable_core = (SANITIZER_WORDSIZE == 64); + f->allow_reexec = true; + f->print_full_thread_history = true; + f->poison_heap = true; + f->poison_partial = true; + // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. + // TODO(glider,timurrrr): Fix known issues and enable this back. + f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); + f->strict_memcmp = true; + f->strict_init_order = false; + + // Override from compile definition. + ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton()); + + // Override from user-specified string. + ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); + if (common_flags()->verbosity) { + Report("Using the defaults from __asan_default_options: %s\n", + MaybeCallAsanDefaultOptions()); + } + + // Override from command line. + ParseFlagsFromString(f, env); + +#if !CAN_SANITIZE_LEAKS + if (cf->detect_leaks) { + Report("%s: detect_leaks is not supported on this platform.\n", + SanitizerToolName); + cf->detect_leaks = false; + } +#endif + + // Make "strict_init_order" imply "check_initialization_order". + // TODO(samsonov): Use a single runtime flag for an init-order checker. + if (f->strict_init_order) { + f->check_initialization_order = true; + } +} + +// -------------------------- Globals --------------------- {{{1 +int asan_inited; +bool asan_init_is_running; +void (*death_callback)(void); + +#if !ASAN_FIXED_MAPPING +uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; +#endif + +// -------------------------- Misc ---------------- {{{1 +void ShowStatsAndAbort() { + __asan_print_accumulated_stats(); + Die(); +} + +// ---------------------- mmap -------------------- {{{1 +// Reserve memory range [beg, end]. +static void ReserveShadowMemoryRange(uptr beg, uptr end) { + CHECK_EQ((beg % GetPageSizeCached()), 0); + CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); + uptr size = end - beg + 1; + void *res = MmapFixedNoReserve(beg, size); + if (res != (void*)beg) { + Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " + "Perhaps you're using ulimit -v\n", size); + Abort(); + } +} + +// --------------- LowLevelAllocateCallbac ---------- {{{1 +static void OnLowLevelAllocate(uptr ptr, uptr size) { + PoisonShadow(ptr, size, kAsanInternalHeapMagic); +} + +// -------------------------- Run-time entry ------------------- {{{1 +// exported functions +#define ASAN_REPORT_ERROR(type, is_write, size) \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## size(uptr addr); \ +void __asan_report_ ## type ## size(uptr addr) { \ + GET_CALLER_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, addr, is_write, size); \ +} + +ASAN_REPORT_ERROR(load, false, 1) +ASAN_REPORT_ERROR(load, false, 2) +ASAN_REPORT_ERROR(load, false, 4) +ASAN_REPORT_ERROR(load, false, 8) +ASAN_REPORT_ERROR(load, false, 16) +ASAN_REPORT_ERROR(store, true, 1) +ASAN_REPORT_ERROR(store, true, 2) +ASAN_REPORT_ERROR(store, true, 4) +ASAN_REPORT_ERROR(store, true, 8) +ASAN_REPORT_ERROR(store, true, 16) + +#define ASAN_REPORT_ERROR_N(type, is_write) \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## _n(uptr addr, uptr size); \ +void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ + GET_CALLER_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, addr, is_write, size); \ +} + +ASAN_REPORT_ERROR_N(load, false) +ASAN_REPORT_ERROR_N(store, true) + +// Force the linker to keep the symbols for various ASan interface functions. +// We want to keep those in the executable in order to let the instrumented +// dynamic libraries access the symbol even if it is not used by the executable +// itself. This should help if the build system is removing dead code at link +// time. +static NOINLINE void force_interface_symbols() { + volatile int fake_condition = 0; // prevent dead condition elimination. + // __asan_report_* functions are noreturn, so we need a switch to prevent + // the compiler from removing any of them. + switch (fake_condition) { + case 1: __asan_report_load1(0); break; + case 2: __asan_report_load2(0); break; + case 3: __asan_report_load4(0); break; + case 4: __asan_report_load8(0); break; + case 5: __asan_report_load16(0); break; + case 6: __asan_report_store1(0); break; + case 7: __asan_report_store2(0); break; + case 8: __asan_report_store4(0); break; + case 9: __asan_report_store8(0); break; + case 10: __asan_report_store16(0); break; + case 12: __asan_register_globals(0, 0); break; + case 13: __asan_unregister_globals(0, 0); break; + case 14: __asan_set_death_callback(0); break; + case 15: __asan_set_error_report_callback(0); break; + case 16: __asan_handle_no_return(); break; + case 17: __asan_address_is_poisoned(0); break; + case 18: __asan_get_allocated_size(0); break; + case 19: __asan_get_current_allocated_bytes(); break; + case 20: __asan_get_estimated_allocated_size(0); break; + case 21: __asan_get_free_bytes(); break; + case 22: __asan_get_heap_size(); break; + case 23: __asan_get_ownership(0); break; + case 24: __asan_get_unmapped_bytes(); break; + case 25: __asan_poison_memory_region(0, 0); break; + case 26: __asan_unpoison_memory_region(0, 0); break; + case 27: __asan_set_error_exit_code(0); break; + case 30: __asan_before_dynamic_init(0); break; + case 31: __asan_after_dynamic_init(); break; + case 32: __asan_poison_stack_memory(0, 0); break; + case 33: __asan_unpoison_stack_memory(0, 0); break; + case 34: __asan_region_is_poisoned(0, 0); break; + case 35: __asan_describe_address(0); break; + } +} + +static void asan_atexit() { + Printf("AddressSanitizer exit stats:\n"); + __asan_print_accumulated_stats(); + // Print AsanMappingProfile. + for (uptr i = 0; i < kAsanMappingProfileSize; i++) { + if (AsanMappingProfile[i] == 0) continue; + Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]); + } +} + +static void InitializeHighMemEnd() { +#if !ASAN_FIXED_MAPPING + kHighMemEnd = GetMaxVirtualAddress(); + // Increase kHighMemEnd to make sure it's properly + // aligned together with kHighMemBeg: + kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1; +#endif // !ASAN_FIXED_MAPPING + CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0); +} + +static void ProtectGap(uptr a, uptr size) { + CHECK_EQ(a, (uptr)Mprotect(a, size)); +} + +static void PrintAddressSpaceLayout() { + Printf("|| `[%p, %p]` || HighMem ||\n", + (void*)kHighMemBeg, (void*)kHighMemEnd); + Printf("|| `[%p, %p]` || HighShadow ||\n", + (void*)kHighShadowBeg, (void*)kHighShadowEnd); + if (kMidMemBeg) { + Printf("|| `[%p, %p]` || ShadowGap3 ||\n", + (void*)kShadowGap3Beg, (void*)kShadowGap3End); + Printf("|| `[%p, %p]` || MidMem ||\n", + (void*)kMidMemBeg, (void*)kMidMemEnd); + Printf("|| `[%p, %p]` || ShadowGap2 ||\n", + (void*)kShadowGap2Beg, (void*)kShadowGap2End); + Printf("|| `[%p, %p]` || MidShadow ||\n", + (void*)kMidShadowBeg, (void*)kMidShadowEnd); + } + Printf("|| `[%p, %p]` || ShadowGap ||\n", + (void*)kShadowGapBeg, (void*)kShadowGapEnd); + if (kLowShadowBeg) { + Printf("|| `[%p, %p]` || LowShadow ||\n", + (void*)kLowShadowBeg, (void*)kLowShadowEnd); + Printf("|| `[%p, %p]` || LowMem ||\n", + (void*)kLowMemBeg, (void*)kLowMemEnd); + } + Printf("MemToShadow(shadow): %p %p %p %p", + (void*)MEM_TO_SHADOW(kLowShadowBeg), + (void*)MEM_TO_SHADOW(kLowShadowEnd), + (void*)MEM_TO_SHADOW(kHighShadowBeg), + (void*)MEM_TO_SHADOW(kHighShadowEnd)); + if (kMidMemBeg) { + Printf(" %p %p", + (void*)MEM_TO_SHADOW(kMidShadowBeg), + (void*)MEM_TO_SHADOW(kMidShadowEnd)); + } + Printf("\n"); + Printf("red_zone=%zu\n", (uptr)flags()->redzone); + Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20); + Printf("malloc_context_size=%zu\n", + (uptr)common_flags()->malloc_context_size); + + Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE); + Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY); + Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET); + CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); + if (kMidMemBeg) + CHECK(kMidShadowBeg > kLowShadowEnd && + kMidMemBeg > kMidShadowEnd && + kHighShadowBeg > kMidMemEnd); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char* __asan_default_options() { return ""; } +} // extern "C" +#endif + +int NOINLINE __asan_set_error_exit_code(int exit_code) { + int old = flags()->exitcode; + flags()->exitcode = exit_code; + return old; +} + +void NOINLINE __asan_handle_no_return() { + int local_stack; + AsanThread *curr_thread = GetCurrentThread(); + CHECK(curr_thread); + uptr PageSize = GetPageSizeCached(); + uptr top = curr_thread->stack_top(); + uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); + static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M + if (top - bottom > kMaxExpectedCleanupSize) { + static bool reported_warning = false; + if (reported_warning) + return; + reported_warning = true; + Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " + "stack top: %p; bottom %p; size: %p (%zd)\n" + "False positive error reports may follow\n" + "For details see " + "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", + top, bottom, top - bottom, top - bottom); + return; + } + PoisonShadow(bottom, top - bottom, 0); + if (curr_thread->has_fake_stack()) + curr_thread->fake_stack()->HandleNoReturn(); +} + +void NOINLINE __asan_set_death_callback(void (*callback)(void)) { + death_callback = callback; +} + +void __asan_init() { + if (asan_inited) return; + SanitizerToolName = "AddressSanitizer"; + CHECK(!asan_init_is_running && "ASan init calls itself!"); + asan_init_is_running = true; + InitializeHighMemEnd(); + + // Make sure we are not statically linked. + AsanDoesNotSupportStaticLinkage(); + + // Install tool-specific callbacks in sanitizer_common. + SetDieCallback(AsanDie); + SetCheckFailedCallback(AsanCheckFailed); + SetPrintfAndReportCallback(AppendToErrorMessageBuffer); + + // Initialize flags. This must be done early, because most of the + // initialization steps look at flags(). + const char *options = GetEnv("ASAN_OPTIONS"); + InitializeFlags(flags(), options); + __sanitizer_set_report_path(common_flags()->log_path); + __asan_option_detect_stack_use_after_return = + flags()->detect_stack_use_after_return; + + if (common_flags()->verbosity && options) { + Report("Parsed ASAN_OPTIONS: %s\n", options); + } + + // Re-exec ourselves if we need to set additional env or command line args. + MaybeReexec(); + + // Setup internal allocator callback. + SetLowLevelAllocateCallback(OnLowLevelAllocate); + + InitializeAsanInterceptors(); + + ReplaceSystemMalloc(); + ReplaceOperatorsNewAndDelete(); + + uptr shadow_start = kLowShadowBeg; + if (kLowShadowBeg) + shadow_start -= GetMmapGranularity(); + bool full_shadow_is_available = + MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); + +#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING + if (!full_shadow_is_available) { + kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; + kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; + } +#endif + + if (common_flags()->verbosity) + PrintAddressSpaceLayout(); + + if (flags()->disable_core) { + DisableCoreDumper(); + } + + if (full_shadow_is_available) { + // mmap the low shadow plus at least one page at the left. + if (kLowShadowBeg) + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); + // protect the gap. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + } else if (kMidMemBeg && + MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && + MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { + CHECK(kLowShadowBeg != kLowShadowEnd); + // mmap the low shadow plus at least one page at the left. + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); + // mmap the mid shadow. + ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd); + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); + // protect the gaps. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); + ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1); + } else { + Report("Shadow memory range interleaves with an existing memory mapping. " + "ASan cannot proceed correctly. ABORTING.\n"); + DumpProcessMap(); + Die(); + } + + AsanTSDInit(PlatformTSDDtor); + InstallSignalHandlers(); + + // Allocator should be initialized before starting external symbolizer, as + // fork() on Mac locks the allocator. + InitializeAllocator(); + + // Start symbolizer process if necessary. + if (common_flags()->symbolize) { + Symbolizer::Init(common_flags()->external_symbolizer_path); + } else { + Symbolizer::Disable(); + } + + // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited + // should be set to 1 prior to initializing the threads. + asan_inited = 1; + asan_init_is_running = false; + + if (flags()->atexit) + Atexit(asan_atexit); + + if (flags()->coverage) + Atexit(__sanitizer_cov_dump); + + // interceptors + InitTlsSize(); + + // Create main thread. + AsanThread *main_thread = AsanThread::Create(0, 0); + CreateThreadContextArgs create_main_args = { main_thread, 0 }; + u32 main_tid = asanThreadRegistry().CreateThread( + 0, true, 0, &create_main_args); + CHECK_EQ(0, main_tid); + SetCurrentThread(main_thread); + main_thread->ThreadStart(internal_getpid()); + force_interface_symbols(); // no-op. + +#if CAN_SANITIZE_LEAKS + __lsan::InitCommonLsan(); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { + Atexit(__lsan::DoLeakCheck); + } +#endif // CAN_SANITIZE_LEAKS + + if (common_flags()->verbosity) { + Report("AddressSanitizer Init done\n"); + } +} diff --git a/projects/compiler-rt/lib/asan/asan_stack.cc b/projects/compiler-rt/lib/asan/asan_stack.cc new file mode 100644 index 00000000..0bc5a5f2 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_stack.cc @@ -0,0 +1,47 @@ +//===-- asan_stack.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Code for ASan stack trace. +//===----------------------------------------------------------------------===// +#include "asan_internal.h" +#include "asan_flags.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_flags.h" + +namespace __asan { + +static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer, + int out_size) { + return (&__asan_symbolize) ? __asan_symbolize(pc, out_buffer, out_size) + : false; +} + +void PrintStack(const uptr *trace, uptr size) { + StackTrace::PrintStack(trace, size, MaybeCallAsanSymbolize); +} + +void PrintStack(StackTrace *stack) { + PrintStack(stack->trace, stack->size); +} + +} // namespace __asan + +// ------------------ Interface -------------- {{{1 + +// Provide default implementation of __asan_symbolize that does nothing +// and may be overriden by user if he wants to use his own symbolization. +// ASan on Windows has its own implementation of this. +#if !SANITIZER_WINDOWS && !SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE +bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { + return false; +} +#endif diff --git a/projects/compiler-rt/lib/asan/asan_stack.h b/projects/compiler-rt/lib/asan/asan_stack.h new file mode 100644 index 00000000..7f5fd661 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_stack.h @@ -0,0 +1,82 @@ +//===-- asan_stack.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_stack.cc. +//===----------------------------------------------------------------------===// +#ifndef ASAN_STACK_H +#define ASAN_STACK_H + +#include "asan_flags.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __asan { + +void PrintStack(StackTrace *stack); +void PrintStack(const uptr *trace, uptr size); + +} // namespace __asan + +// Get the stack trace with the given pc and bp. +// The pc will be in the position 0 of the resulting stack trace. +// The bp may refer to the current frame or to the caller's frame. +#if SANITIZER_WINDOWS +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + stack.Unwind(max_s, pc, bp, 0, 0, fast) +#else +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + { \ + AsanThread *t; \ + stack.size = 0; \ + if (asan_inited && (t = GetCurrentThread()) && !t->isUnwinding()) { \ + uptr stack_top = t->stack_top(); \ + uptr stack_bottom = t->stack_bottom(); \ + ScopedUnwinding unwind_scope(t); \ + stack.Unwind(max_s, pc, bp, stack_top, stack_bottom, fast); \ + } \ + } +#endif // SANITIZER_WINDOWS + +// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors +// as early as possible (in functions exposed to the user), as we generally +// don't want stack trace to contain functions from ASan internals. + +#define GET_STACK_TRACE(max_size, fast) \ + GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \ + StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), fast) + +#define GET_STACK_TRACE_FATAL(pc, bp) \ + GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \ + common_flags()->fast_unwind_on_fatal) + +#define GET_STACK_TRACE_FATAL_HERE \ + GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) + +#define GET_STACK_TRACE_THREAD \ + GET_STACK_TRACE(kStackTraceMax, true) + +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(common_flags()->malloc_context_size, \ + common_flags()->fast_unwind_on_malloc) + +#define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC + +#define PRINT_CURRENT_STACK() \ + { \ + GET_STACK_TRACE(kStackTraceMax, \ + common_flags()->fast_unwind_on_fatal); \ + PrintStack(&stack); \ + } + +#endif // ASAN_STACK_H diff --git a/projects/compiler-rt/lib/asan/asan_stats.cc b/projects/compiler-rt/lib/asan/asan_stats.cc new file mode 100644 index 00000000..73dc3c5a --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_stats.cc @@ -0,0 +1,178 @@ +//===-- asan_stats.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Code related to statistics collected by AddressSanitizer. +//===----------------------------------------------------------------------===// +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stats.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __asan { + +AsanStats::AsanStats() { + Clear(); +} + +void AsanStats::Clear() { + CHECK(REAL(memset)); + REAL(memset)(this, 0, sizeof(AsanStats)); +} + +static void PrintMallocStatsArray(const char *prefix, + uptr (&array)[kNumberOfSizeClasses]) { + Printf("%s", prefix); + for (uptr i = 0; i < kNumberOfSizeClasses; i++) { + if (!array[i]) continue; + Printf("%zu:%zu; ", i, array[i]); + } + Printf("\n"); +} + +void AsanStats::Print() { + Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", + malloced>>20, malloced_redzones>>20, mallocs); + Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); + Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); + Printf("Stats: %zuM really freed by %zu calls\n", + really_freed>>20, real_frees); + Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n", + (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20, + mmaps, munmaps); + + PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size); + PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); + PrintMallocStatsArray(" frees by size class: ", freed_by_size); + PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size); + Printf("Stats: malloc large: %zu small slow: %zu\n", + malloc_large, malloc_small_slow); +} + +void AsanStats::MergeFrom(const AsanStats *stats) { + uptr *dst_ptr = reinterpret_cast(this); + const uptr *src_ptr = reinterpret_cast(stats); + uptr num_fields = sizeof(*this) / sizeof(uptr); + for (uptr i = 0; i < num_fields; i++) + dst_ptr[i] += src_ptr[i]; +} + +static BlockingMutex print_lock(LINKER_INITIALIZED); + +static AsanStats unknown_thread_stats(LINKER_INITIALIZED); +static AsanStats dead_threads_stats(LINKER_INITIALIZED); +static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED); +// Required for malloc_zone_statistics() on OS X. This can't be stored in +// per-thread AsanStats. +static uptr max_malloced_memory; + +static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { + AsanStats *accumulated_stats = reinterpret_cast(arg); + AsanThreadContext *tctx = static_cast(tctx_base); + if (AsanThread *t = tctx->thread) + accumulated_stats->MergeFrom(&t->stats()); +} + +static void GetAccumulatedStats(AsanStats *stats) { + stats->Clear(); + { + ThreadRegistryLock l(&asanThreadRegistry()); + asanThreadRegistry() + .RunCallbackForEachThreadLocked(MergeThreadStats, stats); + } + stats->MergeFrom(&unknown_thread_stats); + { + BlockingMutexLock lock(&dead_threads_stats_lock); + stats->MergeFrom(&dead_threads_stats); + } + // This is not very accurate: we may miss allocation peaks that happen + // between two updates of accumulated_stats_. For more accurate bookkeeping + // the maximum should be updated on every malloc(), which is unacceptable. + if (max_malloced_memory < stats->malloced) { + max_malloced_memory = stats->malloced; + } +} + +void FlushToDeadThreadStats(AsanStats *stats) { + BlockingMutexLock lock(&dead_threads_stats_lock); + dead_threads_stats.MergeFrom(stats); + stats->Clear(); +} + +void FillMallocStatistics(AsanMallocStats *malloc_stats) { + AsanStats stats; + GetAccumulatedStats(&stats); + malloc_stats->blocks_in_use = stats.mallocs; + malloc_stats->size_in_use = stats.malloced; + malloc_stats->max_size_in_use = max_malloced_memory; + malloc_stats->size_allocated = stats.mmaped; +} + +AsanStats &GetCurrentThreadStats() { + AsanThread *t = GetCurrentThread(); + return (t) ? t->stats() : unknown_thread_stats; +} + +static void PrintAccumulatedStats() { + AsanStats stats; + GetAccumulatedStats(&stats); + // Use lock to keep reports from mixing up. + BlockingMutexLock lock(&print_lock); + stats.Print(); + StackDepotStats *stack_depot_stats = StackDepotGetStats(); + Printf("Stats: StackDepot: %zd ids; %zdM mapped\n", + stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20); + PrintInternalAllocatorStats(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +uptr __asan_get_current_allocated_bytes() { + AsanStats stats; + GetAccumulatedStats(&stats); + uptr malloced = stats.malloced; + uptr freed = stats.freed; + // Return sane value if malloced < freed due to racy + // way we update accumulated stats. + return (malloced > freed) ? malloced - freed : 1; +} + +uptr __asan_get_heap_size() { + AsanStats stats; + GetAccumulatedStats(&stats); + return stats.mmaped - stats.munmaped; +} + +uptr __asan_get_free_bytes() { + AsanStats stats; + GetAccumulatedStats(&stats); + uptr total_free = stats.mmaped + - stats.munmaped + + stats.really_freed + + stats.really_freed_redzones; + uptr total_used = stats.malloced + + stats.malloced_redzones; + // Return sane value if total_free < total_used due to racy + // way we update accumulated stats. + return (total_free > total_used) ? total_free - total_used : 1; +} + +uptr __asan_get_unmapped_bytes() { + return 0; +} + +void __asan_print_accumulated_stats() { + PrintAccumulatedStats(); +} diff --git a/projects/compiler-rt/lib/asan/asan_stats.h b/projects/compiler-rt/lib/asan/asan_stats.h new file mode 100644 index 00000000..e3030e88 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_stats.h @@ -0,0 +1,78 @@ +//===-- asan_stats.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for statistics. +//===----------------------------------------------------------------------===// +#ifndef ASAN_STATS_H +#define ASAN_STATS_H + +#include "asan_allocator.h" +#include "asan_internal.h" + +namespace __asan { + +// AsanStats struct is NOT thread-safe. +// Each AsanThread has its own AsanStats, which are sometimes flushed +// to the accumulated AsanStats. +struct AsanStats { + // AsanStats must be a struct consisting of uptr fields only. + // When merging two AsanStats structs, we treat them as arrays of uptr. + uptr mallocs; + uptr malloced; + uptr malloced_redzones; + uptr frees; + uptr freed; + uptr real_frees; + uptr really_freed; + uptr really_freed_redzones; + uptr reallocs; + uptr realloced; + uptr mmaps; + uptr mmaped; + uptr munmaps; + uptr munmaped; + uptr mmaped_by_size[kNumberOfSizeClasses]; + uptr malloced_by_size[kNumberOfSizeClasses]; + uptr freed_by_size[kNumberOfSizeClasses]; + uptr really_freed_by_size[kNumberOfSizeClasses]; + + uptr malloc_large; + uptr malloc_small_slow; + + // Ctor for global AsanStats (accumulated stats and main thread stats). + explicit AsanStats(LinkerInitialized) { } + // Default ctor for thread-local stats. + AsanStats(); + + void Print(); // Prints formatted stats to stderr. + void Clear(); + void MergeFrom(const AsanStats *stats); +}; + +// Returns stats for GetCurrentThread(), or stats for fake "unknown thread" +// if GetCurrentThread() returns 0. +AsanStats &GetCurrentThreadStats(); +// Flushes a given stats into accumulated stats of dead threads. +void FlushToDeadThreadStats(AsanStats *stats); + +// A cross-platform equivalent of malloc_statistics_t on Mac OS. +struct AsanMallocStats { + uptr blocks_in_use; + uptr size_in_use; + uptr max_size_in_use; + uptr size_allocated; +}; + +void FillMallocStatistics(AsanMallocStats *malloc_stats); + +} // namespace __asan + +#endif // ASAN_STATS_H diff --git a/projects/compiler-rt/lib/asan/asan_thread.cc b/projects/compiler-rt/lib/asan/asan_thread.cc new file mode 100644 index 00000000..328ac2fc --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_thread.cc @@ -0,0 +1,344 @@ +//===-- asan_thread.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Thread-related code. +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_poisoning.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "asan_mapping.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "lsan/lsan_common.h" + +namespace __asan { + +// AsanThreadContext implementation. + +void AsanThreadContext::OnCreated(void *arg) { + CreateThreadContextArgs *args = static_cast(arg); + if (args->stack) + stack_id = StackDepotPut(args->stack->trace, args->stack->size); + thread = args->thread; + thread->set_context(this); +} + +void AsanThreadContext::OnFinished() { + // Drop the link to the AsanThread object. + thread = 0; +} + +// MIPS requires aligned address +static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; +static ThreadRegistry *asan_thread_registry; + +static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_thread_context; + +static ThreadContextBase *GetAsanThreadContext(u32 tid) { + BlockingMutexLock lock(&mu_for_thread_context); + return new(allocator_for_thread_context) AsanThreadContext(tid); +} + +ThreadRegistry &asanThreadRegistry() { + static bool initialized; + // Don't worry about thread_safety - this should be called when there is + // a single thread. + if (!initialized) { + // Never reuse ASan threads: we store pointer to AsanThreadContext + // in TSD and can't reliably tell when no more TSD destructors will + // be called. It would be wrong to reuse AsanThreadContext for another + // thread before all TSD destructors will be called for it. + asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry( + GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads); + initialized = true; + } + return *asan_thread_registry; +} + +AsanThreadContext *GetThreadContextByTidLocked(u32 tid) { + return static_cast( + asanThreadRegistry().GetThreadLocked(tid)); +} + +// AsanThread implementation. + +AsanThread *AsanThread::Create(thread_callback_t start_routine, + void *arg) { + uptr PageSize = GetPageSizeCached(); + uptr size = RoundUpTo(sizeof(AsanThread), PageSize); + AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); + thread->start_routine_ = start_routine; + thread->arg_ = arg; + thread->context_ = 0; + + return thread; +} + +void AsanThread::TSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (common_flags()->verbosity >= 1) + Report("T%d TSDDtor\n", context->tid); + if (context->thread) + context->thread->Destroy(); +} + +void AsanThread::Destroy() { + if (common_flags()->verbosity >= 1) { + Report("T%d exited\n", tid()); + } + + malloc_storage().CommitBack(); + if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); + asanThreadRegistry().FinishThread(tid()); + FlushToDeadThreadStats(&stats_); + // We also clear the shadow on thread destruction because + // some code may still be executing in later TSD destructors + // and we don't want it to have any poisoned stack. + ClearShadowForThreadStackAndTLS(); + DeleteFakeStack(); + uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); + UnmapOrDie(this, size); +} + +// We want to create the FakeStack lazyly on the first use, but not eralier +// than the stack size is known and the procedure has to be async-signal safe. +FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { + uptr stack_size = this->stack_size(); + if (stack_size == 0) // stack_size is not yet available, don't use FakeStack. + return 0; + uptr old_val = 0; + // fake_stack_ has 3 states: + // 0 -- not initialized + // 1 -- being initialized + // ptr -- initialized + // This CAS checks if the state was 0 and if so changes it to state 1, + // if that was successfull, it initilizes the pointer. + if (atomic_compare_exchange_strong( + reinterpret_cast(&fake_stack_), &old_val, 1UL, + memory_order_relaxed)) { + uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size)); + if (flags()->uar_stack_size_log) + stack_size_log = static_cast(flags()->uar_stack_size_log); + fake_stack_ = FakeStack::Create(stack_size_log); + SetTLSFakeStack(fake_stack_); + return fake_stack_; + } + return 0; +} + +void AsanThread::Init() { + SetThreadStackAndTls(); + CHECK(AddrIsInMem(stack_bottom_)); + CHECK(AddrIsInMem(stack_top_ - 1)); + ClearShadowForThreadStackAndTLS(); + if (common_flags()->verbosity >= 1) { + int local = 0; + Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n", + tid(), (void*)stack_bottom_, (void*)stack_top_, + stack_top_ - stack_bottom_, &local); + } + fake_stack_ = 0; // Will be initialized lazily if needed. + AsanPlatformThreadInit(); +} + +thread_return_t AsanThread::ThreadStart(uptr os_id) { + Init(); + asanThreadRegistry().StartThread(tid(), os_id, 0); + if (flags()->use_sigaltstack) SetAlternateSignalStack(); + + if (!start_routine_) { + // start_routine_ == 0 if we're on the main thread or on one of the + // OS X libdispatch worker threads. But nobody is supposed to call + // ThreadStart() for the worker threads. + CHECK_EQ(tid(), 0); + return 0; + } + + thread_return_t res = start_routine_(arg_); + + // On POSIX systems we defer this to the TSD destructor. LSan will consider + // the thread's memory as non-live from the moment we call Destroy(), even + // though that memory might contain pointers to heap objects which will be + // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before + // the TSD destructors have run might cause false positives in LSan. + if (!SANITIZER_POSIX) + this->Destroy(); + + return res; +} + +void AsanThread::SetThreadStackAndTls() { + uptr tls_size = 0; + GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_, + &tls_size); + stack_top_ = stack_bottom_ + stack_size_; + tls_end_ = tls_begin_ + tls_size; + + int local; + CHECK(AddrIsInStack((uptr)&local)); +} + +void AsanThread::ClearShadowForThreadStackAndTLS() { + PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); + if (tls_begin_ != tls_end_) + PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0); +} + +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset, + uptr *frame_pc) { + uptr bottom = 0; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); + CHECK(bottom); + *offset = addr - bottom; + *frame_pc = ((uptr*)bottom)[2]; + return (const char *)((uptr*)bottom)[1]; + } + uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr != kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + } + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr == kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + } + + if (shadow_ptr < shadow_bottom) { + *offset = 0; + return "UNKNOWN"; + } + + uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); + CHECK(ptr[0] == kCurrentStackFrameMagic); + *offset = addr - (uptr)ptr; + *frame_pc = ptr[2]; + return (const char*)ptr[1]; +} + +static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, + void *addr) { + AsanThreadContext *tctx = static_cast(tctx_base); + AsanThread *t = tctx->thread; + if (!t) return false; + if (t->AddrIsInStack((uptr)addr)) return true; + if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr)) + return true; + return false; +} + +AsanThread *GetCurrentThread() { + AsanThreadContext *context = + reinterpret_cast(AsanTSDGet()); + if (!context) { + if (SANITIZER_ANDROID) { + // On Android, libc constructor is called _after_ asan_init, and cleans up + // TSD. Try to figure out if this is still the main thread by the stack + // address. We are not entirely sure that we have correct main thread + // limits, so only do this magic on Android, and only if the found thread + // is the main thread. + AsanThreadContext *tctx = GetThreadContextByTidLocked(0); + if (ThreadStackContainsAddress(tctx, &context)) { + SetCurrentThread(tctx->thread); + return tctx->thread; + } + } + return 0; + } + return context->thread; +} + +void SetCurrentThread(AsanThread *t) { + CHECK(t->context()); + if (common_flags()->verbosity >= 2) { + Report("SetCurrentThread: %p for thread %p\n", + t->context(), (void*)GetThreadSelf()); + } + // Make sure we do not reset the current AsanThread. + CHECK_EQ(0, AsanTSDGet()); + AsanTSDSet(t->context()); + CHECK_EQ(t->context(), AsanTSDGet()); +} + +u32 GetCurrentTidOrInvalid() { + AsanThread *t = GetCurrentThread(); + return t ? t->tid() : kInvalidTid; +} + +AsanThread *FindThreadByStackAddress(uptr addr) { + asanThreadRegistry().CheckLocked(); + AsanThreadContext *tctx = static_cast( + asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, + (void *)addr)); + return tctx ? tctx->thread : 0; +} + +void EnsureMainThreadIDIsCorrect() { + AsanThreadContext *context = + reinterpret_cast(AsanTSDGet()); + if (context && (context->tid == 0)) + context->os_id = GetTid(); +} + +__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) { + __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( + __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); + if (!context) return 0; + return context->thread; +} +} // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end) { + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); + if (!t) return false; + *stack_begin = t->stack_bottom(); + *stack_end = t->stack_top(); + *tls_begin = t->tls_begin(); + *tls_end = t->tls_end(); + // ASan doesn't keep allocator caches in TLS, so these are unused. + *cache_begin = 0; + *cache_end = 0; + return true; +} + +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg) { + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); + if (t && t->has_fake_stack()) + t->fake_stack()->ForEachFakeFrame(callback, arg); +} + +void LockThreadRegistry() { + __asan::asanThreadRegistry().Lock(); +} + +void UnlockThreadRegistry() { + __asan::asanThreadRegistry().Unlock(); +} + +void EnsureMainThreadIDIsCorrect() { + __asan::EnsureMainThreadIDIsCorrect(); +} +} // namespace __lsan diff --git a/projects/compiler-rt/lib/asan/asan_thread.h b/projects/compiler-rt/lib/asan/asan_thread.h new file mode 100644 index 00000000..11771ecd --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_thread.h @@ -0,0 +1,166 @@ +//===-- asan_thread.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_thread.cc. +//===----------------------------------------------------------------------===// +#ifndef ASAN_THREAD_H +#define ASAN_THREAD_H + +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_fake_stack.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_thread_registry.h" + +namespace __asan { + +const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. +const u32 kMaxNumberOfThreads = (1 << 22); // 4M + +class AsanThread; + +// These objects are created for every thread and are never deleted, +// so we can find them by tid even if the thread is long dead. +class AsanThreadContext : public ThreadContextBase { + public: + explicit AsanThreadContext(int tid) + : ThreadContextBase(tid), + announced(false), + destructor_iterations(kPthreadDestructorIterations), + stack_id(0), + thread(0) { + } + bool announced; + u8 destructor_iterations; + u32 stack_id; + AsanThread *thread; + + void OnCreated(void *arg); + void OnFinished(); +}; + +// AsanThreadContext objects are never freed, so we need many of them. +COMPILER_CHECK(sizeof(AsanThreadContext) <= 256); + +// AsanThread are stored in TSD and destroyed when the thread dies. +class AsanThread { + public: + static AsanThread *Create(thread_callback_t start_routine, void *arg); + static void TSDDtor(void *tsd); + void Destroy(); + + void Init(); // Should be called from the thread itself. + thread_return_t ThreadStart(uptr os_id); + + uptr stack_top() { return stack_top_; } + uptr stack_bottom() { return stack_bottom_; } + uptr stack_size() { return stack_size_; } + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } + u32 tid() { return context_->tid; } + AsanThreadContext *context() { return context_; } + void set_context(AsanThreadContext *context) { context_ = context; } + + const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc); + + bool AddrIsInStack(uptr addr) { + return addr >= stack_bottom_ && addr < stack_top_; + } + + void DeleteFakeStack() { + if (!fake_stack_) return; + FakeStack *t = fake_stack_; + fake_stack_ = 0; + SetTLSFakeStack(0); + t->Destroy(); + } + + bool has_fake_stack() { + return (reinterpret_cast(fake_stack_) > 1); + } + + FakeStack *fake_stack() { + if (!__asan_option_detect_stack_use_after_return) + return 0; + if (!has_fake_stack()) + return AsyncSignalSafeLazyInitFakeStack(); + return fake_stack_; + } + + // True is this thread is currently unwinding stack (i.e. collecting a stack + // trace). Used to prevent deadlocks on platforms where libc unwinder calls + // malloc internally. See PR17116 for more details. + bool isUnwinding() const { return unwinding; } + void setUnwinding(bool b) { unwinding = b; } + + AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } + AsanStats &stats() { return stats_; } + + private: + AsanThread() : unwinding(false) {} + void SetThreadStackAndTls(); + void ClearShadowForThreadStackAndTLS(); + FakeStack *AsyncSignalSafeLazyInitFakeStack(); + + AsanThreadContext *context_; + thread_callback_t start_routine_; + void *arg_; + uptr stack_top_; + uptr stack_bottom_; + // stack_size_ == stack_top_ - stack_bottom_; + // It needs to be set in a async-signal-safe manner. + uptr stack_size_; + uptr tls_begin_; + uptr tls_end_; + + FakeStack *fake_stack_; + AsanThreadLocalMallocStorage malloc_storage_; + AsanStats stats_; + bool unwinding; +}; + +// ScopedUnwinding is a scope for stacktracing member of a context +class ScopedUnwinding { + public: + explicit ScopedUnwinding(AsanThread *t) : thread(t) { + t->setUnwinding(true); + } + ~ScopedUnwinding() { thread->setUnwinding(false); } + + private: + AsanThread *thread; +}; + +struct CreateThreadContextArgs { + AsanThread *thread; + StackTrace *stack; +}; + +// Returns a single instance of registry. +ThreadRegistry &asanThreadRegistry(); + +// Must be called under ThreadRegistryLock. +AsanThreadContext *GetThreadContextByTidLocked(u32 tid); + +// Get the current thread. May return 0. +AsanThread *GetCurrentThread(); +void SetCurrentThread(AsanThread *t); +u32 GetCurrentTidOrInvalid(); +AsanThread *FindThreadByStackAddress(uptr addr); + +// Used to handle fork(). +void EnsureMainThreadIDIsCorrect(); +} // namespace __asan + +#endif // ASAN_THREAD_H diff --git a/projects/compiler-rt/lib/asan/asan_win.cc b/projects/compiler-rt/lib/asan/asan_win.cc new file mode 100644 index 00000000..9e66b341 --- /dev/null +++ b/projects/compiler-rt/lib/asan/asan_win.cc @@ -0,0 +1,148 @@ +//===-- asan_win.cc -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS +#include + +#include +#include + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_mutex.h" + +extern "C" { + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_should_detect_stack_use_after_return() { + __asan_init(); + return __asan_option_detect_stack_use_after_return; + } +} + +namespace __asan { + +// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1 +static BlockingMutex dbghelp_lock(LINKER_INITIALIZED); +static bool dbghelp_initialized = false; +#pragma comment(lib, "dbghelp.lib") + +// ---------------------- TSD ---------------- {{{1 +static bool tsd_key_inited = false; + +static __declspec(thread) void *fake_tsd = 0; + +void AsanTSDInit(void (*destructor)(void *tsd)) { + // FIXME: we're ignoring the destructor for now. + tsd_key_inited = true; +} + +void *AsanTSDGet() { + CHECK(tsd_key_inited); + return fake_tsd; +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + fake_tsd = tsd; +} + +void PlatformTSDDtor(void *tsd) { + AsanThread::TSDDtor(tsd); +} +// ---------------------- Various stuff ---------------- {{{1 +void MaybeReexec() { + // No need to re-exec on Windows. +} + +void *AsanDoesNotSupportStaticLinkage() { +#if defined(_DEBUG) +#error Please build the runtime with a non-debug CRT: /MD or /MT +#endif + return 0; +} + +void SetAlternateSignalStack() { + // FIXME: Decide what to do on Windows. +} + +void UnsetAlternateSignalStack() { + // FIXME: Decide what to do on Windows. +} + +void InstallSignalHandlers() { + // FIXME: Decide what to do on Windows. +} + +void AsanPlatformThreadInit() { + // Nothing here for now. +} + +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) { + BlockingMutexLock lock(&dbghelp_lock); + if (!dbghelp_initialized) { + SymSetOptions(SYMOPT_DEFERRED_LOADS | + SYMOPT_UNDNAME | + SYMOPT_LOAD_LINES); + CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE)); + // FIXME: We don't call SymCleanup() on exit yet - should we? + dbghelp_initialized = true; + } + + // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + DWORD64 offset = 0; + BOOL got_objname = SymFromAddr(GetCurrentProcess(), + (DWORD64)addr, &offset, symbol); + if (!got_objname) + return false; + + DWORD unused; + IMAGEHLP_LINE64 info; + info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), + (DWORD64)addr, &unused, &info); + int written = 0; + out_buffer[0] = '\0'; + // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too. + if (got_fileline) { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s %s:%d", symbol->Name, + info.FileName, info.LineNumber); + } else { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s+0x%p", symbol->Name, offset); + } + return true; +} +} // extern "C" + + +#endif // _WIN32 diff --git a/projects/compiler-rt/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in b/projects/compiler-rt/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in new file mode 100644 index 00000000..faef4e8c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/32bitConfig/lit.site.cfg.in @@ -0,0 +1,13 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.asan_source_dir = "@ASAN_SOURCE_DIR@" +config.bits = "32" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg") + diff --git a/projects/compiler-rt/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in b/projects/compiler-rt/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in new file mode 100644 index 00000000..a3599448 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/64bitConfig/lit.site.cfg.in @@ -0,0 +1,12 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.asan_source_dir = "@ASAN_SOURCE_DIR@" +config.bits = "64" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/projects/compiler-rt/lib/asan/lit_tests/CMakeLists.txt b/projects/compiler-rt/lib/asan/lit_tests/CMakeLists.txt new file mode 100644 index 00000000..72a3f543 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/CMakeLists.txt @@ -0,0 +1,42 @@ +set(ASAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) +set(ASAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/64bitConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/32bitConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + set(ASAN_TESTSUITES) + if(CAN_TARGET_i386) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + endif() + if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig) + endif() + # Run ASan tests only if we're sure we may produce working binaries. + set(ASAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + asan_runtime_libraries) + set(ASAN_TEST_PARAMS + asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + if(LLVM_INCLUDE_TESTS) + list(APPEND ASAN_TEST_DEPS AsanUnitTests) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) + endif() + add_lit_testsuite(check-asan "Running the AddressSanitizer tests" + ${ASAN_TESTSUITES} + PARAMS ${ASAN_TEST_PARAMS} + DEPENDS ${ASAN_TEST_DEPS}) + set_target_properties(check-asan PROPERTIES FOLDER "ASan tests") +endif() diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/interface_symbols_darwin.c b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/interface_symbols_darwin.c new file mode 100644 index 00000000..b453b64c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/interface_symbols_darwin.c @@ -0,0 +1,41 @@ +// Check the presense of interface symbols in the ASan runtime dylib. +// If you're changing this file, please also change +// ../Linux/interface_symbols.c + +// RUN: %clang_asan -dead_strip -O2 %s -o %t.exe +// RUN: rm -f %t.symbols %t.interface + +// RUN: nm -g `otool -L %t.exe | grep "asan_osx_dynamic.dylib" | \ +// RUN: tr -d '\011' | \ +// RUN: sed "s/.dylib.*/.dylib/"` \ +// RUN: | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | grep -v "__asan_malloc_hook" \ +// RUN: | grep -v "__asan_free_hook" \ +// RUN: | grep -v "__asan_symbolize" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols + +// RUN: cat %p/../../../asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// RUN: echo __asan_report_store16 >> %t.interface +// RUN: echo __asan_report_load_n >> %t.interface +// RUN: echo __asan_report_store_n >> %t.interface +// RUN: for i in `jot - 0 10`; do echo __asan_stack_malloc_$i >> %t.interface; done +// RUN: for i in `jot - 0 10`; do echo __asan_stack_free_$i >> %t.interface; done + +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +int main() { return 0; } diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/lit.local.cfg b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/lit.local.cfg new file mode 100644 index 00000000..a85dfcd2 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Darwin']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/malloc_set_zone_name-mprotect.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/malloc_set_zone_name-mprotect.cc new file mode 100644 index 00000000..807a8283 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/malloc_set_zone_name-mprotect.cc @@ -0,0 +1,51 @@ +// Regression test for a bug in malloc_create_zone() +// (https://code.google.com/p/address-sanitizer/issues/detail?id=203) +// The old implementation of malloc_create_zone() didn't always return a +// page-aligned address, so we can only test on a best-effort basis. + +// RUN: %clangxx_asan %s -o %t +// RUN: %t 2>&1 + +#include +#include +#include +#include + +const int kNumIter = 4096; +const int kNumZones = 100; +int main() { + char *mem[kNumIter * 2]; + // Allocate memory chunks from different size classes up to 1 page. + // (For the case malloc() returns memory chunks in descending order) + for (int i = 0; i < kNumIter; i++) { + mem[i] = (char*)malloc(8 * i); + } + // Try to allocate a page-aligned malloc zone. Otherwise the mprotect() call + // in malloc_set_zone_name() will silently fail. + malloc_zone_t *zone = NULL; + bool aligned = false; + for (int i = 0; i < kNumZones; i++) { + zone = malloc_create_zone(0, 0); + if (((uintptr_t)zone & (~0xfff)) == (uintptr_t)zone) { + aligned = true; + break; + } + } + if (!aligned) { + printf("Warning: couldn't allocate a page-aligned zone."); + return 0; + } + // malloc_set_zone_name() calls mprotect(zone, 4096, PROT_READ | PROT_WRITE), + // modifies the zone contents and then calls mprotect(zone, 4096, PROT_READ). + malloc_set_zone_name(zone, "foobar"); + // Allocate memory chunks from different size classes again. + for (int i = 0; i < kNumIter; i++) { + mem[i + kNumIter] = (char*)malloc(8 * i); + } + // Access the allocated memory chunks and free them. + for (int i = 0; i < kNumIter * 2; i++) { + memset(mem[i], 'a', 8 * (i % kNumIter)); + free(mem[i]); + } + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc new file mode 100644 index 00000000..d5f6c7c1 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/malloc_zone-protected.cc @@ -0,0 +1,20 @@ +// Make sure the zones created by malloc_create_zone() are write-protected. +#include +#include + +// RUN: %clangxx_asan %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + + +void *pwn(malloc_zone_t *unused_zone, size_t unused_size) { + printf("PWNED\n"); + return NULL; +} + +int main() { + malloc_zone_t *zone = malloc_create_zone(0, 0); + zone->malloc = pwn; + void *v = malloc_zone_malloc(zone, 1); + // CHECK-NOT: PWNED + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/reexec-insert-libraries-env.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/reexec-insert-libraries-env.cc new file mode 100644 index 00000000..208fe43a --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/reexec-insert-libraries-env.cc @@ -0,0 +1,20 @@ +// Make sure ASan doesn't hang in an exec loop if DYLD_INSERT_LIBRARIES is set. +// This is a regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=159 + +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib + +// FIXME: the following command line may hang in the case of a regression. +// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \ +// RUN: %t 2>&1 | FileCheck %s || exit 1 +#include +#include + +int main() { + const char kEnvName[] = "DYLD_INSERT_LIBRARIES"; + printf("%s=%s\n", kEnvName, getenv(kEnvName)); + // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc new file mode 100644 index 00000000..fa0dd4f9 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc @@ -0,0 +1,20 @@ +// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before +// executing other programs. + +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx %p/../Helpers/echo-env.cc -o %T/echo-env +// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// RUN: -dynamiclib -o %t-darwin-dummy-shared-lib-so.dylib + +// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before +// execl(). + +// RUN: %t %T/echo-env >/dev/null 2>&1 +// RUN: DYLD_INSERT_LIBRARIES=%t-darwin-dummy-shared-lib-so.dylib \ +// RUN: %t %T/echo-env 2>&1 | FileCheck %s || exit 1 +#include +int main(int argc, char *argv[]) { + execl(argv[1], argv[1], "DYLD_INSERT_LIBRARIES", NULL); + // CHECK: {{DYLD_INSERT_LIBRARIES = .*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/blacklist-extra.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/blacklist-extra.cc new file mode 100644 index 00000000..627115cd --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/blacklist-extra.cc @@ -0,0 +1,5 @@ +// This function is broken, but this file is blacklisted +int externalBrokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/echo-env.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/echo-env.cc new file mode 100644 index 00000000..65e91c15 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/echo-env.cc @@ -0,0 +1,19 @@ +// Helper binary for +// lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc +// Prints the environment variable with the given name. +#include +#include + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s ENVNAME\n", argv[0]); + exit(1); + } + const char *value = getenv(argv[1]); + if (value) { + printf("%s = %s\n", argv[1], value); + } else { + printf("%s not set.\n", argv[1]); + } + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/init-order-atexit-extra.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/init-order-atexit-extra.cc new file mode 100644 index 00000000..e4189d19 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/init-order-atexit-extra.cc @@ -0,0 +1,16 @@ +#include + +class C { + public: + C() { value = 42; } + ~C() { } + int value; +}; + +C c; + +void AccessC() { + printf("C value: %d\n", c.value); +} + +int main() { return 0; } diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/init-order-pthread-create-extra.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/init-order-pthread-create-extra.cc new file mode 100644 index 00000000..d4606f0a --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/init-order-pthread-create-extra.cc @@ -0,0 +1,2 @@ +void *bar(void *input); +void *glob2 = bar((void*)0x2345); diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra.cc new file mode 100644 index 00000000..09aed211 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra.cc @@ -0,0 +1,15 @@ +int zero_init() { return 0; } +int badGlobal = zero_init(); +int readBadGlobal() { return badGlobal; } + +namespace badNamespace { +class BadClass { + public: + BadClass() { value = 0; } + int value; +}; +// Global object with non-trivial constructor. +BadClass bad_object; +} // namespace badNamespace + +int accessBadObject() { return badNamespace::bad_object.value; } diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra2.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra2.cc new file mode 100644 index 00000000..69455a0a --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist-extra2.cc @@ -0,0 +1,4 @@ +int zero_init(); +int badSrcGlobal = zero_init(); +int readBadSrcGlobal() { return badSrcGlobal; } + diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist.txt b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist.txt new file mode 100644 index 00000000..83294635 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-blacklist.txt @@ -0,0 +1,3 @@ +global:*badGlobal*=init +type:*badNamespace::BadClass*=init +src:*initialization-blacklist-extra2.cc=init diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra.cc new file mode 100644 index 00000000..3c4cb411 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra.cc @@ -0,0 +1,5 @@ +// This file simply declares a dynamically initialized var by the name of 'y'. +int initY() { + return 5; +} +int y = initY(); diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra2.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra2.cc new file mode 100644 index 00000000..a3d8f190 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-bug-extra2.cc @@ -0,0 +1,6 @@ +// 'z' is dynamically initialized global from different TU. +extern int z; +int __attribute__((noinline)) initY() { + return z + 1; +} +int y = initY(); diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-constexpr-extra.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-constexpr-extra.cc new file mode 100644 index 00000000..b32466a9 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-constexpr-extra.cc @@ -0,0 +1,3 @@ +// Constexpr: +int getCoolestInteger(); +static int coolest_integer = getCoolestInteger(); diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-nobug-extra.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-nobug-extra.cc new file mode 100644 index 00000000..886165af --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/initialization-nobug-extra.cc @@ -0,0 +1,9 @@ +// Linker initialized: +int getAB(); +static int ab = getAB(); +// Function local statics: +int countCalls(); +static int one = countCalls(); +// Trivial constructor, non-trivial destructor: +int getStructWithDtorValue(); +static int val = getStructWithDtorValue(); diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/lit.local.cfg b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/lit.local.cfg new file mode 100644 index 00000000..2fc4d994 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Helpers/lit.local.cfg @@ -0,0 +1,3 @@ +# Sources in this directory are helper files for tests which test functionality +# involving multiple translation units. +config.suffixes = [] diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/asan_prelink_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/asan_prelink_test.cc new file mode 100644 index 00000000..0f158c1b --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/asan_prelink_test.cc @@ -0,0 +1,28 @@ +// Test if asan works with prelink. +// It does not actually use prelink, but relies on ld's flag -Ttext-segment +// or gold's flag -Ttext (we try the first flag first, if that fails we +// try the second flag). +// +// RUN: %clangxx_asan -c %s -o %t.o +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\ +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000 +// RUN: %clangxx_asan %t.o %t.so -Wl,-R. -o %t +// RUN: ASAN_OPTIONS=verbosity=1 %t 2>&1 | FileCheck %s + +// REQUIRES: x86_64-supported-target, asan-64-bits +#if BUILD_SO +int G; +int *getG() { + return &G; +} +#else +#include +extern int *getG(); +int main(int argc, char **argv) { + long p = (long)getG(); + printf("SO mapped at %lx\n", p & ~0xffffffffUL); + *getG() = 0; +} +#endif +// CHECK: 0x003000000000, 0x004fffffffff{{.*}} MidMem +// CHECK: SO mapped at 3600000000 diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/clone_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/clone_test.cc new file mode 100644 index 00000000..0e12f35b --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/clone_test.cc @@ -0,0 +1,44 @@ +// Regression test for: +// http://code.google.com/p/address-sanitizer/issues/detail?id=37 + +// RUN: %clangxx_asan -O0 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t | FileCheck %s + +#include +#include +#include +#include +#include +#include + +int Child(void *arg) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + _exit(1); // NoReturn, stack will remain unpoisoned unless we do something. +} + +int main(int argc, char **argv) { + const int kStackSize = 1 << 20; + char child_stack[kStackSize + 1]; + char *sp = child_stack + kStackSize; // Stack grows down. + printf("Parent: %p\n", sp); + pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0); + int status; + pid_t wait_result = waitpid(clone_pid, &status, __WCLONE); + if (wait_result < 0) { + perror("waitpid"); + return 0; + } + if (wait_result == clone_pid && WIFEXITED(status)) { + // Make sure the child stack was indeed unpoisoned. + for (int i = 0; i < kStackSize; i++) + child_stack[i] = i; + int ret = child_stack[argc - 1]; + printf("PASSED\n"); + // CHECK: PASSED + return ret; + } + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/coverage.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/coverage.cc new file mode 100644 index 00000000..4373e9b1 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/coverage.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %t.so -fPIC +// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t -Wl,-R. %t.so +// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-main +// RUN: %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo +// RUN: %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar +// RUN: %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar + +#include +#include +#include + +#ifdef SHARED +void bar() { printf("bar\n"); } +#else +__attribute__((noinline)) +void foo() { printf("foo\n"); } +extern void bar(); + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "foo")) + foo(); + if (!strcmp(argv[i], "bar")) + bar(); + } +} +#endif + +// CHECK-main: PID: [[PID:[0-9]+]] +// CHECK-main: [[PID]].sancov: 1 PCs written +// CHECK-main-NOT: .so.[[PID]] +// +// CHECK-foo: PID: [[PID:[0-9]+]] +// CHECK-foo: [[PID]].sancov: 2 PCs written +// CHECK-foo-NOT: .so.[[PID]] +// +// CHECK-bar: PID: [[PID:[0-9]+]] +// CHECK-bar: [[PID]].sancov: 1 PCs written +// CHECK-bar: .so.[[PID]].sancov: 1 PCs written +// +// CHECK-foo-bar: PID: [[PID:[0-9]+]] +// CHECK-foo-bar: [[PID]].sancov: 2 PCs written +// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob.cc new file mode 100644 index 00000000..123768b0 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t %p 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include +#include + + +int main(int argc, char *argv[]) { + std::string path = argv[1]; + std::string pattern = path + "/glob_test_root/*a"; + printf("pattern: %s\n", pattern.c_str()); + + glob_t globbuf; + int res = glob(pattern.c_str(), 0, 0, &globbuf); + + printf("%d %s\n", errno, strerror(errno)); + assert(res == 0); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + globfree(&globbuf); + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob_test_root/aa b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob_test_root/aa new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ab b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ab new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ba b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/glob_test_root/ba new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc new file mode 100644 index 00000000..67e9c371 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/heap-overflow-large.cc @@ -0,0 +1,23 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=183 + +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %t 12 2>&1 | FileCheck %s +// RUN: not %t 100 2>&1 | FileCheck %s +// RUN: not %t 10000 2>&1 | FileCheck %s + +#include +#include + +int main(int argc, char *argv[]) { + int *x = new int[5]; + memset(x, 0, sizeof(x[0]) * 5); + int index = atoi(argv[1]); + int res = x[index]; + // CHECK: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}} + // CHECK: #0 0x{{.*}} in main {{.*}}heap-overflow-large.cc:[[@LINE-2]] + // CHECK: AddressSanitizer can not {{(provide additional info|describe address in more detail \(wild memory access suspected\))}} + // CHECK: SUMMARY: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}} + delete[] x; + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/heavy_uar_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/heavy_uar_test.cc new file mode 100644 index 00000000..27b179e8 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/heavy_uar_test.cc @@ -0,0 +1,54 @@ +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s + +#include +#include +#include + +__attribute__((noinline)) +char *pretend_to_do_something(char *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); + return x; +} + +__attribute__((noinline)) +char *LeakStack() { + char x[1024]; + memset(x, 0, sizeof(x)); + return pretend_to_do_something(x); +} + +template +__attribute__((noinline)) +void RecuriveFunctionWithStackFrame(int depth) { + if (depth <= 0) return; + char x[kFrameSize]; + x[0] = depth; + pretend_to_do_something(x); + RecuriveFunctionWithStackFrame(depth - 1); +} + +int main(int argc, char **argv) { + int n_iter = argc >= 2 ? atoi(argv[1]) : 1000; + int depth = argc >= 3 ? atoi(argv[2]) : 500; + for (int i = 0; i < n_iter; i++) { + RecuriveFunctionWithStackFrame<10>(depth); + RecuriveFunctionWithStackFrame<100>(depth); + RecuriveFunctionWithStackFrame<500>(depth); + RecuriveFunctionWithStackFrame<1024>(depth); + RecuriveFunctionWithStackFrame<2000>(depth); + RecuriveFunctionWithStackFrame<5000>(depth); + RecuriveFunctionWithStackFrame<10000>(depth); + } + char *stale_stack = LeakStack(); + RecuriveFunctionWithStackFrame<1024>(10); + stale_stack[100]++; + // CHECK: ERROR: AddressSanitizer: stack-use-after-return on address + // CHECK: is located in stack of thread T0 at offset 132 in frame + // CHECK: in LeakStack(){{.*}}heavy_uar_test.cc: + // CHECK: [32, 1056) 'x' + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/initialization-bug-any-order.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/initialization-bug-any-order.cc new file mode 100644 index 00000000..042a07e4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/initialization-bug-any-order.cc @@ -0,0 +1,36 @@ +// Test to make sure basic initialization order errors are caught. +// Check that on Linux initialization order bugs are caught +// independently on order in which we list source files (if we specify +// strict init-order checking). + +// RUN: %clangxx_asan -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +#include + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // CHECK: {{AddressSanitizer: initialization-order-fiasco}} + // CHECK: {{READ of size .* at 0x.* thread T0}} + // CHECK: {{#0 0x.* in .*initX.* .*initialization-bug-any-order.cc:}}[[@LINE-3]] + // CHECK: {{0x.* is located 0 bytes inside of global variable .*y.*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_failure_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_failure_test.cc new file mode 100644 index 00000000..9d161aa2 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_failure_test.cc @@ -0,0 +1,22 @@ +// If user provides his own libc functions, ASan doesn't +// intercept these functions. + +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include + +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return 0; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK-NOT: heap-use-after-free +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_malloc_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_malloc_test.cc new file mode 100644 index 00000000..cdd7239a --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_malloc_test.cc @@ -0,0 +1,23 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +extern "C" void *__interceptor_malloc(size_t size); +extern "C" void *malloc(size_t size) { + write(2, "malloc call\n", sizeof("malloc call\n") - 1); + return __interceptor_malloc(size); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: malloc call + // CHECK: heap-use-after-free +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc new file mode 100644 index 00000000..198e1f38 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_readdir_r_test.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -O0 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// +// RUN: %clangxx_asan -O0 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include + + +int main() { + // Ensure the readdir_r interceptor doesn't erroneously mark the entire dirent + // as written when the end of the directory pointer is reached. + fputs("test1: reading the " TEMP_DIR " directory...\n", stderr); + DIR *d = opendir(TEMP_DIR); + struct dirent *result = (struct dirent *)(0xfeedbeef); + // We assume the temp dir for this test doesn't have crazy long file names. + char entry_buffer[4096]; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + unsigned count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent *)entry_buffer)->d_reclen = 9999; + if (readdir_r(d, (struct dirent *)entry_buffer, &result) != 0) + abort(); + ++count; + } while (result != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test1: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries + + // Ensure the readdir64_r interceptor doesn't have the bug either. + fputs("test2: reading the " TEMP_DIR " directory...\n", stderr); + d = opendir(TEMP_DIR); + struct dirent64 *result64; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent64 *)entry_buffer)->d_reclen = 9999; + if (readdir64_r(d, (struct dirent64 *)entry_buffer, &result64) != 0) + abort(); + ++count; + } while (result64 != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test2: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_test.cc new file mode 100644 index 00000000..2b3316d7 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interception_test.cc @@ -0,0 +1,22 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return __interceptor_strtol(nptr, endptr, base); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK: heap-use-after-free +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interface_symbols_linux.c b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interface_symbols_linux.c new file mode 100644 index 00000000..d6ceda7a --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/interface_symbols_linux.c @@ -0,0 +1,35 @@ +// Check the presense of interface symbols in compiled file. + +// RUN: %clang_asan -O2 %s -o %t.exe +// RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | grep -v "__asan_malloc_hook" \ +// RUN: | grep -v "__asan_free_hook" \ +// RUN: | grep -v "__asan_symbolize" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_stack_" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols +// RUN: cat %p/../../../asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// RUN: echo __asan_report_store16 >> %t.interface +// RUN: echo __asan_report_load_n >> %t.interface +// RUN: echo __asan_report_store_n >> %t.interface +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +// FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing +// in "initialized data section". +// REQUIRES: x86_64-supported-target,i386-supported-target + +int main() { return 0; } diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/lit.local.cfg b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/lit.local.cfg new file mode 100644 index 00000000..57271b80 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/malloc-in-qsort.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/malloc-in-qsort.cc new file mode 100644 index 00000000..3251b35e --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/malloc-in-qsort.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW + +// Test how well we unwind in presence of qsort in the stack +// (i.e. if we can unwind through a function compiled w/o frame pointers). +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only avaliable on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +// REQUIRES: compiler-rt-optimized + +#include +#include + +int *GlobalPtr; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + GlobalPtr = new int[10]; + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); + return GlobalPtr[10]; +} + +// Fast unwind: can not unwind through qsort. +// FIXME: this test does not properly work with slow unwind yet. + +// CHECK-FAST: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-FAST: is located 0 bytes to the right +// CHECK-FAST: #0{{.*}}operator new +// CHECK-FAST-NEXT: #1{{.*}}QsortCallback +// CHECK-FAST-NOT: MyQsort +// +// CHECK-SLOW: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-SLOW: is located 0 bytes to the right +// CHECK-SLOW: #0{{.*}}operator new +// CHECK-SLOW-NEXT: #1{{.*}}QsortCallback +// CHECK-SLOW: #{{.*}}MyQsort +// CHECK-SLOW-NEXT: #{{.*}}main diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/malloc_delete_mismatch.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/malloc_delete_mismatch.cc new file mode 100644 index 00000000..7010eb2d --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/malloc_delete_mismatch.cc @@ -0,0 +1,31 @@ +// Check that we detect malloc/delete mismatch only if the approptiate flag +// is set. + +// RUN: %clangxx_asan -g %s -o %t 2>&1 + +// Find error and provide malloc context. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=ALLOC-STACK + +// No error here. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %t + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s +#include + +static volatile char *x; + +int main() { + x = (char*)malloc(10); + x[0] = 0; + delete x; +} +// CHECK: ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete) on 0x +// CHECK-NEXT: #0{{.*}}operator delete +// CHECK: #{{.*}}main +// CHECK: is located 0 bytes inside of 10-byte region +// CHECK-NEXT: allocated by thread T0 here: +// ALLOC-STACK-NEXT: #0{{.*}}malloc +// ALLOC-STACK: #{{.*}}main +// CHECK: HINT: {{.*}} you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0 diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/overflow-in-qsort.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/overflow-in-qsort.cc new file mode 100644 index 00000000..13997726 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/overflow-in-qsort.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW + +// Test how well we unwind in presence of qsort in the stack +// (i.e. if we can unwind through a function compiled w/o frame pointers). +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only avaliable on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +#include +#include + +int global_array[10]; +volatile int one = 1; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + global_array[one * 10] = 0; // BOOM + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); +} + +// Fast unwind: can not unwind through qsort. + +// CHECK-FAST: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-FAST: #0{{.*}} in QsortCallback +// CHECK-FAST-NOT: MyQsort +// CHECK-FAST: is located 0 bytes to the right of global variable 'global_array + +// CHECK-SLOW: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-SLOW: #0{{.*}} in QsortCallback +// CHECK-SLOW: #{{.*}} in MyQsort +// CHECK-SLOW: #{{.*}} in main +// CHECK-SLOW: is located 0 bytes to the right of global variable 'global_array diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/preinit_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/preinit_test.cc new file mode 100644 index 00000000..28e50947 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/preinit_test.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx -DFUNC=zzzz %s -shared -o %t.so -fPIC +// RUN: %clangxx_asan -DFUNC=main %s -o %t -Wl,-R. %t.so +// RUN: %t + +// This test ensures that we call __asan_init early enough. +// We build a shared library w/o asan instrumentation +// and the binary with asan instrumentation. +// Both files include the same header (emulated by -DFUNC here) +// with C++ template magic which runs global initializer at library load time. +// The function get() is instrumented with asan, but called +// before the usual constructors are run. +// So, we must make sure that __asan_init is executed even earlier. +// +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56393 + +struct A { + int foo() const { return 0; } +}; +A get () { return A(); } +template struct O { + static A const e; +}; +template A const O ::e = get(); +int FUNC() { + return O::e.foo(); +} + diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/ptrace.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/ptrace.cc new file mode 100644 index 00000000..8831b81e --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/ptrace.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -DPOSITIVE -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + pid_t pid; + pid = fork(); + if (pid == 0) { // child + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execl("/bin/true", "true", NULL); + } else { + wait(NULL); + user_regs_struct regs; + int res; + user_regs_struct * volatile pregs = ®s; +#ifdef POSITIVE + ++pregs; +#endif + res = ptrace(PTRACE_GETREGS, pid, NULL, pregs); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{.*ptrace.cc:}}[[@LINE-2]] + assert(!res); +#if __WORDSIZE == 64 + printf("%zx\n", regs.rip); +#else + printf("%lx\n", regs.eip); +#endif + + user_fpregs_struct fpregs; + res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + printf("%lx\n", (unsigned long)fpregs.cwd); + +#if __WORDSIZE == 32 + user_fpxregs_struct fpxregs; + res = ptrace(PTRACE_GETFPXREGS, pid, NULL, &fpxregs); + assert(!res); + printf("%lx\n", (unsigned long)fpxregs.mxcsr); +#endif + + ptrace(PTRACE_CONT, pid, NULL, NULL); + wait(NULL); + } + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/rlimit_mmap_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/rlimit_mmap_test.cc new file mode 100644 index 00000000..0d1d4baa --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/rlimit_mmap_test.cc @@ -0,0 +1,16 @@ +// Check that we properly report mmap failure. +// RUN: %clangxx_asan %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +static volatile void *x; + +int main(int argc, char **argv) { + struct rlimit mmap_resource_limit = { 0, 0 }; + assert(0 == setrlimit(RLIMIT_AS, &mmap_resource_limit)); + x = malloc(10000000); +// CHECK: ERROR: Failed to mmap + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/swapcontext_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/swapcontext_test.cc new file mode 100644 index 00000000..6cbb69a3 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/swapcontext_test.cc @@ -0,0 +1,90 @@ +// Check that ASan plays well with easy cases of makecontext/swapcontext. + +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s +// +// This test is too sublte to try on non-x86 arch for now. +// REQUIRES: x86_64-supported-target,i386-supported-target + +#include +#include +#include + +ucontext_t orig_context; +ucontext_t child_context; + +const int kStackSize = 1 << 20; + +__attribute__((noinline)) +void Throw() { + throw 1; +} + +__attribute__((noinline)) +void ThrowAndCatch() { + try { + Throw(); + } catch(int a) { + printf("ThrowAndCatch: %d\n", a); + } +} + +void Child(int mode) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + ThrowAndCatch(); // Simulate __asan_handle_no_return(). + // (a) Do nothing, just return to parent function. + // (b) Jump into the original function. Stack remains poisoned unless we do + // something. + if (mode == 1) { + if (swapcontext(&child_context, &orig_context) < 0) { + perror("swapcontext"); + _exit(0); + } + } +} + +int Run(int arg, int mode, char *child_stack) { + printf("Child stack: %p\n", child_stack); + // Setup child context. + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + if (mode == 0) { + child_context.uc_link = &orig_context; + } + makecontext(&child_context, (void (*)())Child, 1, mode); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 0; + } + // Touch childs's stack to make sure it's unpoisoned. + for (int i = 0; i < kStackSize; i++) { + child_stack[i] = i; + } + return child_stack[arg]; +} + +int main(int argc, char **argv) { + char stack[kStackSize + 1]; + // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext + int ret = 0; + ret += Run(argc - 1, 0, stack); + printf("Test1 passed\n"); + // CHECK: Test1 passed + ret += Run(argc - 1, 1, stack); + printf("Test2 passed\n"); + // CHECK: Test2 passed + char *heap = new char[kStackSize + 1]; + ret += Run(argc - 1, 0, heap); + printf("Test3 passed\n"); + // CHECK: Test3 passed + ret += Run(argc - 1, 1, heap); + printf("Test4 passed\n"); + // CHECK: Test4 passed + + delete [] heap; + return ret; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/syscalls.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/syscalls.cc new file mode 100644 index 00000000..4bcbe446 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/syscalls.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include + +#include + +/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general + sanity of their behaviour. */ + +int main(int argc, char *argv[]) { + char buf[1000]; + __sanitizer_syscall_pre_recvmsg(0, buf - 1, 0); + // CHECK: AddressSanitizer: stack-buffer-{{.*}}erflow + // CHECK: READ of size {{.*}} at {{.*}} thread T0 + // CHECK: #0 {{.*}} in __sanitizer_syscall{{.*}}recvmsg + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/time_null_regtest.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/time_null_regtest.cc new file mode 100644 index 00000000..566409be --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/time_null_regtest.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O0 %s -fsanitize-address-zero-base-shadow -pie -o %t && %t 2>&1 | FileCheck %s + +// Zero-base shadow only works on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +// A regression test for time(NULL), which caused ASan to crash in the +// zero-based shadow mode on Linux. +// FIXME: this test does not work on Darwin, because the code pages of the +// executable interleave with the zero-based shadow. + +#include +#include +#include + +int main() { + time_t t = time(NULL); + fprintf(stderr, "Time: %s\n", ctime(&t)); // NOLINT + // CHECK: {{Time: .* .* .*}} + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/tsd_dtor_leak.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/tsd_dtor_leak.cc new file mode 100644 index 00000000..a1d89ee4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/tsd_dtor_leak.cc @@ -0,0 +1,39 @@ +// Regression test for a leak in tsd: +// https://code.google.com/p/address-sanitizer/issues/detail?id=233 +// RUN: %clangxx_asan -O1 %s -o %t +// RUN: ASAN_OPTIONS=quarantine_size=1 %t +#include +#include +#include +#include + +extern "C" size_t __asan_get_heap_size(); +static pthread_key_t tsd_key; + +void *Thread(void *) { + pthread_setspecific(tsd_key, malloc(10)); + return 0; +} + +static volatile void *v; + +void Dtor(void *tsd) { + v = malloc(10000); + free(tsd); + free((void*)v); // The bug was that this was leaking. +} + +int main() { + assert(0 == pthread_key_create(&tsd_key, Dtor)); + size_t old_heap_size = 0; + for (int i = 0; i < 10; i++) { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + size_t new_heap_size = __asan_get_heap_size(); + fprintf(stderr, "heap size: new: %zd old: %zd\n", new_heap_size, old_heap_size); + if (old_heap_size) + assert(old_heap_size == new_heap_size); + old_heap_size = new_heap_size; + } +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc new file mode 100644 index 00000000..9663859d --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/uar_signals.cc @@ -0,0 +1,70 @@ +// This test shows that the current implementation of use-after-return is +// not signal-safe. +// RUN: %clangxx_asan -O1 %s -o %t -lpthread && %t +// RUN: %clangxx_asan -O1 %s -o %t -lpthread && %t +#include +#include +#include +#include +#include + +int *g; +int n_signals; + +typedef void (*Sigaction)(int, siginfo_t *, void *); + +void SignalHandler(int, siginfo_t*, void*) { + int local; + g = &local; + n_signals++; + // printf("s: %p\n", &local); +} + +static void EnableSigprof(Sigaction SignalHandler) { + struct sigaction sa; + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGPROF, &sa, NULL) != 0) { + perror("sigaction"); + abort(); + } + struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 1; + timer.it_value = timer.it_interval; + if (setitimer(ITIMER_PROF, &timer, 0) != 0) { + perror("setitimer"); + abort(); + } +} + +void RecursiveFunction(int depth) { + if (depth == 0) return; + int local; + g = &local; + // printf("r: %p\n", &local); + // printf("[%2d] n_signals: %d\n", depth, n_signals); + RecursiveFunction(depth - 1); + RecursiveFunction(depth - 1); +} + +void *Thread(void *) { + RecursiveFunction(18); + return NULL; +} + +int main(int argc, char **argv) { + EnableSigprof(SignalHandler); + + for (int i = 0; i < 4; i++) { + fprintf(stderr, "."); + const int kNumThread = sizeof(void*) == 8 ? 16 : 8; + pthread_t t[kNumThread]; + for (int i = 0; i < kNumThread; i++) + pthread_create(&t[i], 0, Thread, 0); + for (int i = 0; i < kNumThread; i++) + pthread_join(t[i], 0); + } + fprintf(stderr, "\n"); +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/unpoison_tls.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/unpoison_tls.cc new file mode 100644 index 00000000..d67c4f95 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/unpoison_tls.cc @@ -0,0 +1,35 @@ +// Test that TLS is unpoisoned on thread death. +// REQUIRES: x86_64-supported-target,i386-supported-target + +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 + +#include +#include +#include + +#include + +__thread int64_t tls_var[2]; + +volatile int64_t *p_tls_var; + +void *first(void *arg) { + ASAN_POISON_MEMORY_REGION(&tls_var, sizeof(tls_var)); + p_tls_var = tls_var; + return 0; +} + +void *second(void *arg) { + assert(tls_var == p_tls_var); + *p_tls_var = 1; + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + assert(0 == pthread_create(&p, 0, first, 0)); + assert(0 == pthread_join(p, 0)); + assert(0 == pthread_create(&p, 0, second, 0)); + assert(0 == pthread_join(p, 0)); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow32.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow32.cc new file mode 100644 index 00000000..e6bcc559 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow32.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// Zero-base shadow only works on x86_64 and i386. +// REQUIRES: i386-supported-target, asan-32-bits + +#include +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*zero-base-shadow32.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is .* frame}} + // CHECK: main + + // Check that shadow for stack memory occupies lower part of address space. + // CHECK: =>0x1 + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow64.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow64.cc new file mode 100644 index 00000000..1db725c9 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/Linux/zero-base-shadow64.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 -fsanitize-address-zero-base-shadow -fPIE -pie %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// Zero-base shadow only works on x86_64 and i386. +// REQUIRES: x86_64-supported-target, asan-64-bits + +#include +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*zero-base-shadow64.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is .* frame}} + // CHECK: main + + // Check that shadow for stack memory occupies lower part of address space. + // CHECK: =>0x0f + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc new file mode 100644 index 00000000..5d939991 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc @@ -0,0 +1,13 @@ +//===----------- darwin-dummy-shared-lib-so.cc ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +void foo() {} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/dlclose-test-so.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/dlclose-test-so.cc new file mode 100644 index 00000000..73e00507 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/dlclose-test-so.cc @@ -0,0 +1,33 @@ +//===----------- dlclose-test-so.cc -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Regression test for +// http://code.google.com/p/address-sanitizer/issues/detail?id=19 +//===----------------------------------------------------------------------===// +#include + +static int pad1; +static int static_var; +static int pad2; + +extern "C" +int *get_address_of_static_var() { + return &static_var; +} + +__attribute__((constructor)) +void at_dlopen() { + printf("%s: I am being dlopened\n", __FILE__); +} +__attribute__((destructor)) +void at_dlclose() { + printf("%s: I am being dlclosed\n", __FILE__); +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/init-order-dlopen-so.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/init-order-dlopen-so.cc new file mode 100644 index 00000000..dc097a52 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/init-order-dlopen-so.cc @@ -0,0 +1,12 @@ +#include +#include + +extern "C" void inc_global(); + +int slow_init() { + sleep(1); + inc_global(); + return 42; +} + +int slowly_init_glob = slow_init(); diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/lit.local.cfg b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/lit.local.cfg new file mode 100644 index 00000000..b3677c17 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/shared-lib-test-so.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/shared-lib-test-so.cc new file mode 100644 index 00000000..6ef565ce --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/SharedLibs/shared-lib-test-so.cc @@ -0,0 +1,26 @@ +//===----------- shared-lib-test-so.cc --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include + +int pad[10]; +int GLOB[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +extern "C" +void inc(int index) { + GLOB[index]++; +} + +extern "C" +void inc2(int *a, int index) { + a[index]++; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/allocator_returns_null.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/allocator_returns_null.cc new file mode 100644 index 00000000..595c9e25 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/allocator_returns_null.cc @@ -0,0 +1,81 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL + +#include +#include +#include +#include +#include +#include +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: AddressSanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/allow_user_segv.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/allow_user_segv.cc new file mode 100644 index 00000000..55cf6044 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/allow_user_segv.cc @@ -0,0 +1,48 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=180 + +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %t 2>&1 | FileCheck %s + +#include +#include + +struct sigaction user_sigaction; +struct sigaction original_sigaction; + +void User_OnSIGSEGV(int signum, siginfo_t *siginfo, void *context) { + fprintf(stderr, "User sigaction called\n"); + if (original_sigaction.sa_flags | SA_SIGINFO) + original_sigaction.sa_sigaction(signum, siginfo, context); + else + original_sigaction.sa_handler(signum); +} + +int DoSEGV() { + volatile int *x = 0; + return *x; +} + +int main() { + user_sigaction.sa_sigaction = User_OnSIGSEGV; + user_sigaction.sa_flags = SA_SIGINFO; +#if defined(__APPLE__) && !defined(__LP64__) + // On 32-bit Darwin KERN_PROTECTION_FAILURE (SIGBUS) is delivered. + int signum = SIGBUS; +#else + // On 64-bit Darwin KERN_INVALID_ADDRESS (SIGSEGV) is delivered. + // On Linux SIGSEGV is delivered as well. + int signum = SIGSEGV; +#endif + if (sigaction(signum, &user_sigaction, &original_sigaction)) { + perror("sigaction"); + return 1; + } + fprintf(stderr, "User sigaction installed\n"); + return DoSEGV(); +} + +// CHECK: User sigaction installed +// CHECK-NEXT: User sigaction called +// CHECK-NEXT: ASAN:SIGSEGV +// CHECK: AddressSanitizer: SEGV on unknown address diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/asan-symbolize-sanity-test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/asan-symbolize-sanity-test.cc new file mode 100644 index 00000000..0efe245b --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/asan-symbolize-sanity-test.cc @@ -0,0 +1,39 @@ +// Check that asan_symbolize.py script works (for binaries, ASan RTL and +// shared object files. + +// RUN: %clangxx_asan -O0 %p/SharedLibs/shared-lib-test-so.cc -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_SYMBOLIZER_PATH= not %t 2>&1 | %asan_symbolize | FileCheck %s +#include +#include +#include + +#include + +using std::string; + +typedef void (fun_t)(int*, int); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc2 = (fun_t*)dlsym(lib, "inc2"); + if (!inc2) return 1; + printf("ok\n"); + int *array = (int*)malloc(40); + inc2(array, 1); + inc2(array, -1); // BOOM + // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow + // CHECK: READ of size 4 at 0x{{.*}} + // CHECK: #0 {{.*}} in inc2 {{.*}}shared-lib-test-so.cc:25 + // CHECK: #1 {{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-4]] + // CHECK: allocated by thread T{{.*}} here: + // CHECK: #{{.*}} in {{(wrap_|__interceptor_)?}}malloc + // CHECK: #{{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-9]] + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/assign_large_valloc_to_global.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/assign_large_valloc_to_global.cc new file mode 100644 index 00000000..b0a50157 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/assign_large_valloc_to_global.cc @@ -0,0 +1,8 @@ +// Make sure we don't report a leak nor hang. +// RUN: %clangxx_asan -O3 %s -o %t && %t +#include +#ifndef __APPLE__ +#include +#endif // __APPLE__ +int *p = (int*)valloc(1 << 20); +int main() { } diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/atexit_stats.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/atexit_stats.cc new file mode 100644 index 00000000..e3b1269d --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/atexit_stats.cc @@ -0,0 +1,13 @@ +// Make sure we report atexit stats. +// RUN: %clangxx_asan -O3 %s -o %t +// RUN: ASAN_OPTIONS=atexit=1:print_stats=1 %t 2>&1 | FileCheck %s +#include +#if !defined(__APPLE__) +#include +#endif +int *p1 = (int*)malloc(900); +int *p2 = (int*)malloc(90000); +int *p3 = (int*)malloc(9000000); +int main() { } + +// CHECK: AddressSanitizer exit stats: diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/blacklist.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/blacklist.cc new file mode 100644 index 00000000..46625ee7 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/blacklist.cc @@ -0,0 +1,38 @@ +// Test the blacklist functionality of ASan + +// RUN: echo "fun:*brokenFunction*" > %tmp +// RUN: echo "global:*badGlobal*" >> %tmp +// RUN: echo "src:*blacklist-extra.cc" >> %tmp +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O0 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O1 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O2 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O3 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 + +// badGlobal is accessed improperly, but we blacklisted it. Align +// it to make sure memory past the end of badGlobal will be in +// the same page. +__attribute__((aligned(16))) int badGlobal; +int readBadGlobal() { + return (&badGlobal)[1]; +} + +// A function which is broken, but excluded in the blacklist. +int brokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} + +// This function is defined in Helpers/blacklist-extra.cc, a source file which +// is blacklisted by name +int externalBrokenFunction(int x); + +int main(int argc, char **argv) { + brokenFunction(argc); + int x = readBadGlobal(); + externalBrokenFunction(argc); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/contiguous_container.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/contiguous_container.cc new file mode 100644 index 00000000..aa97592c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/contiguous_container.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_asan -O %s -o %t && %t +// +// Test __sanitizer_annotate_contiguous_container. + +#include +#include +#include +#include + +extern "C" { +void __sanitizer_annotate_contiguous_container(void *beg, void *end, + void *old_mid, void *new_mid); +bool __asan_address_is_poisoned(void *addr); +} // extern "C" + +void TestContainer(size_t capacity) { + char *beg = new char[capacity]; + char *end = beg + capacity; + char *mid = beg + capacity; + char *old_mid = 0; + unsigned seed = 0; + + for (int i = 0; i < 10000; i++) { + size_t size = rand_r(&seed) % (capacity + 1); + assert(size <= capacity); + old_mid = mid; + mid = beg + size; + __sanitizer_annotate_contiguous_container(beg, end, old_mid, mid); + + for (size_t idx = 0; idx < size; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + for (size_t idx = size; idx < capacity; idx++) + assert(__asan_address_is_poisoned(beg + idx)); + } + + // Don't forget to unpoison the whole thing before destroing/reallocating. + __sanitizer_annotate_contiguous_container(beg, end, mid, end); + for (size_t idx = 0; idx < capacity; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + delete[] beg; +} + +int main(int argc, char **argv) { + int n = argc == 1 ? 128 : atoi(argv[1]); + for (int i = 0; i <= n; i++) + TestContainer(i); +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc new file mode 100644 index 00000000..669cf150 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/current_allocated_bytes.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O2 %s -o %t && %t + +#include +#include +#include +#include +#include + +const size_t kLargeAlloc = 1UL << 20; + +void* allocate(void *arg) { + volatile void *ptr = malloc(kLargeAlloc); + free((void*)ptr); + return 0; +} + +void* check_stats(void *arg) { + assert(__asan_get_current_allocated_bytes() > 0); + return 0; +} + +int main() { + size_t used_mem = __asan_get_current_allocated_bytes(); + printf("Before: %zu\n", used_mem); + const int kNumIterations = 1000; + for (int iter = 0; iter < kNumIterations; iter++) { + pthread_t thr[4]; + for (int j = 0; j < 4; j++) { + assert(0 == + pthread_create(&thr[j], 0, (j < 2) ? allocate : check_stats, 0)); + } + for (int j = 0; j < 4; j++) + assert(0 == pthread_join(thr[j], 0)); + used_mem = __asan_get_current_allocated_bytes(); + if (used_mem > kLargeAlloc) { + printf("After iteration %d: %zu\n", iter, used_mem); + return 1; + } + } + printf("Success after %d iterations\n", kNumIterations); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_call_stack.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_call_stack.cc new file mode 100644 index 00000000..e24704b9 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_call_stack.cc @@ -0,0 +1,25 @@ +// Check that UAR mode can handle very deep recusrion. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: %t 2>&1 | FileCheck %s +// Also check that use_sigaltstack+verbosity doesn't crash. +// RUN: ASAN_OPTIONS=verbosity=1:use_sigaltstack=1 %t | FileCheck %s +#include + +__attribute__((noinline)) +void RecursiveFunc(int depth, int *ptr) { + if ((depth % 1000) == 0) + printf("[%05d] ptr: %p\n", depth, ptr); + if (depth == 0) + return; + int local; + RecursiveFunc(depth - 1, &local); +} + +int main(int argc, char **argv) { + RecursiveFunc(40000, 0); + return 0; +} +// CHECK: [40000] ptr: +// CHECK: [20000] ptr: +// CHECK: [00000] ptr diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_stack_uaf.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_stack_uaf.cc new file mode 100644 index 00000000..920411c4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_stack_uaf.cc @@ -0,0 +1,31 @@ +// Check that we can store lots of stack frames if asked to. + +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 not %t 2>&1 | FileCheck %s +#include +#include + +template +struct DeepFree { + static void free(char *x) { + DeepFree::free(x); + } +}; + +template<> +struct DeepFree<0> { + static void free(char *x) { + ::free(x); + } +}; + +int main() { + char *x = (char*)malloc(10); + // deep_free(x); + DeepFree<200>::free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: DeepFree<36> + // CHECK: DeepFree<98> + // CHECK: DeepFree<115> +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_tail_call.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_tail_call.cc new file mode 100644 index 00000000..2e7aa8e0 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_tail_call.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// CHECK: AddressSanitizer: global-buffer-overflow +int global[10]; +// CHECK: {{#0.*call4}} +void __attribute__((noinline)) call4(int i) { global[i+10]++; } +// CHECK: {{#1.*call3}} +void __attribute__((noinline)) call3(int i) { call4(i); } +// CHECK: {{#2.*call2}} +void __attribute__((noinline)) call2(int i) { call3(i); } +// CHECK: {{#3.*call1}} +void __attribute__((noinline)) call1(int i) { call2(i); } +// CHECK: {{#4.*main}} +int main(int argc, char **argv) { + call1(argc); + return global[0]; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_thread_stack.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_thread_stack.cc new file mode 100644 index 00000000..92e0d66c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/deep_thread_stack.cc @@ -0,0 +1,57 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include + +int *x; + +void *AllocThread(void *arg) { + x = new int; + *x = 42; + return NULL; +} + +void *FreeThread(void *arg) { + delete x; + return NULL; +} + +void *AccessThread(void *arg) { + *x = 43; // BOOM + return NULL; +} + +typedef void* (*callback_type)(void* arg); + +void *RunnerThread(void *function) { + pthread_t thread; + pthread_create(&thread, NULL, (callback_type)function, NULL); + pthread_join(thread, NULL); + return NULL; +} + +void RunThread(callback_type function) { + pthread_t runner; + pthread_create(&runner, NULL, RunnerThread, (void*)function); + pthread_join(runner, NULL); +} + +int main(int argc, char *argv[]) { + RunThread(AllocThread); + RunThread(FreeThread); + RunThread(AccessThread); + return (x != 0); +} + +// CHECK: AddressSanitizer: heap-use-after-free +// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]] +// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here: +// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here: +// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[FREE_RUNNER]] created by T0 here: +// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here: diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/default_blacklist.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/default_blacklist.cc new file mode 100644 index 00000000..25a1ae17 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/default_blacklist.cc @@ -0,0 +1,3 @@ +// Test that ASan uses the default blacklist from resource directory. +// RUN: %clangxx_asan -### %s 2>&1 | FileCheck %s +// CHECK: fsanitize-blacklist={{.*}}asan_blacklist.txt diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/default_options.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/default_options.cc new file mode 100644 index 00000000..84b80557 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/default_options.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s + +const char *kAsanDefaultOptions="verbosity=1 foo=bar"; + +extern "C" +__attribute__((no_sanitize_address)) +const char *__asan_default_options() { + // CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar + return kAsanDefaultOptions; +} + +int main() { + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/dlclose-test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/dlclose-test.cc new file mode 100644 index 00000000..03ed1601 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/dlclose-test.cc @@ -0,0 +1,81 @@ +// Regression test for +// http://code.google.com/p/address-sanitizer/issues/detail?id=19 +// Bug description: +// 1. application dlopens foo.so +// 2. asan registers all globals from foo.so +// 3. application dlcloses foo.so +// 4. application mmaps some memory to the location where foo.so was before +// 5. application starts using this mmaped memory, but asan still thinks there +// are globals. +// 6. BOOM + +// This sublte test assumes that after a foo.so is dlclose-d +// we can mmap the region of memory that has been occupied by the library. +// It works on i368/x86_64 Linux, but not necessary anywhere else. +// REQUIRES: x86_64-supported-target,i386-supported-target + +// RUN: %clangxx_asan -O0 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include +#include + +#include + +using std::string; + +typedef int *(fun_t)(); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + size_t PageSize = sysconf(_SC_PAGESIZE); + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *get = (fun_t*)dlsym(lib, "get_address_of_static_var"); + if (!get) { + printf("failed dlsym\n"); + return 1; + } + int *addr = get(); + assert(((size_t)addr % 32) == 0); // should be 32-byte aligned. + printf("addr: %p\n", addr); + addr[0] = 1; // make sure we can write there. + + // Now dlclose the shared library. + printf("attempting to dlclose\n"); + if (dlclose(lib)) { + printf("failed to dlclose\n"); + return 1; + } + // Now, the page where 'addr' is unmapped. Map it. + size_t page_beg = ((size_t)addr) & ~(PageSize - 1); + void *res = mmap((void*)(page_beg), PageSize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0); + if (res == (char*)-1L) { + printf("failed to mmap\n"); + return 1; + } + addr[1] = 2; // BOOM (if the bug is not fixed). + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/double-free.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/double-free.cc new file mode 100644 index 00000000..6bfd4fa2 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/double-free.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s + +#include +#include +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc]; + free(x); + free(x + argc - 1); // BOOM + // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0 + // CHECK: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-3]] + // CHECK: freed by thread T0 here: + // MALLOC-CTX: #0 0x{{.*}} in {{.*}}free + // MALLOC-CTX: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-7]] + // CHECK: allocated by thread T0 here: + // MALLOC-CTX: double-free.cc:[[@LINE-12]] + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/force_inline_opt0.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/force_inline_opt0.cc new file mode 100644 index 00000000..775a66df --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/force_inline_opt0.cc @@ -0,0 +1,14 @@ +// This test checks that we are no instrumenting a memory access twice +// (before and after inlining) +// RUN: %clangxx_asan -O1 %s -o %t && %t +// RUN: %clangxx_asan -O0 %s -o %t && %t +__attribute__((always_inline)) +void foo(int *x) { + *x = 0; +} + +int main() { + int x; + foo(&x); + return x; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/free_hook_realloc.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/free_hook_realloc.cc new file mode 100644 index 00000000..7a71964b --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/free_hook_realloc.cc @@ -0,0 +1,32 @@ +// Check that free hook doesn't conflict with Realloc. +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include +#include + +static void *glob_ptr; + +extern "C" { +void __asan_free_hook(void *ptr) { + if (ptr == glob_ptr) { + *(int*)ptr = 0; + write(1, "FreeHook\n", sizeof("FreeHook\n")); + } +} +} + +int main() { + int *x = (int*)malloc(100); + x[0] = 42; + glob_ptr = x; + int *y = (int*)realloc(x, 200); + // Verify that free hook was called and didn't spoil the memory. + if (y[0] != 42) { + _exit(1); + } + write(1, "Passed\n", sizeof("Passed\n")); + free(y); + // CHECK: FreeHook + // CHECK: Passed + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/global-demangle.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/global-demangle.cc new file mode 100644 index 00000000..d050b70f --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/global-demangle.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +namespace XXX { +class YYY { + public: + static char ZZZ[]; +}; +char YYY::ZZZ[] = "abc"; +} + +int main(int argc, char **argv) { + return (int)XXX::YYY::ZZZ[argc + 5]; // BOOM + // CHECK: {{READ of size 1 at 0x.*}} + // CHECK: {{0x.* is located 2 bytes to the right of global variable}} + // CHECK: 'XXX::YYY::ZZZ' {{.*}} of size 4 + // CHECK: 'XXX::YYY::ZZZ' is ascii string 'abc' +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/global-overflow.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/global-overflow.cc new file mode 100644 index 00000000..0f080f55 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/global-overflow.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +int main(int argc, char **argv) { + static char XXX[10]; + static char YYY[10]; + static char ZZZ[10]; + memset(XXX, 0, 10); + memset(YYY, 0, 10); + memset(ZZZ, 0, 10); + int res = YYY[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*global-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of global variable}} + // CHECK: {{.*YYY.* of size 10}} + res += XXX[argc] + ZZZ[argc]; + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/heap-overflow.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/heap-overflow.cc new file mode 100644 index 00000000..2c943a36 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/heap-overflow.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#include +#include +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*heap-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*heap-overflow.cc:9}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*heap-overflow.cc:9}} + free(x); + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/huge_negative_hea_oob.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/huge_negative_hea_oob.cc new file mode 100644 index 00000000..58a44c5f --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/huge_negative_hea_oob.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O %s -o %t && not %t 2>&1 | FileCheck %s +// Check that we can find huge buffer overflows to the left. +#include +#include +int main(int argc, char **argv) { + char *x = (char*)malloc(1 << 20); + memset(x, 0, 10); + int res = x[-argc * 4000]; // BOOOM + // CHECK: is located 4000 bytes to the left of + free(x); + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-atexit.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-atexit.cc new file mode 100644 index 00000000..e38cdd27 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-atexit.cc @@ -0,0 +1,31 @@ +// Test for the following situation: +// (1) global A is constructed. +// (2) exit() is called during construction of global B. +// (3) destructor of A reads uninitialized global C from another module. +// We do *not* want to report init-order bug in this case. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s + +#include +#include + +void AccessC(); + +class A { + public: + A() { } + ~A() { AccessC(); printf("PASSED\n"); } + // CHECK-NOT: AddressSanitizer + // CHECK: PASSED +}; + +A a; + +class B { + public: + B() { exit(1); } + ~B() { } +}; + +B b; diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-dlopen.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-dlopen.cc new file mode 100644 index 00000000..d30d1199 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-dlopen.cc @@ -0,0 +1,57 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=178 + +// Assume we're on Darwin and try to pass -U to the linker. If this flag is +// unsupported, don't use it. +// RUN: %clangxx_asan -O0 %p/SharedLibs/init-order-dlopen-so.cc \ +// RUN: -fPIC -shared -o %t-so.so -Wl,-U,_inc_global || \ +// RUN: %clangxx_asan -O0 %p/SharedLibs/init-order-dlopen-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// If the linker doesn't support --export-dynamic (which is ELF-specific), +// try to link without that option. +// FIXME: find a better solution. +// RUN: %clangxx_asan -O0 %s -o %t -Wl,--export-dynamic || \ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +#include + +using std::string; + +int foo() { + return 42; +} +int global = foo(); + +__attribute__((visibility("default"))) +extern "C" +void inc_global() { + global++; +} + +void *global_poller(void *arg) { + while (true) { + if (global != 42) + break; + usleep(100); + } + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + pthread_create(&p, 0, global_poller, 0); + string path = string(argv[0]) + "-so.so"; + if (0 == dlopen(path.c_str(), RTLD_NOW)) { + fprintf(stderr, "dlerror: %s\n", dlerror()); + return 1; + } + pthread_join(p, 0); + printf("PASSED\n"); + // CHECK: PASSED + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-pthread-create.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-pthread-create.cc new file mode 100644 index 00000000..52031216 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/init-order-pthread-create.cc @@ -0,0 +1,32 @@ +// Check that init-order checking is properly disabled if pthread_create is +// called. + +// RUN: %clangxx_asan %s %p/Helpers/init-order-pthread-create-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true %t + +#include +#include + +void *run(void *arg) { + return arg; +} + +void *foo(void *input) { + pthread_t t; + pthread_create(&t, 0, run, input); + void *res; + pthread_join(t, &res); + return res; +} + +void *bar(void *input) { + return input; +} + +void *glob = foo((void*)0x1234); +extern void *glob2; + +int main() { + printf("%p %p\n", glob, glob2); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-blacklist.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-blacklist.cc new file mode 100644 index 00000000..f40fcc08 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-blacklist.cc @@ -0,0 +1,32 @@ +// Test for blacklist functionality of initialization-order checker. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ +// RUN: %p/Helpers/initialization-blacklist-extra2.cc \ +// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +// Function is defined in another TU. +int readBadGlobal(); +int x = readBadGlobal(); // init-order bug. + +// Function is defined in another TU. +int accessBadObject(); +int y = accessBadObject(); // init-order bug. + +int readBadSrcGlobal(); +int z = readBadSrcGlobal(); // init-order bug. + +int main(int argc, char **argv) { + return argc + x + y + z - 1; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-bug.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-bug.cc new file mode 100644 index 00000000..fb289b1c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-bug.cc @@ -0,0 +1,45 @@ +// Test to make sure basic initialization order errors are caught. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true not %t 2>&1 | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186 +// XFAIL: darwin + +#include + +// The structure of the test is: +// "x", "y", "z" are dynamically initialized globals. +// Value of "x" depends on "y", value of "y" depends on "z". +// "x" and "z" are defined in this TU, "y" is defined in another one. +// Thus we shoud stably report initialization order fiasco independently of +// the translation unit order. + +int initZ() { + return 5; +} +int z = initZ(); + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // CHECK: {{AddressSanitizer: initialization-order-fiasco}} + // CHECK: {{READ of size .* at 0x.* thread T0}} + // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-constexpr.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-constexpr.cc new file mode 100644 index 00000000..65c95edd --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-constexpr.cc @@ -0,0 +1,31 @@ +// Constexpr: +// We need to check that a global variable initialized with a constexpr +// constructor can be accessed during dynamic initialization (as a constexpr +// constructor implies that it was initialized during constant initialization, +// not dynamic initialization). + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +class Integer { + private: + int value; + + public: + constexpr Integer(int x = 0) : value(x) {} + int getValue() {return value;} +}; +Integer coolestInteger(42); +int getCoolestInteger() { return coolestInteger.getValue(); } + +int main() { return 0; } diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-nobug.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-nobug.cc new file mode 100644 index 00000000..ed37d137 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/initialization-nobug.cc @@ -0,0 +1,48 @@ +// A collection of various initializers which shouldn't trip up initialization +// order checking. If successful, this will just return 0. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +// Simple access: +// Make sure that accessing a global in the same TU is safe + +bool condition = true; +int initializeSameTU() { + return condition ? 0x2a : 052; +} +int sameTU = initializeSameTU(); + +// Linker initialized: +// Check that access to linker initialized globals originating from a different +// TU's initializer is safe. + +int A = (1 << 1) + (1 << 3) + (1 << 5), B; +int getAB() { + return A * B; +} + +// Function local statics: +// Check that access to function local statics originating from a different +// TU's initializer is safe. + +int countCalls() { + static int calls; + return ++calls; +} + +// Trivial constructor, non-trivial destructor. +struct StructWithDtor { + ~StructWithDtor() { } + int value; +}; +StructWithDtor struct_with_dtor; +int getStructWithDtorValue() { return struct_with_dtor.value; } + +int main() { return 0; } diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/inline.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/inline.cc new file mode 100644 index 00000000..792aff59 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/inline.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O3 %s -o %t && %t + +// Test that no_sanitize_address attribute applies even when the function would +// be normally inlined. + +#include + +__attribute__((no_sanitize_address)) +int f(int *p) { + return *p; // BOOOM?? Nope! +} + +int main(int argc, char **argv) { + int * volatile x = (int*)malloc(2*sizeof(int) + 2); + int res = f(x + 2); + if (res) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/interface_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/interface_test.cc new file mode 100644 index 00000000..297b5526 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/interface_test.cc @@ -0,0 +1,8 @@ +// Check that user may include ASan interface header. +// RUN: %clang_asan %s -o %t && %t +// RUN: %clang %s -o %t && %t +#include + +int main() { + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/invalid-free.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/invalid-free.cc new file mode 100644 index 00000000..f940b501 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/invalid-free.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s + +#include +#include +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc]; + free(x + 5); // BOOM + // CHECK: AddressSanitizer: attempting free on address{{.*}}in thread T0 + // CHECK: invalid-free.cc:[[@LINE-2]] + // CHECK: is located 5 bytes inside of 10-byte region + // CHECK: allocated by thread T0 here: + // MALLOC-CTX: invalid-free.cc:[[@LINE-8]] + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/ioctl.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/ioctl.cc new file mode 100644 index 00000000..08ca688d --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/ioctl.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -O0 -g %s -o %t && %t +// RUN: %clangxx_asan -O3 -g %s -o %t && %t + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + int nonblock; + int res = ioctl(fd, FIONBIO, &nonblock + 1); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 4 at + // CHECK: {{#.* in main .*ioctl.cc:}}[[@LINE-3]] + assert(res == 0); + close(fd); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/large_func_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/large_func_test.cc new file mode 100644 index 00000000..0534bcd3 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/large_func_test.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#include +__attribute__((noinline)) +static void LargeFunction(int *x, int zero) { + x[0]++; + x[1]++; + x[2]++; + x[3]++; + x[4]++; + x[5]++; + x[6]++; + x[7]++; + x[8]++; + x[9]++; + + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + x[zero + 103]++; // we should report this exact line + // atos incorrectly extracts the symbol name for the static functions on + // Darwin. + // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]] + // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]] + + x[10]++; + x[11]++; + x[12]++; + x[13]++; + x[14]++; + x[15]++; + x[16]++; + x[17]++; + x[18]++; + x[19]++; +} + +int main(int argc, char **argv) { + int *x = new int[100]; + LargeFunction(x, argc - 1); + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-1]] + // CHECK: {{0x.* is located 12 bytes to the right of 400-byte region}} + // CHECK: {{allocated by thread T0 here:}} + // CHECK-Linux: {{ #0 0x.* in operator new.*}} + // CHECK-Darwin: {{ #0 0x.* in .*_Zna.*}} + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-7]] + delete x; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/log-path_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/log-path_test.cc new file mode 100644 index 00000000..1072670f --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/log-path_test.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_asan %s -o %t + +// Regular run. +// RUN: not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out + +// Good log_path. +// RUN: rm -f %t.log.* +// RUN: ASAN_OPTIONS=log_path=%t.log not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.* + +// Invalid log_path. +// RUN: ASAN_OPTIONS=log_path=/INVALID not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-INVALID < %t.out + +// Too long log_path. +// RUN: ASAN_OPTIONS=log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \ +// RUN: not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-LONG < %t.out + +// Run w/o errors should not produce any log. +// RUN: rm -f %t.log.* +// RUN: ASAN_OPTIONS=log_path=%t.log %t ARG ARG ARG +// RUN: not cat %t.log.* + + +#include +#include +int main(int argc, char **argv) { + if (argc > 2) return 0; + char *x = (char*)malloc(10); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + free(x); + return res; +} +// CHECK-ERROR: ERROR: AddressSanitizer +// CHECK-INVALID: ERROR: Can't open file: /INVALID +// CHECK-LONG: ERROR: Path is too long: 01234 diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/log_path_fork_test.cc.disabled b/projects/compiler-rt/lib/asan/lit_tests/TestCases/log_path_fork_test.cc.disabled new file mode 100644 index 00000000..c6c1b49e --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/log_path_fork_test.cc.disabled @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan %s -o %t +// RUN: rm -f %t.log.* +// Set verbosity to 1 so that the log files are opened prior to fork(). +// RUN: ASAN_OPTIONS="log_path=%t.log verbosity=1" not %t 2> %t.out +// RUN: for f in %t.log.* ; do FileCheck %s < $f; done +// RUN: [ `ls %t.log.* | wc -l` == 2 ] + +#include +#include +#include + +int main(int argc, char **argv) { + void *x = malloc(10); + free(x); + if (fork() == -1) return 1; + // There are two processes at this point, thus there should be two distinct + // error logs. + free(x); + return 0; +} + +// CHECK: ERROR: AddressSanitizer diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/lsan_annotations.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/lsan_annotations.cc new file mode 100644 index 00000000..c55ab869 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/lsan_annotations.cc @@ -0,0 +1,16 @@ +// Check that LSan annotations work fine. +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O3 %s -o %t && %t + +#include +#include + +int main() { + int *x = new int; + __lsan_ignore_object(x); + { + __lsan::ScopedDisabler disabler; + double *y = new double; + } + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_context_size.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_context_size.cc new file mode 100644 index 00000000..266ce66f --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_context_size.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os + +int main() { + char *x = new char[20]; + delete[] x; + return x[0]; + // We need to keep duplicate lines with different 'CHECK-%os' prefixes, + // otherwise FileCheck barks on missing 'CHECK-%os' before 'CHECK-%os-NEXT'. + + // CHECK-Linux: freed by thread T{{.*}} here: + // CHECK-Linux-NEXT: #0 0x{{.*}} in operator delete[] + // CHECK-Darwin: freed by thread T{{.*}} here: + // CHECK-Darwin-NEXT: #0 0x{{.*}} in wrap__ZdaPv + // CHECK-NOT: #1 0x{{.*}} + + // CHECK-Linux: previously allocated by thread T{{.*}} here: + // CHECK-Linux-NEXT: #0 0x{{.*}} in operator new[] + // CHECK-Darwin: previously allocated by thread T{{.*}} here: + // CHECK-Darwin-NEXT: #0 0x{{.*}} in wrap__Znam + // CHECK-NOT: #1 0x{{.*}} + + // CHECK: SUMMARY: AddressSanitizer: heap-use-after-free +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_fill.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_fill.cc new file mode 100644 index 00000000..57f50d14 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_fill.cc @@ -0,0 +1,22 @@ +// Check that we fill malloc-ed memory correctly. +// RUN: %clangxx_asan %s -o %t +// RUN: %t | FileCheck %s +// RUN: ASAN_OPTIONS=max_malloc_fill_size=10:malloc_fill_byte=8 %t | FileCheck %s --check-prefix=CHECK-10-8 +// RUN: ASAN_OPTIONS=max_malloc_fill_size=20:malloc_fill_byte=171 %t | FileCheck %s --check-prefix=CHECK-20-ab + +#include +int main(int argc, char **argv) { + // With asan allocator this makes sure we get memory from mmap. + static const int kSize = 1 << 25; + unsigned char *x = new unsigned char[kSize]; + printf("-"); + for (int i = 0; i <= 32; i++) { + printf("%02x", x[i]); + } + printf("-\n"); + delete [] x; +} + +// CHECK: -bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe- +// CHECK-10-8: -080808080808080808080000000000000000000000000000000000000000000000- +// CHECK-20-ab: -abababababababababababababababababababab00000000000000000000000000- diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_hook.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_hook.cc new file mode 100644 index 00000000..83be1020 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/malloc_hook.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include +#include + +extern "C" { +bool __asan_get_ownership(const void *p); + +void *global_ptr; + +// Note: avoid calling functions that allocate memory in malloc/free +// to avoid infinite recursion. +void __asan_malloc_hook(void *ptr, size_t sz) { + if (__asan_get_ownership(ptr)) { + write(1, "MallocHook\n", sizeof("MallocHook\n")); + global_ptr = ptr; + } +} +void __asan_free_hook(void *ptr) { + if (__asan_get_ownership(ptr) && ptr == global_ptr) + write(1, "FreeHook\n", sizeof("FreeHook\n")); +} +} // extern "C" + +int main() { + volatile int *x = new int; + // CHECK: MallocHook + // Check that malloc hook was called with correct argument. + if (global_ptr != (void*)x) { + _exit(1); + } + *x = 0; + delete x; + // CHECK: FreeHook + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/memcmp_strict_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/memcmp_strict_test.cc new file mode 100644 index 00000000..e06a8c7e --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/memcmp_strict_test.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=0 %t +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=1 not %t 2>&1 | FileCheck %s +// Default to strict_memcmp=1. +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include +int main() { + char kFoo[] = "foo"; + char kFubar[] = "fubar"; + int res = memcmp(kFoo, kFubar, strlen(kFubar)); + printf("res: %d\n", res); + // CHECK: AddressSanitizer: stack-buffer-overflow + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/memcmp_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/memcmp_test.cc new file mode 100644 index 00000000..758311dd --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/memcmp_test.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// REQUIRES: compiler-rt-optimized + +#include +int main(int argc, char **argv) { + char a1[] = {argc, 2, 3, 4}; + char a2[] = {1, 2*argc, 3, 4}; + int res = memcmp(a1, a2, 4 + argc); // BOOM + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{#0.*memcmp}} + // CHECK: {{#1.*main}} + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/null_deref.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/null_deref.cc new file mode 100644 index 00000000..47641832 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/null_deref.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: SEGV on unknown address + // CHECK: {{0x0*00028 .*pc 0x.*}} + ptr[10]++; // BOOM + // atos on Mac cannot extract the symbol name correctly. + // CHECK-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:}}[[@LINE-2]] + // CHECK-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]] +} +int main() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in main.*null_deref.cc:}}[[@LINE-1]] + // CHECK: {{AddressSanitizer can not provide additional info.}} +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/on_error_callback.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/on_error_callback.cc new file mode 100644 index 00000000..d0cec2eb --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/on_error_callback.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include + +extern "C" +void __asan_on_error() { + fprintf(stderr, "__asan_on_error called"); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: __asan_on_error called +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/partial_right.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/partial_right.cc new file mode 100644 index 00000000..a000a913 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/partial_right.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +int main(int argc, char **argv) { + volatile int *x = (int*)malloc(2*sizeof(int) + 2); + int res = x[2]; // BOOOM + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: [[ADDR:0x[01-9a-fa-f]+]] is located 0 bytes to the right of {{.*}}-byte region [{{.*}},{{.*}}[[ADDR]]) + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/poison_partial.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/poison_partial.cc new file mode 100644 index 00000000..f7c48bf5 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/poison_partial.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: not %t heap 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=poison_partial=0 %t +// RUN: ASAN_OPTIONS=poison_partial=0 %t heap +#include +char g[21]; +char *x; + +int main(int argc, char **argv) { + if (argc >= 2) + x = new char[21]; + else + x = &g[0]; + memset(x, 0, 21); + int *y = (int*)x; + return y[5]; +} +// CHECK: 0 bytes to the right diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/print_summary.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/print_summary.cc new file mode 100644 index 00000000..949c9b54 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/print_summary.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=YES +// RUN: ASAN_OPTIONS=print_summary=false not %t 2>&1 | FileCheck %s --check-prefix=NO + +int main() { + char *x = new char[20]; + delete[] x; + return x[0]; + // YES: ERROR: AddressSanitizer: heap-use-after-free + // YES: SUMMARY: AddressSanitizer: heap-use-after-free + // NO: ERROR: AddressSanitizer: heap-use-after-free + // NO-NOT: SUMMARY: AddressSanitizer: heap-use-after-free +} + diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/readv.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/readv.cc new file mode 100644 index 00000000..ba17505f --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/readv.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O0 %s -DPOSITIVE -o %t && not %t 2>&1 | FileCheck %s + +// Test the readv() interceptor. + +#include +#include +#include +#include +#include +#include +#include + +int main() { + char buf[2011]; + struct iovec iov[2]; +#ifdef POSITIVE + char * volatile buf_ = buf; + iov[0].iov_base = buf_ - 1; +#else + iov[0].iov_base = buf + 1; +#endif + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/etc/hosts", O_RDONLY); + assert(fd > 0); + readv(fd, iov, 2); + // CHECK: WRITE of size 5 at + close(fd); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/sanity_check_pure_c.c b/projects/compiler-rt/lib/asan/lit_tests/TestCases/sanity_check_pure_c.c new file mode 100644 index 00000000..df150675 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/sanity_check_pure_c.c @@ -0,0 +1,19 @@ +// Sanity checking a test in pure C. +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// Sanity checking a test in pure C with -pie. +// RUN: %clang_asan -O2 %s -pie -o %t +// RUN: not %t 2>&1 | FileCheck %s + +#include +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: heap-use-after-free + // CHECK: free + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-4]] + // CHECK: malloc + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-7]] +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/shared-lib-test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/shared-lib-test.cc new file mode 100644 index 00000000..126903a5 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/shared-lib-test.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_asan -O0 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include +#include + +#include + +using std::string; + +typedef void (fun_t)(int x); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc = (fun_t*)dlsym(lib, "inc"); + if (!inc) return 1; + printf("ok\n"); + inc(1); + inc(-1); // BOOM + // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: {{ #0 0x.*}} + // CHECK: {{ #1 0x.* in main .*shared-lib-test.cc:}}[[@LINE-4]] + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/sleep_before_dying.c b/projects/compiler-rt/lib/asan/lit_tests/TestCases/sleep_before_dying.c new file mode 100644 index 00000000..8dee9f27 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/sleep_before_dying.c @@ -0,0 +1,10 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS="sleep_before_dying=1" not %t 2>&1 | FileCheck %s + +#include +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: Sleeping for 1 second +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-buffer-overflow-with-position.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-buffer-overflow-with-position.cc new file mode 100644 index 00000000..91820db0 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-buffer-overflow-with-position.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %t -2 2>&1 | FileCheck --check-prefix=CHECK-m2 %s +// RUN: not %t -1 2>&1 | FileCheck --check-prefix=CHECK-m1 %s +// RUN: %t 0 +// RUN: %t 8 +// RUN: not %t 9 2>&1 | FileCheck --check-prefix=CHECK-9 %s +// RUN: not %t 10 2>&1 | FileCheck --check-prefix=CHECK-10 %s +// RUN: not %t 62 2>&1 | FileCheck --check-prefix=CHECK-62 %s +// RUN: not %t 63 2>&1 | FileCheck --check-prefix=CHECK-63 %s +// RUN: not %t 63 2>&1 | FileCheck --check-prefix=CHECK-63 %s +// RUN: not %t 73 2>&1 | FileCheck --check-prefix=CHECK-73 %s +// RUN: not %t 74 2>&1 | FileCheck --check-prefix=CHECK-74 %s +// RUN: not %t 126 2>&1 | FileCheck --check-prefix=CHECK-126 %s +// RUN: not %t 127 2>&1 | FileCheck --check-prefix=CHECK-127 %s +// RUN: not %t 137 2>&1 | FileCheck --check-prefix=CHECK-137 %s +// RUN: not %t 138 2>&1 | FileCheck --check-prefix=CHECK-138 %s +#include +#include +#include +#include +int main(int argc, char **argv) { + assert(argc >= 2); + int idx = atoi(argv[1]); + char AAA[10], BBB[10], CCC[10]; + memset(AAA, 0, sizeof(AAA)); + memset(BBB, 0, sizeof(BBB)); + memset(CCC, 0, sizeof(CCC)); + int res = 0; + char *p = AAA + idx; + printf("AAA: %p\ny: %p\nz: %p\np: %p\n", AAA, BBB, CCC, p); + // make sure BBB and CCC are not removed; + return *(short*)(p) + BBB[argc % 2] + CCC[argc % 2]; +} +// CHECK-m2: 'AAA' <== Memory access at offset 30 underflows this variable +// CHECK-m1: 'AAA' <== Memory access at offset 31 partially underflows this variable +// CHECK-9: 'AAA' <== Memory access at offset 41 partially overflows this variable +// CHECK-10: 'AAA' <== Memory access at offset 42 overflows this variable +// CHECK-62: 'BBB' <== Memory access at offset 94 underflows this variable +// CHECK-63: 'BBB' <== Memory access at offset 95 partially underflows this variable +// CHECK-73: 'BBB' <== Memory access at offset 105 partially overflows this variable +// CHECK-74: 'BBB' <== Memory access at offset 106 overflows this variable +// CHECK-126: 'CCC' <== Memory access at offset 158 underflows this variable +// CHECK-127: 'CCC' <== Memory access at offset 159 partially underflows this variable +// CHECK-137: 'CCC' <== Memory access at offset 169 partially overflows this variable +// CHECK-138: 'CCC' <== Memory access at offset 170 overflows this variable diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-frame-demangle.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-frame-demangle.cc new file mode 100644 index 00000000..2b83ecc2 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-frame-demangle.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include + +namespace XXX { +struct YYY { + static int ZZZ(int x) { + char array[10]; + memset(array, 0, 10); + return array[x]; // BOOOM + // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 1 at + // CHECK: is located in stack of thread T0 at offset + // CHECK: XXX::YYY::ZZZ + } +}; +} // namespace XXX + +int main(int argc, char **argv) { + int res = XXX::YYY::ZZZ(argc + 10); + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-oob-frames.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-oob-frames.cc new file mode 100644 index 00000000..909e700b --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-oob-frames.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -O1 %s -o %t +// RUN: not %t 0 2>&1 | FileCheck %s --check-prefix=CHECK0 +// RUN: not %t 1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: not %t 2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: not %t 3 2>&1 | FileCheck %s --check-prefix=CHECK3 + +#define NOINLINE __attribute__((noinline)) +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { + char s[4] = {0}; + char *d = s; + break_optimization(&d); + switch (frame) { + case 3: a[5]++; break; + case 2: b[5]++; break; + case 1: c[5]++; break; + case 0: d[5]++; break; + } +} +NOINLINE static void Frame1(int frame, char *a, char *b) { + char c[4] = {0}; Frame0(frame, a, b, c); + break_optimization(0); +} +NOINLINE static void Frame2(int frame, char *a) { + char b[4] = {0}; Frame1(frame, a, b); + break_optimization(0); +} +NOINLINE static void Frame3(int frame) { + char a[4] = {0}; Frame2(frame, a); + break_optimization(0); +} + +int main(int argc, char **argv) { + if (argc != 2) return 1; + Frame3(argv[1][0] - '0'); +} + +// CHECK0: AddressSanitizer: stack-buffer-overflow +// CHECK0: #0{{.*}}Frame0 +// CHECK0: #1{{.*}}Frame1 +// CHECK0: #2{{.*}}Frame2 +// CHECK0: #3{{.*}}Frame3 +// CHECK0: is located in stack of thread T0 at offset +// CHECK0-NEXT: #0{{.*}}Frame0 +// +// CHECK1: AddressSanitizer: stack-buffer-overflow +// CHECK1: is located in stack of thread T0 at offset +// CHECK1-NEXT: #0{{.*}}Frame1 +// +// CHECK2: AddressSanitizer: stack-buffer-overflow +// CHECK2: is located in stack of thread T0 at offset +// CHECK2-NEXT: #0{{.*}}Frame2 +// +// CHECK3: AddressSanitizer: stack-buffer-overflow +// CHECK3: is located in stack of thread T0 at offset +// CHECK3-NEXT: #0{{.*}}Frame3 diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-overflow.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-overflow.cc new file mode 100644 index 00000000..adf1c078 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-overflow.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*stack-overflow.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} + // CHECK-NEXT: in{{.*}}main{{.*}}stack-overflow.cc + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-use-after-return.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-use-after-return.cc new file mode 100644 index 00000000..5ed42a8c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/stack-use-after-return.cc @@ -0,0 +1,77 @@ +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=detect_stack_use_after_return=0 %t +// Regression test for a CHECK failure with small stack size and large frame. +// RUN: %clangxx_asan -O3 %s -o %t -DkSize=10000 && \ +// RUN: (ulimit -s 65; not %t) 2>&1 | FileCheck %s +// +// Test that we can find UAR in a thread other than main: +// RUN: %clangxx_asan -DUseThread -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck --check-prefix=THREAD %s +// +// Test the uar_stack_size_log flag. +// +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:uar_stack_size_log=20:verbosity=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-20 %s +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:uar_stack_size_log=24:verbosity=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-24 %s + +#include +#include + +#ifndef kSize +# define kSize 1 +#endif + +#ifndef UseThread +# define UseThread 0 +#endif + +__attribute__((noinline)) +char *Ident(char *x) { + fprintf(stderr, "1: %p\n", x); + return x; +} + +__attribute__((noinline)) +char *Func1() { + char local[kSize]; + return Ident(local); +} + +__attribute__((noinline)) +void Func2(char *x) { + fprintf(stderr, "2: %p\n", x); + *x = 1; + // CHECK: WRITE of size 1 {{.*}} thread T0 + // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]] + // CHECK: is located in stack of thread T0 at offset + // CHECK: 'local' <== Memory access at offset 32 is inside this variable + // THREAD: WRITE of size 1 {{.*}} thread T{{[1-9]}} + // THREAD: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-6]] + // THREAD: is located in stack of thread T{{[1-9]}} at offset + // THREAD: 'local' <== Memory access at offset 32 is inside this variable + // CHECK-20: T0: FakeStack created:{{.*}} stack_size_log: 20 + // CHECK-24: T0: FakeStack created:{{.*}} stack_size_log: 24 +} + +void *Thread(void *unused) { + Func2(Func1()); + return NULL; +} + +int main(int argc, char **argv) { +#if UseThread + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +#else + Func2(Func1()); +#endif + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/strdup_oob_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strdup_oob_test.cc new file mode 100644 index 00000000..e92afd3c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strdup_oob_test.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include + +char kString[] = "foo"; + +int main(int argc, char **argv) { + char *copy = strdup(kString); + int x = copy[4 + argc]; // BOOM + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: #0 {{.*}}main {{.*}}strdup_oob_test.cc:[[@LINE-2]] + // CHECK: allocated by thread T{{.*}} here: + // CHECK: #0 {{.*}}strdup + // CHECK: strdup_oob_test.cc:[[@LINE-6]] + return x; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/strerror_r_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strerror_r_test.cc new file mode 100644 index 00000000..0df009b8 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strerror_r_test.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t + +// Regression test for PR17138. + +#include +#include + +int main() { + char buf[1024]; + char *res = (char *)strerror_r(300, buf, sizeof(buf)); + assert(res != 0); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/strip_path_prefix.c b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strip_path_prefix.c new file mode 100644 index 00000000..c4d6ba49 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strip_path_prefix.c @@ -0,0 +1,12 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS="strip_path_prefix='/'" not %t 2>&1 | FileCheck %s + +#include +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // Check that paths in error report don't start with slash. + // CHECK: heap-use-after-free + // CHECK-NOT: #0 0x{{.*}} ({{[/].*}}) +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/strncpy-overflow.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strncpy-overflow.cc new file mode 100644 index 00000000..f91e191f --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/strncpy-overflow.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +// REQUIRES: compiler-rt-optimized + +#include +#include +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + char *short_buffer = (char*)malloc(9); + strncpy(short_buffer, hello, 10); // BOOM + // CHECK: {{WRITE of size 10 at 0x.* thread T0}} + // CHECK-Linux: {{ #0 0x.* in .*strncpy}} + // CHECK-Darwin: {{ #0 0x.* in wrap_strncpy}} + // CHECK: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-4]] + // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-13]] + return short_buffer[8]; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/symbolize_callback.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/symbolize_callback.cc new file mode 100644 index 00000000..058b3150 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/symbolize_callback.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include + +extern "C" +bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { + snprintf(out_buffer, out_size, "MySymbolizer"); + return true; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: MySymbolizer +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/throw_call_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/throw_call_test.cc new file mode 100644 index 00000000..974bc51d --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/throw_call_test.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_asan %s -o %t && %t +// http://code.google.com/p/address-sanitizer/issues/detail?id=147 (not fixed). +// BROKEN: %clangxx_asan %s -o %t -static-libstdc++ && %t +#include +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline, no_sanitize_address)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + if (zero == 0) + throw 42; +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/throw_invoke_test.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/throw_invoke_test.cc new file mode 100644 index 00000000..077a940e --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/throw_invoke_test.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_asan %s -o %t && %t +// RUN: %clangxx_asan %s -o %t -static-libstdc++ && %t +#include +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + try { + if (zero == 0) + throw 42; + else if (zero == 1) + throw 1.; + } catch(double x) { + } +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} + diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/time_interceptor.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/time_interceptor.cc new file mode 100644 index 00000000..3be00d60 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/time_interceptor.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +// Test the time() interceptor. + +#include +#include +#include + +int main() { + time_t *tm = (time_t*)malloc(sizeof(time_t)); + free(tm); + time_t t = time(tm); + printf("Time: %s\n", ctime(&t)); // NOLINT + // CHECK: use-after-free + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc new file mode 100644 index 00000000..c967531c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/uar_and_exceptions.cc @@ -0,0 +1,40 @@ +// Test that use-after-return works with exceptions. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && %t + +#include + +volatile char *g; + +#ifndef FRAME_SIZE +# define FRAME_SIZE 100 +#endif + +#ifndef NUM_ITER +# define NUM_ITER 4000 +#endif + +#ifndef DO_THROW +# define DO_THROW 1 +#endif + +void Func(int depth) { + char frame[FRAME_SIZE]; + g = &frame[0]; + if (depth) + Func(depth - 1); + else if (DO_THROW) + throw 1; +} + +int main(int argc, char **argv) { + for (int i = 0; i < NUM_ITER; i++) { + try { + Func(argc * 100); + } catch(...) { + } + if ((i % (NUM_ITER / 10)) == 0) + fprintf(stderr, "done [%d]\n", i); + } + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/unaligned_loads_and_stores.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/unaligned_loads_and_stores.cc new file mode 100644 index 00000000..d50566c4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/unaligned_loads_and_stores.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t A 2>&1 | FileCheck --check-prefix=CHECK-A %s +// RUN: not %t B 2>&1 | FileCheck --check-prefix=CHECK-B %s +// RUN: not %t C 2>&1 | FileCheck --check-prefix=CHECK-C %s +// RUN: not %t D 2>&1 | FileCheck --check-prefix=CHECK-D %s +// RUN: not %t E 2>&1 | FileCheck --check-prefix=CHECK-E %s + +// RUN: not %t K 2>&1 | FileCheck --check-prefix=CHECK-K %s +// RUN: not %t L 2>&1 | FileCheck --check-prefix=CHECK-L %s +// RUN: not %t M 2>&1 | FileCheck --check-prefix=CHECK-M %s +// RUN: not %t N 2>&1 | FileCheck --check-prefix=CHECK-N %s +// RUN: not %t O 2>&1 | FileCheck --check-prefix=CHECK-O %s + +#include + +#include +#include +int main(int argc, char **argv) { + if (argc != 2) return 1; + char *x = new char[16]; + memset(x, 0xab, 16); + int res = 1; + switch (argv[1][0]) { + case 'A': res = __sanitizer_unaligned_load16(x + 15); break; +// CHECK-A ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-A: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-A: is located 0 bytes to the right of 16-byte region + case 'B': res = __sanitizer_unaligned_load32(x + 14); break; +// CHECK-B: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'C': res = __sanitizer_unaligned_load32(x + 13); break; +// CHECK-C: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'D': res = __sanitizer_unaligned_load64(x + 15); break; +// CHECK-D: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'E': res = __sanitizer_unaligned_load64(x + 9); break; +// CHECK-E: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + + case 'K': __sanitizer_unaligned_store16(x + 15, 0); break; +// CHECK-K ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-K: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-K: is located 0 bytes to the right of 16-byte region + case 'L': __sanitizer_unaligned_store32(x + 15, 0); break; +// CHECK-L: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'M': __sanitizer_unaligned_store32(x + 13, 0); break; +// CHECK-M: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'N': __sanitizer_unaligned_store64(x + 10, 0); break; +// CHECK-N: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'O': __sanitizer_unaligned_store64(x + 14, 0); break; +// CHECK-O: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + } + delete x; + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-free-right.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-free-right.cc new file mode 100644 index 00000000..88d91f53 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-free-right.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +// Test use-after-free report in the case when access is at the right border of +// the allocation. + +#include +int main() { + volatile char *x = (char*)malloc(sizeof(char)); + free((void*)x); + *x = 42; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{WRITE of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free-right.cc:13}} + // CHECK: {{0x.* is located 0 bytes inside of 1-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:12}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:12}} + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:11}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:11}} +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-free.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-free.cc new file mode 100644 index 00000000..84ba479c --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-free.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#include +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free.cc:10}} + // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:9}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:9}} + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:8}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:8}} +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-poison.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-poison.cc new file mode 100644 index 00000000..e3bc6ece --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-poison.cc @@ -0,0 +1,20 @@ +// Check that __asan_poison_memory_region works. +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// +// Check that we can disable it +// RUN: ASAN_OPTIONS=allow_user_poisoning=0 %t + +#include + +extern "C" void __asan_poison_memory_region(void *, size_t); + +int main(int argc, char **argv) { + char *x = new char[16]; + x[10] = 0; + __asan_poison_memory_region(x, 16); + int res = x[argc * 10]; // BOOOM + // CHECK: ERROR: AddressSanitizer: use-after-poison + // CHECK: main{{.*}}use-after-poison.cc:[[@LINE-2]] + delete [] x; + return res; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-dtor-order.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-dtor-order.cc new file mode 100644 index 00000000..32fa6ad8 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-dtor-order.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +#include + +struct IntHolder { + explicit IntHolder(int *val = 0) : val_(val) { } + ~IntHolder() { + printf("Value: %d\n", *val_); // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in IntHolder::~IntHolder{{.*}}use-after-scope-dtor-order.cc:[[@LINE-2]] + } + void set(int *val) { val_ = val; } + int *get() { return val_; } + + int *val_; +}; + +int main(int argc, char *argv[]) { + // It is incorrect to use "x" int IntHolder destructor, because "x" is + // "destroyed" earlier as it's declared later. + IntHolder holder; + int x = argc; + holder.set(&x); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-inlined.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-inlined.cc new file mode 100644 index 00000000..0bad048e --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-inlined.cc @@ -0,0 +1,27 @@ +// Test with "-O2" only to make sure inlining (leading to use-after-scope) +// happens. "always_inline" is not enough, as Clang doesn't emit +// llvm.lifetime intrinsics at -O0. +// +// RUN: %clangxx_asan -O2 -fsanitize=use-after-scope %s -o %t && not %t 2>&1 | FileCheck %s + +int *arr; + +__attribute__((always_inline)) +void inlined(int arg) { + int x[5]; + for (int i = 0; i < arg; i++) x[i] = i; + arr = x; +} + +int main(int argc, char *argv[]) { + inlined(argc); + return arr[argc - 1]; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: READ of size 4 at 0x{{.*}} thread T0 + // CHECK: #0 0x{{.*}} in main + // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]] + // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset + // CHECK: [[OFFSET:[^ ]*]] in frame + // CHECK: main + // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-nobug.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-nobug.cc new file mode 100644 index 00000000..c23acf76 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-nobug.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && %t + +#include + +int main() { + int *p = 0; + // Variable goes in and out of scope. + for (int i = 0; i < 3; i++) { + int x = 0; + p = &x; + } + printf("PASSED\n"); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-temp.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-temp.cc new file mode 100644 index 00000000..13d714f9 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope-temp.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: %t 2>&1 | FileCheck %s +// +// Lifetime for temporaries is not emitted yet. +// XFAIL: * + +#include + +struct IntHolder { + explicit IntHolder(int val) : val(val) { + printf("IntHolder: %d\n", val); + } + int val; +}; + +const IntHolder *saved; + +void save(const IntHolder &holder) { + saved = &holder; +} + +int main(int argc, char *argv[]) { + save(IntHolder(10)); + int x = saved->val; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-temp.cc:[[@LINE-2]] + printf("saved value: %d\n", x); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope.cc new file mode 100644 index 00000000..c46c9594 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/use-after-scope.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS="detect_stack_use_after_return=1" not %t 2>&1 | FileCheck %s + +int main() { + int *p = 0; + { + int x = 0; + p = &x; + } + return *p; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope.cc:[[@LINE-2]] + // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame + // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/TestCases/wait.cc b/projects/compiler-rt/lib/asan/lit_tests/TestCases/wait.cc new file mode 100644 index 00000000..b5580dc8 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/TestCases/wait.cc @@ -0,0 +1,63 @@ +// RUN: %clangxx_asan -DWAIT -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAITPID -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAITPID -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAITID -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAITID -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3 -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3 -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4 -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4 -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O3 %s -o %t && not %t 2>&1 | FileCheck %s + + +#include +#include +#include + +int main(int argc, char **argv) { + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; +#if defined(WAIT) + res = wait(status); +#elif defined(WAITPID) + res = waitpid(pid, status, WNOHANG); +#elif defined(WAITID) + siginfo_t *si = (siginfo_t*)(x + argc * 3); + res = waitid(P_ALL, 0, si, WEXITED | WNOHANG); +#elif defined(WAIT3) + res = wait3(status, WNOHANG, NULL); +#elif defined(WAIT4) + res = wait4(pid, status, WNOHANG, NULL); +#elif defined(WAIT3_RUSAGE) || defined(WAIT4_RUSAGE) + struct rusage *ru = (struct rusage*)(x + argc * 3); + int good_status; +# if defined(WAIT3_RUSAGE) + res = wait3(&good_status, WNOHANG, ru); +# elif defined(WAIT4_RUSAGE) + res = wait4(pid, &good_status, WNOHANG, ru); +# endif +#endif + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*wait}} + // CHECK: {{in main .*wait.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in main}} + return res != -1; + } + // child + return 0; +} diff --git a/projects/compiler-rt/lib/asan/lit_tests/Unit/lit.site.cfg.in b/projects/compiler-rt/lib/asan/lit_tests/Unit/lit.site.cfg.in new file mode 100644 index 00000000..a45870c9 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/Unit/lit.site.cfg.in @@ -0,0 +1,16 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") + +# Setup config name. +config.name = 'AddressSanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with ASan unit tests. +config.test_exec_root = "@ASAN_BINARY_DIR@/tests" +config.test_source_root = config.test_exec_root + +if config.host_os == 'Linux': + config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' diff --git a/projects/compiler-rt/lib/asan/lit_tests/lit.cfg b/projects/compiler-rt/lib/asan/lit_tests/lit.cfg new file mode 100644 index 00000000..71a700c6 --- /dev/null +++ b/projects/compiler-rt/lib/asan/lit_tests/lit.cfg @@ -0,0 +1,95 @@ +# -*- Python -*- + +import os + +import lit.util + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'AddressSanitizer' + config.bits + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +def DisplayNoConfigMessage(): + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-asan") + +# Figure out LLVM source root. +llvm_src_root = getattr(config, 'llvm_src_root', None) +if llvm_src_root is None: + # We probably haven't loaded the site-specific configuration: the user + # is likely trying to run a test file directly, and the site configuration + # wasn't created by the build system. + asan_site_cfg = lit_config.params.get('asan_site_config', None) + if (asan_site_cfg) and (os.path.exists(asan_site_cfg)): + lit_config.load_config(config, asan_site_cfg) + raise SystemExit + + # Try to guess the location of site-specific configuration using llvm-config + # util that can point where the build tree is. + llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) + if not llvm_config: + DisplayNoConfigMessage() + + # Find out the presumed location of generated site config. + llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() + asan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", + "lib", "asan", "lit_tests", "lit.site.cfg") + if (not asan_site_cfg) or (not os.path.exists(asan_site_cfg)): + DisplayNoConfigMessage() + + lit_config.load_config(config, asan_site_cfg) + raise SystemExit + +# Setup default compiler flags used with -fsanitize=address option. +# FIXME: Review the set of required flags and check if it can be reduced. +bits_cflag = " -m" + config.bits +clang_asan_cflags = (" -fsanitize=address" + + " -mno-omit-leaf-frame-pointer" + + " -fno-omit-frame-pointer" + + " -fno-optimize-sibling-calls" + + " -g" + + bits_cflag) +clang_asan_cxxflags = " --driver-mode=g++" + clang_asan_cflags +config.substitutions.append( ("%clang ", " " + config.clang + bits_cflag + " ")) +config.substitutions.append( ("%clangxx ", (" " + config.clang + + " --driver-mode=g++" + + bits_cflag + " ")) ) +config.substitutions.append( ("%clang_asan ", (" " + config.clang + " " + + clang_asan_cflags + " ")) ) +config.substitutions.append( ("%clangxx_asan ", (" " + config.clang + " " + + clang_asan_cxxflags + " ")) ) + +# Setup path to asan_symbolize.py script. +asan_source_dir = get_required_attr(config, "asan_source_dir") +asan_symbolize = os.path.join(asan_source_dir, "scripts", "asan_symbolize.py") +if not os.path.exists(asan_symbolize): + lit_config.fatal("Can't find script on path %r" % asan_symbolize) +python_exec = get_required_attr(config, "python_executable") +config.substitutions.append( ("%asan_symbolize", python_exec + " " + asan_symbolize + " ") ) + +# Define CHECK-%os to check for OS-dependent output. +config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) + +config.available_features.add("asan-" + config.bits + "-bits") + +# Turn on leak detection on 64-bit Linux. +if config.host_os == 'Linux' and config.bits == '64': + config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# AddressSanitizer tests are currently supported on Linux and Darwin only. +if config.host_os not in ['Linux', 'Darwin']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/asan/scripts/asan_symbolize.py b/projects/compiler-rt/lib/asan/scripts/asan_symbolize.py new file mode 100755 index 00000000..a398fcf1 --- /dev/null +++ b/projects/compiler-rt/lib/asan/scripts/asan_symbolize.py @@ -0,0 +1,392 @@ +#!/usr/bin/env python +#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +import bisect +import getopt +import os +import pty +import re +import subprocess +import sys +import termios + +llvm_symbolizer = None +symbolizers = {} +DEBUG = False +demangle = False; + + +# FIXME: merge the code that calls fix_filename(). +def fix_filename(file_name): + for path_to_cut in sys.argv[1:]: + file_name = re.sub('.*' + path_to_cut, '', file_name) + file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name) + file_name = re.sub('.*crtstuff.c:0', '???:0', file_name) + return file_name + + +class Symbolizer(object): + def __init__(self): + pass + + def symbolize(self, addr, binary, offset): + """Symbolize the given address (pair of binary and offset). + + Overriden in subclasses. + Args: + addr: virtual address of an instruction. + binary: path to executable/shared object containing this instruction. + offset: instruction offset in the @binary. + Returns: + list of strings (one string for each inlined frame) describing + the code locations for this instruction (that is, function name, file + name, line and column numbers). + """ + return None + + +class LLVMSymbolizer(Symbolizer): + def __init__(self, symbolizer_path): + super(LLVMSymbolizer, self).__init__() + self.symbolizer_path = symbolizer_path + self.pipe = self.open_llvm_symbolizer() + + def open_llvm_symbolizer(self): + if not os.path.exists(self.symbolizer_path): + return None + cmd = [self.symbolizer_path, + '--use-symbol-table=true', + '--demangle=%s' % demangle, + '--functions=true', + '--inlining=true'] + if DEBUG: + print ' '.join(cmd) + return subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + if not self.pipe: + return None + result = [] + try: + symbolizer_input = '%s %s' % (binary, offset) + if DEBUG: + print symbolizer_input + print >> self.pipe.stdin, symbolizer_input + while True: + function_name = self.pipe.stdout.readline().rstrip() + if not function_name: + break + file_name = self.pipe.stdout.readline().rstrip() + file_name = fix_filename(file_name) + if (not function_name.startswith('??') and + not file_name.startswith('??')): + # Append only valid frames. + result.append('%s in %s %s' % (addr, function_name, + file_name)) + except Exception: + result = [] + if not result: + result = None + return result + + +def LLVMSymbolizerFactory(system): + symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH') + if not symbolizer_path: + symbolizer_path = os.getenv('ASAN_SYMBOLIZER_PATH') + if not symbolizer_path: + # Assume llvm-symbolizer is in PATH. + symbolizer_path = 'llvm-symbolizer' + return LLVMSymbolizer(symbolizer_path) + + +class Addr2LineSymbolizer(Symbolizer): + def __init__(self, binary): + super(Addr2LineSymbolizer, self).__init__() + self.binary = binary + self.pipe = self.open_addr2line() + + def open_addr2line(self): + cmd = ['addr2line', '-f'] + if demangle: + cmd += ['--demangle'] + cmd += ['-e', self.binary] + if DEBUG: + print ' '.join(cmd) + return subprocess.Popen(cmd, + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + if self.binary != binary: + return None + try: + print >> self.pipe.stdin, offset + function_name = self.pipe.stdout.readline().rstrip() + file_name = self.pipe.stdout.readline().rstrip() + except Exception: + function_name = '' + file_name = '' + file_name = fix_filename(file_name) + return ['%s in %s %s' % (addr, function_name, file_name)] + + +class UnbufferedLineConverter(object): + """ + Wrap a child process that responds to each line of input with one line of + output. Uses pty to trick the child into providing unbuffered output. + """ + def __init__(self, args, close_stderr=False): + pid, fd = pty.fork() + if pid == 0: + # We're the child. Transfer control to command. + if close_stderr: + dev_null = os.open('/dev/null', 0) + os.dup2(dev_null, 2) + os.execvp(args[0], args) + else: + # Disable echoing. + attr = termios.tcgetattr(fd) + attr[3] = attr[3] & ~termios.ECHO + termios.tcsetattr(fd, termios.TCSANOW, attr) + # Set up a file()-like interface to the child process + self.r = os.fdopen(fd, "r", 1) + self.w = os.fdopen(os.dup(fd), "w", 1) + + def convert(self, line): + self.w.write(line + "\n") + return self.readline() + + def readline(self): + return self.r.readline().rstrip() + + +class DarwinSymbolizer(Symbolizer): + def __init__(self, addr, binary): + super(DarwinSymbolizer, self).__init__() + self.binary = binary + # Guess which arch we're running. 10 = len('0x') + 8 hex digits. + if len(addr) > 10: + self.arch = 'x86_64' + else: + self.arch = 'i386' + self.open_atos() + + def open_atos(self): + if DEBUG: + print 'atos -o %s -arch %s' % (self.binary, self.arch) + cmdline = ['atos', '-o', self.binary, '-arch', self.arch] + self.atos = UnbufferedLineConverter(cmdline, close_stderr=True) + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + if self.binary != binary: + return None + atos_line = self.atos.convert('0x%x' % int(offset, 16)) + while "got symbolicator for" in atos_line: + atos_line = self.atos.readline() + # A well-formed atos response looks like this: + # foo(type1, type2) (in object.name) (filename.cc:80) + match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line) + if DEBUG: + print 'atos_line: ', atos_line + if match: + function_name = match.group(1) + function_name = re.sub('\(.*?\)', '', function_name) + file_name = fix_filename(match.group(3)) + return ['%s in %s %s' % (addr, function_name, file_name)] + else: + return ['%s in %s' % (addr, atos_line)] + + +# Chain several symbolizers so that if one symbolizer fails, we fall back +# to the next symbolizer in chain. +class ChainSymbolizer(Symbolizer): + def __init__(self, symbolizer_list): + super(ChainSymbolizer, self).__init__() + self.symbolizer_list = symbolizer_list + + def symbolize(self, addr, binary, offset): + """Overrides Symbolizer.symbolize.""" + for symbolizer in self.symbolizer_list: + if symbolizer: + result = symbolizer.symbolize(addr, binary, offset) + if result: + return result + return None + + def append_symbolizer(self, symbolizer): + self.symbolizer_list.append(symbolizer) + + +def BreakpadSymbolizerFactory(binary): + suffix = os.getenv('BREAKPAD_SUFFIX') + if suffix: + filename = binary + suffix + if os.access(filename, os.F_OK): + return BreakpadSymbolizer(filename) + return None + + +def SystemSymbolizerFactory(system, addr, binary): + if system == 'Darwin': + return DarwinSymbolizer(addr, binary) + elif system == 'Linux': + return Addr2LineSymbolizer(binary) + + +class BreakpadSymbolizer(Symbolizer): + def __init__(self, filename): + super(BreakpadSymbolizer, self).__init__() + self.filename = filename + lines = file(filename).readlines() + self.files = [] + self.symbols = {} + self.address_list = [] + self.addresses = {} + # MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t + fragments = lines[0].rstrip().split() + self.arch = fragments[2] + self.debug_id = fragments[3] + self.binary = ' '.join(fragments[4:]) + self.parse_lines(lines[1:]) + + def parse_lines(self, lines): + cur_function_addr = '' + for line in lines: + fragments = line.split() + if fragments[0] == 'FILE': + assert int(fragments[1]) == len(self.files) + self.files.append(' '.join(fragments[2:])) + elif fragments[0] == 'PUBLIC': + self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:]) + elif fragments[0] in ['CFI', 'STACK']: + pass + elif fragments[0] == 'FUNC': + cur_function_addr = int(fragments[1], 16) + if not cur_function_addr in self.symbols.keys(): + self.symbols[cur_function_addr] = ' '.join(fragments[4:]) + else: + # Line starting with an address. + addr = int(fragments[0], 16) + self.address_list.append(addr) + # Tuple of symbol address, size, line, file number. + self.addresses[addr] = (cur_function_addr, + int(fragments[1], 16), + int(fragments[2]), + int(fragments[3])) + self.address_list.sort() + + def get_sym_file_line(self, addr): + key = None + if addr in self.addresses.keys(): + key = addr + else: + index = bisect.bisect_left(self.address_list, addr) + if index == 0: + return None + else: + key = self.address_list[index - 1] + sym_id, size, line_no, file_no = self.addresses[key] + symbol = self.symbols[sym_id] + filename = self.files[file_no] + if addr < key + size: + return symbol, filename, line_no + else: + return None + + def symbolize(self, addr, binary, offset): + if self.binary != binary: + return None + res = self.get_sym_file_line(int(offset, 16)) + if res: + function_name, file_name, line_no = res + result = ['%s in %s %s:%d' % ( + addr, function_name, file_name, line_no)] + print result + return result + else: + return None + + +class SymbolizationLoop(object): + def __init__(self, binary_name_filter=None): + # Used by clients who may want to supply a different binary name. + # E.g. in Chrome several binaries may share a single .dSYM. + self.binary_name_filter = binary_name_filter + self.system = os.uname()[0] + if self.system in ['Linux', 'Darwin']: + self.llvm_symbolizer = LLVMSymbolizerFactory(self.system) + else: + raise Exception('Unknown system') + + def symbolize_address(self, addr, binary, offset): + # Use the chain of symbolizers: + # Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos + # (fall back to next symbolizer if the previous one fails). + if not binary in symbolizers: + symbolizers[binary] = ChainSymbolizer( + [BreakpadSymbolizerFactory(binary), self.llvm_symbolizer]) + result = symbolizers[binary].symbolize(addr, binary, offset) + if result is None: + # Initialize system symbolizer only if other symbolizers failed. + symbolizers[binary].append_symbolizer( + SystemSymbolizerFactory(self.system, addr, binary)) + result = symbolizers[binary].symbolize(addr, binary, offset) + # The system symbolizer must produce some result. + assert result + return result + + def print_symbolized_lines(self, symbolized_lines): + if not symbolized_lines: + print self.current_line + else: + for symbolized_frame in symbolized_lines: + print ' #' + str(self.frame_no) + ' ' + symbolized_frame.rstrip() + self.frame_no += 1 + + def process_stdin(self): + self.frame_no = 0 + while True: + line = sys.stdin.readline() + if not line: + break + self.current_line = line.rstrip() + #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) + stack_trace_line_format = ( + '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') + match = re.match(stack_trace_line_format, line) + if not match: + print self.current_line + continue + if DEBUG: + print line + _, frameno_str, addr, binary, offset = match.groups() + if frameno_str == '0': + # Assume that frame #0 is the first frame of new stack trace. + self.frame_no = 0 + original_binary = binary + if self.binary_name_filter: + binary = self.binary_name_filter(binary) + symbolized_line = self.symbolize_address(addr, binary, offset) + if not symbolized_line: + if original_binary != binary: + symbolized_line = self.symbolize_address(addr, binary, offset) + self.print_symbolized_lines(symbolized_line) + + +if __name__ == '__main__': + opts, args = getopt.getopt(sys.argv[1:], "d", ["demangle"]) + for o, a in opts: + if o in ("-d", "--demangle"): + demangle = True; + loop = SymbolizationLoop() + loop.process_stdin() diff --git a/projects/compiler-rt/lib/asan/tests/CMakeLists.txt b/projects/compiler-rt/lib/asan/tests/CMakeLists.txt new file mode 100644 index 00000000..fc0f1788 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/CMakeLists.txt @@ -0,0 +1,253 @@ +# Testing rules for AddressSanitizer. +# +# These are broken into two buckets. One set of tests directly interacts with +# the runtime library and checks its functionality. These are the +# no-instrumentation tests. +# +# Another group of tests relies upon the ability to compile the test with +# address sanitizer instrumentation pass. These tests form "integration" tests +# and have some elements of version skew -- they test the *host* compiler's +# instrumentation against the just-built runtime library. + +include(CheckCXXCompilerFlag) +include(CompilerRTCompile) + +include_directories(..) +include_directories(../..) + +# Use zero-based shadow on Android. +if(ANDROID) + set(ASAN_TESTS_USE_ZERO_BASE_SHADOW TRUE) +else() + set(ASAN_TESTS_USE_ZERO_BASE_SHADOW FALSE) +endif() + +set(ASAN_UNITTEST_HEADERS + asan_mac_test.h + asan_test_config.h + asan_test_utils.h) + +set(ASAN_UNITTEST_COMMON_CFLAGS + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/asan + -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/tests + -Wall + -Wno-format + -Werror + -Werror=sign-compare + -g + -O2) +if(SUPPORTS_NO_VARIADIC_MACROS_FLAG) + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -Wno-variadic-macros) +endif() + +# Use -D instead of definitions to please custom compile command. +list(APPEND ASAN_UNITTEST_COMMON_CFLAGS + -DASAN_HAS_BLACKLIST=1 + -DASAN_HAS_EXCEPTIONS=1 + -DASAN_UAR=0) +if(ANDROID) + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 + -DASAN_NEEDS_SEGV=0) +else() + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 + -DASAN_NEEDS_SEGV=1) +endif() + +set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore") +set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS + ${ASAN_UNITTEST_COMMON_CFLAGS} + -fsanitize=address + "-fsanitize-blacklist=${ASAN_BLACKLIST_FILE}" + -mllvm -asan-stack=1 + -mllvm -asan-globals=1 + -mllvm -asan-mapping-scale=0 # default will be used + -mllvm -asan-mapping-offset-log=-1 # default will be used +) +if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) + list(APPEND ASAN_UNITTEST_INSTRUMENTED_CFLAGS + -fsanitize-address-zero-base-shadow) +endif() + +# Unit tests require libstdc++. +set(ASAN_UNITTEST_COMMON_LINKFLAGS -lstdc++) +# Unit tests on Mac depend on Foundation. +if(APPLE) + list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS -framework Foundation) +endif() +if(ASAN_TESTS_USE_ZERO_BASE_SHADOW) + list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS -pie) +endif() + +set(ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS + ${ASAN_UNITTEST_COMMON_LINKFLAGS}) +# On Android, we link with ASan runtime manually. On other platforms we depend +# on Clang driver behavior, passing -fsanitize=address flag. +if(NOT ANDROID) + list(APPEND ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS -fsanitize=address) +endif() + +set(ASAN_UNITTEST_NOINST_LINKFLAGS + ${ASAN_UNITTEST_COMMON_LINKFLAGS} + -ldl -lm) +if(NOT ANDROID) + list(APPEND ASAN_UNITTEST_NOINST_LINKFLAGS -lpthread) +endif() + +# Compile source for the given architecture, using compiler +# options in ${ARGN}, and add it to the object list. +macro(asan_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${obj_list}.${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + clang_compile(${output_obj} ${source} + CFLAGS ${ARGN} ${TARGET_CFLAGS} + DEPS gtest asan_runtime_libraries + ${ASAN_UNITTEST_HEADERS} + ${ASAN_BLACKLIST_FILE}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +# Link ASan unit test for a given architecture from a set +# of objects in with given linker flags. +macro(add_asan_test test_suite test_name arch) + parse_arguments(TEST "OBJECTS;LINKFLAGS" "WITH_TEST_RUNTIME" ${ARGN}) + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + set(TEST_DEPS asan_runtime_libraries ${TEST_OBJECTS}) + if(TEST_WITH_TEST_RUNTIME) + list(APPEND TEST_DEPS ${ASAN_TEST_RUNTIME}) + list(APPEND TEST_OBJECTS lib${ASAN_TEST_RUNTIME}.a) + endif() + add_compiler_rt_test(${test_suite} ${test_name} + OBJECTS ${TEST_OBJECTS} + DEPS ${TEST_DEPS} + LINK_FLAGS ${TEST_LINKFLAGS} + ${TARGET_LINK_FLAGS}) +endmacro() + +# Main AddressSanitizer unit tests. +add_custom_target(AsanUnitTests) +set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests") +# ASan benchmarks (not actively used now). +add_custom_target(AsanBenchmarks) +set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") + +set(ASAN_NOINST_TEST_SOURCES + ${COMPILER_RT_GTEST_SOURCE} + asan_fake_stack_test.cc + asan_noinst_test.cc + asan_test_main.cc) + +set(ASAN_INST_TEST_SOURCES + ${COMPILER_RT_GTEST_SOURCE} + asan_globals_test.cc + asan_interface_test.cc + asan_test.cc + asan_oob_test.cc + asan_mem_test.cc + asan_str_test.cc + asan_test_main.cc) +if(APPLE) + list(APPEND ASAN_INST_TEST_SOURCES asan_mac_test.cc) +endif() + +set(ASAN_BENCHMARKS_SOURCES + ${COMPILER_RT_GTEST_SOURCE} + asan_benchmarks_test.cc) + +# Adds ASan unit tests and benchmarks for architecture. +macro(add_asan_tests_for_arch arch) + # Instrumented tests. + set(ASAN_INST_TEST_OBJECTS) + foreach(src ${ASAN_INST_TEST_SOURCES}) + asan_compile(ASAN_INST_TEST_OBJECTS ${src} ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + endforeach() + if (APPLE) + # Add Mac-specific helper. + asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC) + endif() + add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch} + OBJECTS ${ASAN_INST_TEST_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + + # Add static ASan runtime that will be linked with uninstrumented tests. + set(ASAN_TEST_RUNTIME RTAsanTest.${arch}) + if(APPLE) + set(ASAN_TEST_RUNTIME_OBJECTS + $ + $ + $ + $) + else() + set(ASAN_TEST_RUNTIME_OBJECTS + $ + $ + $ + $ + $) + endif() + add_library(${ASAN_TEST_RUNTIME} STATIC ${ASAN_TEST_RUNTIME_OBJECTS}) + set_target_properties(${ASAN_TEST_RUNTIME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + # Uninstrumented tests. + set(ASAN_NOINST_TEST_OBJECTS) + foreach(src ${ASAN_NOINST_TEST_SOURCES}) + asan_compile(ASAN_NOINST_TEST_OBJECTS ${src} ${arch} + ${ASAN_UNITTEST_COMMON_CFLAGS}) + endforeach() + add_asan_test(AsanUnitTests "Asan-${arch}-Noinst-Test" ${arch} + OBJECTS ${ASAN_NOINST_TEST_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_NOINST_LINKFLAGS} + WITH_TEST_RUNTIME) + + # Benchmarks. + set(ASAN_BENCHMARKS_OBJECTS) + foreach(src ${ASAN_BENCHMARKS_SOURCES}) + asan_compile(ASAN_BENCHMARKS_OBJECTS ${src} ${arch} + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + endforeach() + add_asan_test(AsanBenchmarks "Asan-${arch}-Benchmark" ${arch} + OBJECTS ${ASAN_BENCHMARKS_OBJECTS} + LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) +endmacro() + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + foreach(arch ${ASAN_SUPPORTED_ARCH}) + add_asan_tests_for_arch(${arch}) + endforeach() +endif() + +if(ANDROID) + # We assume that unit tests on Android are built in a build + # tree with fresh Clang as a host compiler. + + # Test w/o ASan instrumentation. Link it with ASan statically. + add_executable(AsanNoinstTest + $ + $ + $ + ${COMPILER_RT_GTEST_SOURCE} + ${ASAN_NOINST_TEST_SOURCES}) + set_target_compile_flags(AsanNoinstTest ${ASAN_UNITTEST_COMMON_CFLAGS}) + set_target_link_flags(AsanNoinstTest ${ASAN_UNITTEST_NOINST_LINKFLAGS}) + + # Test with ASan instrumentation. Link with ASan dynamic runtime. + add_executable(AsanTest + ${COMPILER_RT_GTEST_SOURCE} + ${ASAN_INST_TEST_SOURCES}) + set_target_compile_flags(AsanTest ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}) + set_target_link_flags(AsanTest ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) + target_link_libraries(AsanTest clang_rt.asan-arm-android) + + # Setup correct output directory and link flags. + set_target_properties(AsanNoinstTest AsanTest PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + # Add unit test to test suite. + add_dependencies(AsanUnitTests AsanNoinstTest AsanTest) +endif() diff --git a/projects/compiler-rt/lib/asan/tests/asan_benchmarks_test.cc b/projects/compiler-rt/lib/asan/tests/asan_benchmarks_test.cc new file mode 100644 index 00000000..fc522de4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_benchmarks_test.cc @@ -0,0 +1,85 @@ +//===-- asan_benchmarks_test.cc ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Some benchmarks for the instrumented code. +//===----------------------------------------------------------------------===// + +#include "asan_test_utils.h" + +template +__attribute__((noinline)) +static void ManyAccessFunc(T *x, size_t n_elements, size_t n_iter) { + for (size_t iter = 0; iter < n_iter; iter++) { + break_optimization(0); + // hand unroll the loop to stress the reg alloc. + for (size_t i = 0; i <= n_elements - 16; i += 16) { + x[i + 0] = i; + x[i + 1] = i; + x[i + 2] = i; + x[i + 3] = i; + x[i + 4] = i; + x[i + 5] = i; + x[i + 6] = i; + x[i + 7] = i; + x[i + 8] = i; + x[i + 9] = i; + x[i + 10] = i; + x[i + 11] = i; + x[i + 12] = i; + x[i + 13] = i; + x[i + 14] = i; + x[i + 15] = i; + } + } +} + +TEST(AddressSanitizer, ManyAccessBenchmark) { + size_t kLen = 1024; + int *int_array = new int[kLen]; + ManyAccessFunc(int_array, kLen, 1 << 24); + delete [] int_array; +} + +// access 7 char elements in a 7 byte array (i.e. on the border). +__attribute__((noinline)) +static void BorderAccessFunc(char *x, size_t n_iter) { + for (size_t iter = 0; iter < n_iter; iter++) { + break_optimization(x); + x[0] = 0; + x[1] = 0; + x[2] = 0; + x[3] = 0; + x[4] = 0; + x[5] = 0; + x[6] = 0; + } +} + +TEST(AddressSanitizer, BorderAccessBenchmark) { + char *char_7_array = new char[7]; + BorderAccessFunc(char_7_array, 1 << 30); + delete [] char_7_array; +} + +static void FunctionWithLargeStack() { + int stack[1000]; + Ident(stack); +} + +TEST(AddressSanitizer, FakeStackBenchmark) { + for (int i = 0; i < 10000000; i++) + Ident(&FunctionWithLargeStack)(); +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_exceptions_test.cc b/projects/compiler-rt/lib/asan/tests/asan_exceptions_test.cc new file mode 100644 index 00000000..ecd406de --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_exceptions_test.cc @@ -0,0 +1,27 @@ +// See http://llvm.org/bugs/show_bug.cgi?id=11468 +#include +#include + +class Action { + public: + Action() {} + void PrintString(const std::string& msg) const { + fprintf(stderr, "%s\n", msg.c_str()); + } + void Throw(const char& arg) const { + PrintString("PrintString called!"); // this line is important + throw arg; + } +}; + +int main() { + const Action a; + fprintf(stderr, "&a before = %p\n", &a); + try { + a.Throw('c'); + } catch(const char&) { + fprintf(stderr, "&a in catch = %p\n", &a); + } + fprintf(stderr, "&a final = %p\n", &a); + return 0; +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_fake_stack_test.cc b/projects/compiler-rt/lib/asan/tests/asan_fake_stack_test.cc new file mode 100644 index 00000000..1c98125e --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_fake_stack_test.cc @@ -0,0 +1,150 @@ +//===-- asan_fake_stack_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Tests for FakeStack. +// This test file should be compiled w/o asan instrumentation. +//===----------------------------------------------------------------------===// + +#include "asan_fake_stack.h" +#include "asan_test_utils.h" +#include "sanitizer_common/sanitizer_common.h" + +#include +#include +#include + +#include + +namespace __asan { + +TEST(FakeStack, FlagsSize) { + EXPECT_EQ(FakeStack::SizeRequiredForFlags(10), 1U << 5); + EXPECT_EQ(FakeStack::SizeRequiredForFlags(11), 1U << 6); + EXPECT_EQ(FakeStack::SizeRequiredForFlags(20), 1U << 15); +} + +TEST(FakeStack, RequiredSize) { + // for (int i = 15; i < 20; i++) { + // uptr alloc_size = FakeStack::RequiredSize(i); + // printf("%zdK ==> %zd\n", 1 << (i - 10), alloc_size); + // } + EXPECT_EQ(FakeStack::RequiredSize(15), 365568U); + EXPECT_EQ(FakeStack::RequiredSize(16), 727040U); + EXPECT_EQ(FakeStack::RequiredSize(17), 1449984U); + EXPECT_EQ(FakeStack::RequiredSize(18), 2895872U); + EXPECT_EQ(FakeStack::RequiredSize(19), 5787648U); +} + +TEST(FakeStack, FlagsOffset) { + for (uptr stack_size_log = 15; stack_size_log <= 20; stack_size_log++) { + uptr stack_size = 1UL << stack_size_log; + uptr offset = 0; + for (uptr class_id = 0; class_id < FakeStack::kNumberOfSizeClasses; + class_id++) { + uptr frame_size = FakeStack::BytesInSizeClass(class_id); + uptr num_flags = stack_size / frame_size; + EXPECT_EQ(offset, FakeStack::FlagsOffset(stack_size_log, class_id)); + // printf("%zd: %zd => %zd %zd\n", stack_size_log, class_id, offset, + // FakeStack::FlagsOffset(stack_size_log, class_id)); + offset += num_flags; + } + } +} + +TEST(FakeStack, CreateDestroy) { + for (int i = 0; i < 1000; i++) { + for (uptr stack_size_log = 20; stack_size_log <= 22; stack_size_log++) { + FakeStack *fake_stack = FakeStack::Create(stack_size_log); + fake_stack->Destroy(); + } + } +} + +TEST(FakeStack, ModuloNumberOfFrames) { + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, 0), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15)), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<10)), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<9)), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<8)), 1U<<8); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15) + 1), 1U); + + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 0), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<9), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<8), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<7), 1U<<7); + + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 0), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 1), 1U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 15), 15U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 16), 0U); + EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 17), 1U); +} + +TEST(FakeStack, GetFrame) { + const uptr stack_size_log = 20; + const uptr stack_size = 1 << stack_size_log; + FakeStack *fs = FakeStack::Create(stack_size_log); + u8 *base = fs->GetFrame(stack_size_log, 0, 0); + EXPECT_EQ(base, reinterpret_cast(fs) + + fs->SizeRequiredForFlags(stack_size_log) + 4096); + EXPECT_EQ(base + 0*stack_size + 64 * 7, fs->GetFrame(stack_size_log, 0, 7U)); + EXPECT_EQ(base + 1*stack_size + 128 * 3, fs->GetFrame(stack_size_log, 1, 3U)); + EXPECT_EQ(base + 2*stack_size + 256 * 5, fs->GetFrame(stack_size_log, 2, 5U)); + fs->Destroy(); +} + +TEST(FakeStack, Allocate) { + const uptr stack_size_log = 19; + FakeStack *fs = FakeStack::Create(stack_size_log); + std::map s; + for (int iter = 0; iter < 2; iter++) { + s.clear(); + for (uptr cid = 0; cid < FakeStack::kNumberOfSizeClasses; cid++) { + uptr n = FakeStack::NumberOfFrames(stack_size_log, cid); + uptr bytes_in_class = FakeStack::BytesInSizeClass(cid); + for (uptr j = 0; j < n; j++) { + FakeFrame *ff = fs->Allocate(stack_size_log, cid, 0); + uptr x = reinterpret_cast(ff); + EXPECT_TRUE(s.insert(std::make_pair(ff, cid)).second); + EXPECT_EQ(x, fs->AddrIsInFakeStack(x)); + EXPECT_EQ(x, fs->AddrIsInFakeStack(x + 1)); + EXPECT_EQ(x, fs->AddrIsInFakeStack(x + bytes_in_class - 1)); + EXPECT_NE(x, fs->AddrIsInFakeStack(x + bytes_in_class)); + } + // We are out of fake stack, so Allocate should return 0. + EXPECT_EQ(0UL, fs->Allocate(stack_size_log, cid, 0)); + } + for (std::map::iterator it = s.begin(); it != s.end(); + ++it) { + fs->Deallocate(reinterpret_cast(it->first), it->second); + } + } + fs->Destroy(); +} + +static void RecursiveFunction(FakeStack *fs, int depth) { + uptr class_id = depth / 3; + FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, 0); + if (depth) { + RecursiveFunction(fs, depth - 1); + RecursiveFunction(fs, depth - 1); + } + fs->Deallocate(reinterpret_cast(ff), class_id); +} + +TEST(FakeStack, RecursiveStressTest) { + const uptr stack_size_log = 16; + FakeStack *fs = FakeStack::Create(stack_size_log); + RecursiveFunction(fs, 22); // with 26 runs for 2-3 seconds. + fs->Destroy(); +} + +} // namespace __asan diff --git a/projects/compiler-rt/lib/asan/tests/asan_globals_test.cc b/projects/compiler-rt/lib/asan/tests/asan_globals_test.cc new file mode 100644 index 00000000..5042ef07 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_globals_test.cc @@ -0,0 +1,45 @@ +//===-- asan_globals_test.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Some globals in a separate file. +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +char glob1[1]; +char glob2[2]; +char glob3[3]; +char glob4[4]; +char glob5[5]; +char glob6[6]; +char glob7[7]; +char glob8[8]; +char glob9[9]; +char glob10[10]; +char glob11[11]; +char glob12[12]; +char glob13[13]; +char glob14[14]; +char glob15[15]; +char glob16[16]; +char glob17[17]; +char glob1000[1000]; +char glob10000[10000]; +char glob100000[100000]; + +static char static10[10]; + +int GlobalsTest(int zero) { + static char func_static15[15]; + glob5[zero] = 0; + static10[zero] = 0; + func_static15[zero] = 0; + return glob5[1] + func_static15[2]; +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_interface_test.cc b/projects/compiler-rt/lib/asan/tests/asan_interface_test.cc new file mode 100644 index 00000000..f3667901 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_interface_test.cc @@ -0,0 +1,426 @@ +//===-- asan_interface_test.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" +#include "sanitizer/asan_interface.h" + +TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { + EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0)); + const size_t sizes[] = { 1, 30, 1<<30 }; + for (size_t i = 0; i < 3; i++) { + EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i])); + } +} + +static const char* kGetAllocatedSizeErrorMsg = + "attempting to call __asan_get_allocated_size()"; + +TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + + // Allocated memory is owned by allocator. Allocated size should be + // equal to requested size. + EXPECT_EQ(true, __asan_get_ownership(array)); + EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); + EXPECT_EQ(true, __asan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); + + // We cannot call GetAllocatedSize from the memory we didn't map, + // and from the interior pointers (not returned by previous malloc). + void *wild_addr = (void*)0x1; + EXPECT_FALSE(__asan_get_ownership(wild_addr)); + EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); + EXPECT_FALSE(__asan_get_ownership(array + kArraySize / 2)); + EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2), + kGetAllocatedSizeErrorMsg); + + // NULL is not owned, but is a valid argument for __asan_get_allocated_size(). + EXPECT_FALSE(__asan_get_ownership(NULL)); + EXPECT_EQ(0U, __asan_get_allocated_size(NULL)); + + // When memory is freed, it's not owned, and call to GetAllocatedSize + // is forbidden. + free(array); + EXPECT_FALSE(__asan_get_ownership(array)); + EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg); + delete int_ptr; + + void *zero_alloc = Ident(malloc(0)); + if (zero_alloc != 0) { + // If malloc(0) is not null, this pointer is owned and should have valid + // allocated size. + EXPECT_TRUE(__asan_get_ownership(zero_alloc)); + // Allocated size is 0 or 1 depending on the allocator used. + EXPECT_LT(__asan_get_allocated_size(zero_alloc), 2U); + } + free(zero_alloc); +} + +TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { + size_t before_malloc, after_malloc, after_free; + char *array; + const size_t kMallocSize = 100; + before_malloc = __asan_get_current_allocated_bytes(); + + array = Ident((char*)malloc(kMallocSize)); + after_malloc = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc + kMallocSize, after_malloc); + + free(array); + after_free = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc, after_free); +} + +TEST(AddressSanitizerInterface, GetHeapSizeTest) { + // asan_allocator2 does not keep huge chunks in free list, but unmaps them. + // The chunk should be greater than the quarantine size, + // otherwise it will be stuck in quarantine instead of being unmaped. + static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M + free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. + size_t old_heap_size = __asan_get_heap_size(); + for (int i = 0; i < 3; i++) { + // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); + free(Ident(malloc(kLargeMallocSize))); + EXPECT_EQ(old_heap_size, __asan_get_heap_size()); + } +} + +static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; +static const size_t kManyThreadsIterations = 250; +static const size_t kManyThreadsNumThreads = + (SANITIZER_WORDSIZE == 32) ? 40 : 200; + +static void *ManyThreadsWithStatsWorker(void *arg) { + (void)arg; + for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { + for (size_t size_index = 0; size_index < 4; size_index++) { + free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); + } + } + // Just one large allocation. + free(Ident(malloc(1 << 20))); + return 0; +} + +TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { + size_t before_test, after_test, i; + pthread_t threads[kManyThreadsNumThreads]; + before_test = __asan_get_current_allocated_bytes(); + for (i = 0; i < kManyThreadsNumThreads; i++) { + PTHREAD_CREATE(&threads[i], 0, + (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); + } + for (i = 0; i < kManyThreadsNumThreads; i++) { + PTHREAD_JOIN(threads[i], 0); + } + after_test = __asan_get_current_allocated_bytes(); + // ASan stats also reflect memory usage of internal ASan RTL structs, + // so we can't check for equality here. + EXPECT_LT(after_test, before_test + (1UL<<20)); +} + +static void DoDoubleFree() { + int *x = Ident(new int); + delete Ident(x); + delete Ident(x); +} + +TEST(AddressSanitizerInterface, ExitCode) { + int original_exit_code = __asan_set_error_exit_code(7); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); + EXPECT_EQ(7, __asan_set_error_exit_code(8)); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); + EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); + EXPECT_EXIT(DoDoubleFree(), + ::testing::ExitedWithCode(original_exit_code), ""); +} + +static void MyDeathCallback() { + fprintf(stderr, "MyDeathCallback\n"); +} + +TEST(AddressSanitizerInterface, DeathCallbackTest) { + __asan_set_death_callback(MyDeathCallback); + EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); + __asan_set_death_callback(NULL); +} + +static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; + +#define GOOD_ACCESS(ptr, offset) \ + EXPECT_FALSE(__asan_address_is_poisoned(ptr + offset)) + +#define BAD_ACCESS(ptr, offset) \ + EXPECT_TRUE(__asan_address_is_poisoned(ptr + offset)) + +TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // poison array[40..80) + __asan_poison_memory_region(array + 40, 40); + GOOD_ACCESS(array, 39); + GOOD_ACCESS(array, 80); + BAD_ACCESS(array, 40); + BAD_ACCESS(array, 60); + BAD_ACCESS(array, 79); + char value; + EXPECT_DEATH(value = Ident(array[40]), kUseAfterPoisonErrorMessage); + __asan_unpoison_memory_region(array + 40, 40); + // access previously poisoned memory. + GOOD_ACCESS(array, 40); + GOOD_ACCESS(array, 79); + free(array); +} + +TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // Poison [0..40) and [80..120) + __asan_poison_memory_region(array, 40); + __asan_poison_memory_region(array + 80, 40); + BAD_ACCESS(array, 20); + GOOD_ACCESS(array, 60); + BAD_ACCESS(array, 100); + // Poison whole array - [0..120) + __asan_poison_memory_region(array, 120); + BAD_ACCESS(array, 60); + // Unpoison [24..96) + __asan_unpoison_memory_region(array + 24, 72); + BAD_ACCESS(array, 23); + GOOD_ACCESS(array, 24); + GOOD_ACCESS(array, 60); + GOOD_ACCESS(array, 95); + BAD_ACCESS(array, 96); + free(array); +} + +TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { + // Vector of capacity 20 + char *vec = Ident((char*)malloc(20)); + __asan_poison_memory_region(vec, 20); + for (size_t i = 0; i < 7; i++) { + // Simulate push_back. + __asan_unpoison_memory_region(vec + i, 1); + GOOD_ACCESS(vec, i); + BAD_ACCESS(vec, i + 1); + } + for (size_t i = 7; i > 0; i--) { + // Simulate pop_back. + __asan_poison_memory_region(vec + i - 1, 1); + BAD_ACCESS(vec, i - 1); + if (i > 1) GOOD_ACCESS(vec, i - 2); + } + free(vec); +} + +// Make sure that each aligned block of size "2^granularity" doesn't have +// "true" value before "false" value. +static void MakeShadowValid(bool *shadow, int length, int granularity) { + bool can_be_poisoned = true; + for (int i = length - 1; i >= 0; i--) { + if (!shadow[i]) + can_be_poisoned = false; + if (!can_be_poisoned) + shadow[i] = false; + if (i % (1 << granularity) == 0) { + can_be_poisoned = true; + } + } +} + +TEST(AddressSanitizerInterface, PoisoningStressTest) { + const size_t kSize = 24; + bool expected[kSize]; + char *arr = Ident((char*)malloc(kSize)); + for (size_t l1 = 0; l1 < kSize; l1++) { + for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { + for (size_t l2 = 0; l2 < kSize; l2++) { + for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { + // Poison [l1, l1+s1), [l2, l2+s2) and check result. + __asan_unpoison_memory_region(arr, kSize); + __asan_poison_memory_region(arr + l1, s1); + __asan_poison_memory_region(arr + l2, s2); + memset(expected, false, kSize); + memset(expected + l1, true, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, true, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. + __asan_poison_memory_region(arr, kSize); + __asan_unpoison_memory_region(arr + l1, s1); + __asan_unpoison_memory_region(arr + l2, s2); + memset(expected, true, kSize); + memset(expected + l1, false, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, false, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + } + } + } + } + free(arr); +} + +TEST(AddressSanitizerInterface, GlobalRedzones) { + GOOD_ACCESS(glob1, 1 - 1); + GOOD_ACCESS(glob2, 2 - 1); + GOOD_ACCESS(glob3, 3 - 1); + GOOD_ACCESS(glob4, 4 - 1); + GOOD_ACCESS(glob5, 5 - 1); + GOOD_ACCESS(glob6, 6 - 1); + GOOD_ACCESS(glob7, 7 - 1); + GOOD_ACCESS(glob8, 8 - 1); + GOOD_ACCESS(glob9, 9 - 1); + GOOD_ACCESS(glob10, 10 - 1); + GOOD_ACCESS(glob11, 11 - 1); + GOOD_ACCESS(glob12, 12 - 1); + GOOD_ACCESS(glob13, 13 - 1); + GOOD_ACCESS(glob14, 14 - 1); + GOOD_ACCESS(glob15, 15 - 1); + GOOD_ACCESS(glob16, 16 - 1); + GOOD_ACCESS(glob17, 17 - 1); + GOOD_ACCESS(glob1000, 1000 - 1); + GOOD_ACCESS(glob10000, 10000 - 1); + GOOD_ACCESS(glob100000, 100000 - 1); + + BAD_ACCESS(glob1, 1); + BAD_ACCESS(glob2, 2); + BAD_ACCESS(glob3, 3); + BAD_ACCESS(glob4, 4); + BAD_ACCESS(glob5, 5); + BAD_ACCESS(glob6, 6); + BAD_ACCESS(glob7, 7); + BAD_ACCESS(glob8, 8); + BAD_ACCESS(glob9, 9); + BAD_ACCESS(glob10, 10); + BAD_ACCESS(glob11, 11); + BAD_ACCESS(glob12, 12); + BAD_ACCESS(glob13, 13); + BAD_ACCESS(glob14, 14); + BAD_ACCESS(glob15, 15); + BAD_ACCESS(glob16, 16); + BAD_ACCESS(glob17, 17); + BAD_ACCESS(glob1000, 1000); + BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. + BAD_ACCESS(glob10000, 10000); + BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. + BAD_ACCESS(glob100000, 100000); + BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. +} + +TEST(AddressSanitizerInterface, PoisonedRegion) { + size_t rz = 16; + for (size_t size = 1; size <= 64; size++) { + char *p = new char[size]; + for (size_t beg = 0; beg < size + rz; beg++) { + for (size_t end = beg; end < size + rz; end++) { + void *first_poisoned = __asan_region_is_poisoned(p + beg, end - beg); + if (beg == end) { + EXPECT_FALSE(first_poisoned); + } else if (beg < size && end <= size) { + EXPECT_FALSE(first_poisoned); + } else if (beg >= size) { + EXPECT_EQ(p + beg, first_poisoned); + } else { + EXPECT_GT(end, size); + EXPECT_EQ(p + size, first_poisoned); + } + } + } + delete [] p; + } +} + +// This is a performance benchmark for manual runs. +// asan's memset interceptor calls mem_is_zero for the entire shadow region. +// the profile should look like this: +// 89.10% [.] __memset_sse2 +// 10.50% [.] __sanitizer::mem_is_zero +// I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles +// than memset itself. +TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) { + size_t size = 1 << 20; + char *x = new char[size]; + for (int i = 0; i < 100000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} + +// Same here, but we run memset with small sizes. +TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { + size_t size = 32; + char *x = new char[size]; + for (int i = 0; i < 100000000; i++) + Ident(memset)(x, 0, size); + delete [] x; +} +static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; +static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; + +TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { + char *array = Ident((char*)malloc(120)); + __asan_unpoison_memory_region(array, 120); + // Try to unpoison not owned memory + EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), + kInvalidUnpoisonMessage); + EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), + kInvalidUnpoisonMessage); + + __asan_poison_memory_region(array, 120); + // Try to poison not owned memory. + EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); + EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), + kInvalidPoisonMessage); + free(array); +} + +static void ErrorReportCallbackOneToZ(const char *report) { + int report_len = strlen(report); + ASSERT_EQ(6, write(2, "ABCDEF", 6)); + ASSERT_EQ(report_len, write(2, report, report_len)); + ASSERT_EQ(6, write(2, "ABCDEF", 6)); + _exit(1); +} + +TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) { + __asan_set_error_report_callback(ErrorReportCallbackOneToZ); + EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), + ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF"); + __asan_set_error_report_callback(NULL); +} + +TEST(AddressSanitizerInterface, GetOwnershipStressTest) { + std::vector pointers; + std::vector sizes; + const size_t kNumMallocs = 1 << 9; + for (size_t i = 0; i < kNumMallocs; i++) { + size_t size = i * 100 + 1; + pointers.push_back((char*)malloc(size)); + sizes.push_back(size); + } + for (size_t i = 0; i < 4000000; i++) { + EXPECT_FALSE(__asan_get_ownership(&pointers)); + EXPECT_FALSE(__asan_get_ownership((void*)0x1234)); + size_t idx = i % kNumMallocs; + EXPECT_TRUE(__asan_get_ownership(pointers[idx])); + EXPECT_EQ(sizes[idx], __asan_get_allocated_size(pointers[idx])); + } + for (size_t i = 0, n = pointers.size(); i < n; i++) + free(pointers[i]); +} + diff --git a/projects/compiler-rt/lib/asan/tests/asan_mac_test.cc b/projects/compiler-rt/lib/asan/tests/asan_mac_test.cc new file mode 100644 index 00000000..cabdfd71 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_mac_test.cc @@ -0,0 +1,236 @@ +//===-- asan_test_mac.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// + +#include "asan_test_utils.h" + +#include "asan_mac_test.h" + +#include +#include // For MAC_OS_X_VERSION_* +#include + +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) { + EXPECT_DEATH( + CFAllocatorDefaultDoubleFree(NULL), + "attempting double-free"); +} + +void CFAllocator_DoubleFreeOnPthread() { + pthread_t child; + PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL); + PTHREAD_JOIN(child, NULL); // Shouldn't be reached. +} + +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) { + EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free"); +} + +namespace { + +void *GLOB; + +void *CFAllocatorAllocateToGlob(void *unused) { + GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0); + return NULL; +} + +void *CFAllocatorDeallocateFromGlob(void *unused) { + char *p = (char*)GLOB; + p[100] = 'A'; // ASan should report an error here. + CFAllocatorDeallocate(NULL, GLOB); + return NULL; +} + +void CFAllocator_PassMemoryToAnotherThread() { + pthread_t th1, th2; + PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL); + PTHREAD_JOIN(th1, NULL); + PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL); + PTHREAD_JOIN(th2, NULL); +} + +TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) { + EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(), + "heap-buffer-overflow"); +} + +} // namespace + +// TODO(glider): figure out whether we still need these tests. Is it correct +// to intercept the non-default CFAllocators? +TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) { + EXPECT_DEATH( + CFAllocatorSystemDefaultDoubleFree(), + "attempting double-free"); +} + +// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan. +TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) { + EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free"); +} + +TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) { + EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free"); +} + +// For libdispatch tests below we check that ASan got to the shadow byte +// legend, i.e. managed to print the thread stacks (this almost certainly +// means that the libdispatch task creation has been intercepted correctly). +TEST(AddressSanitizerMac, GCDDispatchAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDDispatchSync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend"); +} + + +TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDDispatchAfter) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDSourceEvent) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDSourceCancel) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend"); +} + +TEST(AddressSanitizerMac, GCDGroupAsync) { + // Make sure the whole ASan report is printed, i.e. that we don't die + // on a CHECK. + EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend"); +} + +void *MallocIntrospectionLockWorker(void *_) { + const int kNumPointers = 100; + int i; + void *pointers[kNumPointers]; + for (i = 0; i < kNumPointers; i++) { + pointers[i] = malloc(i + 1); + } + for (i = 0; i < kNumPointers; i++) { + free(pointers[i]); + } + + return NULL; +} + +void *MallocIntrospectionLockForker(void *_) { + pid_t result = fork(); + if (result == -1) { + perror("fork"); + } + assert(result != -1); + if (result == 0) { + // Call malloc in the child process to make sure we won't deadlock. + void *ptr = malloc(42); + free(ptr); + exit(0); + } else { + // Return in the parent process. + return NULL; + } +} + +TEST(AddressSanitizerMac, MallocIntrospectionLock) { + // Incorrect implementation of force_lock and force_unlock in our malloc zone + // will cause forked processes to deadlock. + // TODO(glider): need to detect that none of the child processes deadlocked. + const int kNumWorkers = 5, kNumIterations = 100; + int i, iter; + for (iter = 0; iter < kNumIterations; iter++) { + pthread_t workers[kNumWorkers], forker; + for (i = 0; i < kNumWorkers; i++) { + PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0); + } + PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0); + for (i = 0; i < kNumWorkers; i++) { + PTHREAD_JOIN(workers[i], 0); + } + PTHREAD_JOIN(forker, 0); + } +} + +void *TSDAllocWorker(void *test_key) { + if (test_key) { + void *mem = malloc(10); + pthread_setspecific(*(pthread_key_t*)test_key, mem); + } + return NULL; +} + +TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) { + pthread_t th; + pthread_key_t test_key; + pthread_key_create(&test_key, CallFreeOnWorkqueue); + PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key); + PTHREAD_JOIN(th, NULL); + pthread_key_delete(test_key); +} + +// Test that CFStringCreateCopy does not copy constant strings. +TEST(AddressSanitizerMac, CFStringCreateCopy) { + CFStringRef str = CFSTR("Hello world!\n"); + CFStringRef str2 = CFStringCreateCopy(0, str); + EXPECT_EQ(str, str2); +} + +TEST(AddressSanitizerMac, NSObjectOOB) { + // Make sure that our allocators are used for NSObjects. + EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow"); +} + +// Make sure that correct pointer is passed to free() when deallocating a +// NSURL object. +// See http://code.google.com/p/address-sanitizer/issues/detail?id=70. +TEST(AddressSanitizerMac, NSURLDeallocation) { + TestNSURLDeallocation(); +} + +// See http://code.google.com/p/address-sanitizer/issues/detail?id=109. +TEST(AddressSanitizerMac, Mstats) { + malloc_statistics_t stats1, stats2; + malloc_zone_statistics(/*all zones*/NULL, &stats1); + const size_t kMallocSize = 100000; + void *alloc = Ident(malloc(kMallocSize)); + malloc_zone_statistics(/*all zones*/NULL, &stats2); + EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use); + EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize); + free(alloc); + // Even the default OSX allocator may not change the stats after free(). +} + diff --git a/projects/compiler-rt/lib/asan/tests/asan_mac_test.h b/projects/compiler-rt/lib/asan/tests/asan_mac_test.h new file mode 100644 index 00000000..441547a5 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_mac_test.h @@ -0,0 +1,19 @@ +extern "C" { + void *CFAllocatorDefaultDoubleFree(void *unused); + void CFAllocatorSystemDefaultDoubleFree(); + void CFAllocatorMallocDoubleFree(); + void CFAllocatorMallocZoneDoubleFree(); + void CallFreeOnWorkqueue(void *mem); + void TestGCDDispatchAsync(); + void TestGCDDispatchSync(); + void TestGCDReuseWqthreadsAsync(); + void TestGCDReuseWqthreadsSync(); + void TestGCDDispatchAfter(); + void TestGCDInTSDDestructor(); + void TestGCDSourceEvent(); + void TestGCDSourceCancel(); + void TestGCDGroupAsync(); + void TestOOBNSObjects(); + void TestNSURLDeallocation(); + void TestPassCFMemoryToAnotherThread(); +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm b/projects/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm new file mode 100644 index 00000000..a7e4b9d1 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_mac_test_helpers.mm @@ -0,0 +1,240 @@ +// Mac OS X 10.6 or higher only. +#include +#include // for pthread_yield_np() +#include +#include +#include +#include + +#import +#import +#import + +// This is a (void*)(void*) function so it can be passed to pthread_create. +void *CFAllocatorDefaultDoubleFree(void *unused) { + void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); + CFAllocatorDeallocate(kCFAllocatorDefault, mem); + CFAllocatorDeallocate(kCFAllocatorDefault, mem); + return 0; +} + +void CFAllocatorSystemDefaultDoubleFree() { + void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem); +} + +void CFAllocatorMallocDoubleFree() { + void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0); + CFAllocatorDeallocate(kCFAllocatorMalloc, mem); + CFAllocatorDeallocate(kCFAllocatorMalloc, mem); +} + +void CFAllocatorMallocZoneDoubleFree() { + void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0); + CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); + CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); +} + +__attribute__((noinline)) +void access_memory(char *a) { + *a = 0; +} + +// Test the +load instrumentation. +// Because the +load methods are invoked before anything else is initialized, +// it makes little sense to wrap the code below into a gTest test case. +// If AddressSanitizer doesn't instrument the +load method below correctly, +// everything will just crash. + +char kStartupStr[] = + "If your test didn't crash, AddressSanitizer is instrumenting " + "the +load methods correctly."; + +@interface LoadSomething : NSObject { +} +@end + +@implementation LoadSomething + ++(void) load { + for (size_t i = 0; i < strlen(kStartupStr); i++) { + access_memory(&kStartupStr[i]); // make sure no optimizations occur. + } + // Don't print anything here not to interfere with the death tests. +} + +@end + +void worker_do_alloc(int size) { + char * volatile mem = (char * volatile)malloc(size); + mem[0] = 0; // Ok + free(mem); +} + +void worker_do_crash(int size) { + char * volatile mem = (char * volatile)malloc(size); + access_memory(&mem[size]); // BOOM + free(mem); +} + +// Used by the GCD tests to avoid a race between the worker thread reporting a +// memory error and the main thread which may exit with exit code 0 before +// that. +void wait_forever() { + volatile bool infinite = true; + while (infinite) pthread_yield_np(); +} + +// Tests for the Grand Central Dispatch. See +// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html +// for the reference. +void TestGCDDispatchAsync() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block = ^{ worker_do_crash(1024); }; + // dispatch_async() runs the task on a worker thread that does not go through + // pthread_create(). We need to verify that AddressSanitizer notices that the + // thread has started. + dispatch_async(queue, block); + wait_forever(); +} + +void TestGCDDispatchSync() { + dispatch_queue_t queue = dispatch_get_global_queue(2, 0); + dispatch_block_t block = ^{ worker_do_crash(1024); }; + // dispatch_sync() runs the task on a worker thread that does not go through + // pthread_create(). We need to verify that AddressSanitizer notices that the + // thread has started. + dispatch_sync(queue, block); + wait_forever(); +} + +// libdispatch spawns a rather small number of threads and reuses them. We need +// to make sure AddressSanitizer handles the reusing correctly. +void TestGCDReuseWqthreadsAsync() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; + dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; + for (int i = 0; i < 100; i++) { + dispatch_async(queue, block_alloc); + } + dispatch_async(queue, block_crash); + wait_forever(); +} + +// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us. +void TestGCDReuseWqthreadsSync() { + dispatch_queue_t queue[4]; + queue[0] = dispatch_get_global_queue(2, 0); + queue[1] = dispatch_get_global_queue(0, 0); + queue[2] = dispatch_get_global_queue(-2, 0); + queue[3] = dispatch_queue_create("my_queue", NULL); + dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); }; + dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; + for (int i = 0; i < 1000; i++) { + dispatch_sync(queue[i % 4], block_alloc); + } + dispatch_sync(queue[3], block_crash); + wait_forever(); +} + +void TestGCDDispatchAfter() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block_crash = ^{ worker_do_crash(1024); }; + // Schedule the event one second from the current time. + dispatch_time_t milestone = + dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); + dispatch_after(milestone, queue, block_crash); + wait_forever(); +} + +void worker_do_deallocate(void *ptr) { + free(ptr); +} + +void CallFreeOnWorkqueue(void *tsd) { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); }; + dispatch_async(queue, block_dealloc); + // Do not wait for the worker to free the memory -- nobody is going to touch + // it. +} + +void TestGCDSourceEvent() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_source_t timer = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + // Schedule the timer one second from the current time. + dispatch_time_t milestone = + dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); + + dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); + char * volatile mem = (char * volatile)malloc(10); + dispatch_source_set_event_handler(timer, ^{ + access_memory(&mem[10]); + }); + dispatch_resume(timer); + wait_forever(); +} + +void TestGCDSourceCancel() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_source_t timer = + dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + // Schedule the timer one second from the current time. + dispatch_time_t milestone = + dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC); + + dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); + char * volatile mem = (char * volatile)malloc(10); + // Both dispatch_source_set_cancel_handler() and + // dispatch_source_set_event_handler() use dispatch_barrier_async_f(). + // It's tricky to test dispatch_source_set_cancel_handler() separately, + // so we test both here. + dispatch_source_set_event_handler(timer, ^{ + dispatch_source_cancel(timer); + }); + dispatch_source_set_cancel_handler(timer, ^{ + access_memory(&mem[10]); + }); + dispatch_resume(timer); + wait_forever(); +} + +void TestGCDGroupAsync() { + dispatch_queue_t queue = dispatch_get_global_queue(0, 0); + dispatch_group_t group = dispatch_group_create(); + char * volatile mem = (char * volatile)malloc(10); + dispatch_group_async(group, queue, ^{ + access_memory(&mem[10]); + }); + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + wait_forever(); +} + +@interface FixedArray : NSObject { + int items[10]; +} +@end + +@implementation FixedArray +-(int) access: (int)index { + return items[index]; +} +@end + +void TestOOBNSObjects() { + id anObject = [FixedArray new]; + [anObject access:1]; + [anObject access:11]; + [anObject release]; +} + +void TestNSURLDeallocation() { + NSURL *base = + [[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"]; + volatile NSURL *u = + [[NSURL alloc] initWithString:@"Saved Application State" + relativeToURL:base]; + [u release]; +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_mem_test.cc b/projects/compiler-rt/lib/asan/tests/asan_mem_test.cc new file mode 100644 index 00000000..60f5cd4c --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_mem_test.cc @@ -0,0 +1,240 @@ +//===-- asan_mem_test.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +template +void MemSetOOBTestTemplate(size_t length) { + if (length == 0) return; + size_t size = Ident(sizeof(T) * length); + T *array = Ident((T*)malloc(size)); + int element = Ident(42); + int zero = Ident(0); + void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset); + // memset interval inside array + MEMSET(array, element, size); + MEMSET(array, element, size - 1); + MEMSET(array + length - 1, element, sizeof(T)); + MEMSET(array, element, 1); + + // memset 0 bytes + MEMSET(array - 10, element, zero); + MEMSET(array - 1, element, zero); + MEMSET(array, element, zero); + MEMSET(array + length, 0, zero); + MEMSET(array + length + 1, 0, zero); + + // try to memset bytes to the right of array + EXPECT_DEATH(MEMSET(array, 0, size + 1), + RightOOBWriteMessage(0)); + EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6), + RightOOBWriteMessage(0)); + EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)), + RightOOBWriteMessage(0)); + // whole interval is to the right + EXPECT_DEATH(MEMSET(array + length + 1, 0, 10), + RightOOBWriteMessage(sizeof(T))); + + // try to memset bytes to the left of array + EXPECT_DEATH(MEMSET((char*)array - 1, element, size), + LeftOOBWriteMessage(1)); + EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6), + LeftOOBWriteMessage(5)); + if (length >= 100) { + // Large OOB, we find it only if the redzone is large enough. + EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)), + LeftOOBWriteMessage(5 * sizeof(T))); + } + // whole interval is to the left + EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)), + LeftOOBWriteMessage(2 * sizeof(T))); + + // try to memset bytes both to the left & to the right + EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4), + LeftOOBWriteMessage(2)); + + free(array); +} + +TEST(AddressSanitizer, MemSetOOBTest) { + MemSetOOBTestTemplate(100); + MemSetOOBTestTemplate(5); + MemSetOOBTestTemplate(256); + // We can test arrays of structres/classes here, but what for? +} + +// Try to allocate two arrays of 'size' bytes that are near each other. +// Strictly speaking we are not guaranteed to find such two pointers, +// but given the structure of asan's allocator we will. +static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) { + vector v; + bool res = false; + for (size_t i = 0; i < 1000U && !res; i++) { + v.push_back(new char[size]); + if (i == 0) continue; + sort(v.begin(), v.end()); + for (size_t j = 1; j < v.size(); j++) { + assert(v[j] > v[j-1]); + if ((size_t)(v[j] - v[j-1]) < size * 2) { + *x2 = v[j]; + *x1 = v[j-1]; + res = true; + break; + } + } + } + + for (size_t i = 0; i < v.size(); i++) { + if (res && v[i] == *x1) continue; + if (res && v[i] == *x2) continue; + delete [] v[i]; + } + return res; +} + +TEST(AddressSanitizer, LargeOOBInMemset) { + for (size_t size = 200; size < 100000; size += size / 2) { + char *x1, *x2; + if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size)) + continue; + // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size); + // Do a memset on x1 with huge out-of-bound access that will end up in x2. + EXPECT_DEATH(Ident(memset)(x1, 0, size * 2), + "is located 0 bytes to the right"); + delete [] x1; + delete [] x2; + return; + } + assert(0 && "Did not find two adjacent malloc-ed pointers"); +} + +// Same test for memcpy and memmove functions +template +void MemTransferOOBTestTemplate(size_t length) { + if (length == 0) return; + size_t size = Ident(sizeof(T) * length); + T *src = Ident((T*)malloc(size)); + T *dest = Ident((T*)malloc(size)); + int zero = Ident(0); + + // valid transfer of bytes between arrays + M::transfer(dest, src, size); + M::transfer(dest + 1, src, size - sizeof(T)); + M::transfer(dest, src + length - 1, sizeof(T)); + M::transfer(dest, src, 1); + + // transfer zero bytes + M::transfer(dest - 1, src, 0); + M::transfer(dest + length, src, zero); + M::transfer(dest, src - 1, zero); + M::transfer(dest, src, zero); + + // try to change mem to the right of dest + EXPECT_DEATH(M::transfer(dest + 1, src, size), + RightOOBWriteMessage(0)); + EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5), + RightOOBWriteMessage(0)); + + // try to change mem to the left of dest + EXPECT_DEATH(M::transfer(dest - 2, src, size), + LeftOOBWriteMessage(2 * sizeof(T))); + EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4), + LeftOOBWriteMessage(3)); + + // try to access mem to the right of src + EXPECT_DEATH(M::transfer(dest, src + 2, size), + RightOOBReadMessage(0)); + EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6), + RightOOBReadMessage(0)); + + // try to access mem to the left of src + EXPECT_DEATH(M::transfer(dest, src - 1, size), + LeftOOBReadMessage(sizeof(T))); + EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7), + LeftOOBReadMessage(6)); + + // Generally we don't need to test cases where both accessing src and writing + // to dest address to poisoned memory. + + T *big_src = Ident((T*)malloc(size * 2)); + T *big_dest = Ident((T*)malloc(size * 2)); + // try to change mem to both sides of dest + EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2), + LeftOOBWriteMessage(sizeof(T))); + // try to access mem to both sides of src + EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2), + LeftOOBReadMessage(2 * sizeof(T))); + + free(src); + free(dest); + free(big_src); + free(big_dest); +} + +class MemCpyWrapper { + public: + static void* transfer(void *to, const void *from, size_t size) { + return Ident(memcpy)(to, from, size); + } +}; + +TEST(AddressSanitizer, MemCpyOOBTest) { + MemTransferOOBTestTemplate(100); + MemTransferOOBTestTemplate(1024); +} + +class MemMoveWrapper { + public: + static void* transfer(void *to, const void *from, size_t size) { + return Ident(memmove)(to, from, size); + } +}; + +TEST(AddressSanitizer, MemMoveOOBTest) { + MemTransferOOBTestTemplate(100); + MemTransferOOBTestTemplate(1024); +} + + +TEST(AddressSanitizer, MemCmpOOBTest) { + size_t size = Ident(100); + char *s1 = MallocAndMemsetString(size); + char *s2 = MallocAndMemsetString(size); + // Normal memcmp calls. + Ident(memcmp(s1, s2, size)); + Ident(memcmp(s1 + size - 1, s2 + size - 1, 1)); + Ident(memcmp(s1 - 1, s2 - 1, 0)); + // One of arguments points to not allocated memory. + EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); + // Hit unallocated memory and die. + EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); + // Zero bytes are not terminators and don't prevent from OOB. + s1[size - 1] = '\0'; + s2[size - 1] = '\0'; + EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); + + // Even if the buffers differ in the first byte, we still assume that + // memcmp may access the whole buffer and thus reporting the overflow here: + s1[0] = 1; + s2[0] = 123; + EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); + + free(s1); + free(s2); +} + + + diff --git a/projects/compiler-rt/lib/asan/tests/asan_noinst_test.cc b/projects/compiler-rt/lib/asan/tests/asan_noinst_test.cc new file mode 100644 index 00000000..cb6223c0 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_noinst_test.cc @@ -0,0 +1,247 @@ +//===-- asan_noinst_test.cc -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This test file should be compiled w/o asan instrumentation. +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_test_utils.h" + +#include +#include +#include +#include // for memset() +#include +#include +#include + +#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 +// Manually set correct ASan mapping scale and offset, as they won't be +// exported from instrumented sources (there are none). +# define FLEXIBLE_SHADOW_SCALE kDefaultShadowScale +# if SANITIZER_ANDROID +# define FLEXIBLE_SHADOW_OFFSET (0) +# else +# if SANITIZER_WORDSIZE == 32 +# if defined(__mips__) +# define FLEXIBLE_SHADOW_OFFSET kMIPS32_ShadowOffset32 +# else +# define FLEXIBLE_SHADOW_OFFSET kDefaultShadowOffset32 +# endif +# else +# if defined(__powerpc64__) +# define FLEXIBLE_SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif SANITIZER_MAC +# define FLEXIBLE_SHADOW_OFFSET kDefaultShadowOffset64 +# else +# define FLEXIBLE_SHADOW_OFFSET kDefaultShort64bitShadowOffset +# endif +# endif +# endif +SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale = FLEXIBLE_SHADOW_SCALE; +SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset = + FLEXIBLE_SHADOW_OFFSET; +#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET + +extern "C" { +// Set specific ASan options for uninstrumented unittest. +const char* __asan_default_options() { + return "allow_reexec=0"; +} +} // extern "C" + +// Make sure __asan_init is called before any test case is run. +struct AsanInitCaller { + AsanInitCaller() { __asan_init(); } +}; +static AsanInitCaller asan_init_caller; + +TEST(AddressSanitizer, InternalSimpleDeathTest) { + EXPECT_DEATH(exit(1), ""); +} + +static void MallocStress(size_t n) { + u32 seed = my_rand(); + StackTrace stack1; + stack1.trace[0] = 0xa123; + stack1.trace[1] = 0xa456; + stack1.size = 2; + + StackTrace stack2; + stack2.trace[0] = 0xb123; + stack2.trace[1] = 0xb456; + stack2.size = 2; + + StackTrace stack3; + stack3.trace[0] = 0xc123; + stack3.trace[1] = 0xc456; + stack3.size = 2; + + std::vector vec; + for (size_t i = 0; i < n; i++) { + if ((i % 3) == 0) { + if (vec.empty()) continue; + size_t idx = my_rand_r(&seed) % vec.size(); + void *ptr = vec[idx]; + vec[idx] = vec.back(); + vec.pop_back(); + __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC); + } else { + size_t size = my_rand_r(&seed) % 1000 + 1; + switch ((my_rand_r(&seed) % 128)) { + case 0: size += 1024; break; + case 1: size += 2048; break; + case 2: size += 4096; break; + } + size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1); + char *ptr = (char*)__asan::asan_memalign(alignment, size, + &stack2, __asan::FROM_MALLOC); + EXPECT_EQ(size, __asan::asan_malloc_usable_size(ptr, 0, 0)); + vec.push_back(ptr); + ptr[0] = 0; + ptr[size-1] = 0; + ptr[size/2] = 0; + } + } + for (size_t i = 0; i < vec.size(); i++) + __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC); +} + + +TEST(AddressSanitizer, NoInstMallocTest) { + MallocStress(ASAN_LOW_MEMORY ? 300000 : 1000000); +} + +TEST(AddressSanitizer, ThreadedMallocStressTest) { + const int kNumThreads = 4; + const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000; + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress, + (void*)kNumIterations); + } + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_JOIN(t[i], 0); + } +} + +static void PrintShadow(const char *tag, uptr ptr, size_t size) { + fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size); + uptr prev_shadow = 0; + for (sptr i = -32; i < (sptr)size + 32; i++) { + uptr shadow = __asan::MemToShadow(ptr + i); + if (i == 0 || i == (sptr)size) + fprintf(stderr, "."); + if (shadow != prev_shadow) { + prev_shadow = shadow; + fprintf(stderr, "%02x", (int)*(u8*)shadow); + } + } + fprintf(stderr, "\n"); +} + +TEST(AddressSanitizer, DISABLED_InternalPrintShadow) { + for (size_t size = 1; size <= 513; size++) { + char *ptr = new char[size]; + PrintShadow("m", (uptr)ptr, size); + delete [] ptr; + PrintShadow("f", (uptr)ptr, size); + } +} + +TEST(AddressSanitizer, QuarantineTest) { + StackTrace stack; + stack.trace[0] = 0x890; + stack.size = 1; + + const int size = 1024; + void *p = __asan::asan_malloc(size, &stack); + __asan::asan_free(p, &stack, __asan::FROM_MALLOC); + size_t i; + size_t max_i = 1 << 30; + for (i = 0; i < max_i; i++) { + void *p1 = __asan::asan_malloc(size, &stack); + __asan::asan_free(p1, &stack, __asan::FROM_MALLOC); + if (p1 == p) break; + } + EXPECT_GE(i, 10000U); + EXPECT_LT(i, max_i); +} + +void *ThreadedQuarantineTestWorker(void *unused) { + (void)unused; + u32 seed = my_rand(); + StackTrace stack; + stack.trace[0] = 0x890; + stack.size = 1; + + for (size_t i = 0; i < 1000; i++) { + void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack); + __asan::asan_free(p, &stack, __asan::FROM_MALLOC); + } + return NULL; +} + +// Check that the thread local allocators are flushed when threads are +// destroyed. +TEST(AddressSanitizer, ThreadedQuarantineTest) { + const int n_threads = 3000; + size_t mmaped1 = __asan_get_heap_size(); + for (int i = 0; i < n_threads; i++) { + pthread_t t; + PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0); + PTHREAD_JOIN(t, 0); + size_t mmaped2 = __asan_get_heap_size(); + EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20)); + } +} + +void *ThreadedOneSizeMallocStress(void *unused) { + (void)unused; + StackTrace stack; + stack.trace[0] = 0x890; + stack.size = 1; + const size_t kNumMallocs = 1000; + for (int iter = 0; iter < 1000; iter++) { + void *p[kNumMallocs]; + for (size_t i = 0; i < kNumMallocs; i++) { + p[i] = __asan::asan_malloc(32, &stack); + } + for (size_t i = 0; i < kNumMallocs; i++) { + __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC); + } + } + return NULL; +} + +TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { + const int kNumThreads = 4; + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0); + } + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_JOIN(t[i], 0); + } +} + +TEST(AddressSanitizer, ShadowRegionIsPoisonedTest) { + using __asan::kHighMemEnd; + // Check that __asan_region_is_poisoned works for shadow regions. + uptr ptr = kLowShadowBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); + ptr = kShadowGapBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); + ptr = kHighShadowBeg + 200; + EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_oob_test.cc b/projects/compiler-rt/lib/asan/tests/asan_oob_test.cc new file mode 100644 index 00000000..f8343f19 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_oob_test.cc @@ -0,0 +1,126 @@ +//===-- asan_oob_test.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) { + EXPECT_EQ(0U, ((uintptr_t)p % size)); + if (size == 1) asan_write((uint8_t*)p); + else if (size == 2) asan_write((uint16_t*)p); + else if (size == 4) asan_write((uint32_t*)p); + else if (size == 8) asan_write((uint64_t*)p); +} + +template +NOINLINE void oob_test(int size, int off) { + char *p = (char*)malloc_aaa(size); + // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n", + // sizeof(T), p, p + size, off); + asan_write((T*)(p + off)); + free_aaa(p); +} + +template +void OOBTest() { + char expected_str[100]; + for (int size = sizeof(T); size < 20; size += 5) { + for (int i = -5; i < 0; i++) { + const char *str = + "is located.*%d byte.*to the left"; + sprintf(expected_str, str, abs(i)); + EXPECT_DEATH(oob_test(size, i), expected_str); + } + + for (int i = 0; i < (int)(size - sizeof(T) + 1); i++) + oob_test(size, i); + + for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) { + const char *str = + "is located.*%d byte.*to the right"; + int off = i >= size ? (i - size) : 0; + // we don't catch unaligned partially OOB accesses. + if (i % sizeof(T)) continue; + sprintf(expected_str, str, off); + EXPECT_DEATH(oob_test(size, i), expected_str); + } + } + + EXPECT_DEATH(oob_test(kLargeMalloc, -1), + "is located.*1 byte.*to the left"); + EXPECT_DEATH(oob_test(kLargeMalloc, kLargeMalloc), + "is located.*0 byte.*to the right"); +} + +// TODO(glider): the following tests are EXTREMELY slow on Darwin: +// AddressSanitizer.OOB_char (125503 ms) +// AddressSanitizer.OOB_int (126890 ms) +// AddressSanitizer.OOBRightTest (315605 ms) +// AddressSanitizer.SimpleStackTest (366559 ms) + +TEST(AddressSanitizer, OOB_char) { + OOBTest(); +} + +TEST(AddressSanitizer, OOB_int) { + OOBTest(); +} + +TEST(AddressSanitizer, OOBRightTest) { + for (size_t access_size = 1; access_size <= 8; access_size *= 2) { + for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) { + for (size_t offset = 0; offset <= 8; offset += access_size) { + void *p = malloc(alloc_size); + // allocated: [p, p + alloc_size) + // accessed: [p + offset, p + offset + access_size) + uint8_t *addr = (uint8_t*)p + offset; + if (offset + access_size <= alloc_size) { + asan_write_sized_aligned(addr, access_size); + } else { + int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0; + const char *str = + "is located.%d *byte.*to the right"; + char expected_str[100]; + sprintf(expected_str, str, outside_bytes); + EXPECT_DEATH(asan_write_sized_aligned(addr, access_size), + expected_str); + } + free(p); + } + } + } +} + +TEST(AddressSanitizer, LargeOOBRightTest) { + size_t large_power_of_two = 1 << 19; + for (size_t i = 16; i <= 256; i *= 2) { + size_t size = large_power_of_two - i; + char *p = Ident(new char[size]); + EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right"); + delete [] p; + } +} + +TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) { + oob_test(10, -1); +} + +TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) { + oob_test(kLargeMalloc, -1); +} + +TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) { + oob_test(10, 10); +} + +TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) { + oob_test(kLargeMalloc, kLargeMalloc); +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_racy_double_free_test.cc b/projects/compiler-rt/lib/asan/tests/asan_racy_double_free_test.cc new file mode 100644 index 00000000..deeeb4f4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_racy_double_free_test.cc @@ -0,0 +1,32 @@ +#include +#include +#include + +const int N = 1000; +void *x[N]; + +void *Thread1(void *unused) { + for (int i = 0; i < N; i++) { + fprintf(stderr, "%s %d\n", __FUNCTION__, i); + free(x[i]); + } + return NULL; +} + +void *Thread2(void *unused) { + for (int i = 0; i < N; i++) { + fprintf(stderr, "%s %d\n", __FUNCTION__, i); + free(x[i]); + } + return NULL; +} + +int main() { + for (int i = 0; i < N; i++) + x[i] = malloc(128); + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_str_test.cc b/projects/compiler-rt/lib/asan/tests/asan_str_test.cc new file mode 100644 index 00000000..178d00d4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_str_test.cc @@ -0,0 +1,588 @@ +//=-- asan_str_test.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +#if defined(__APPLE__) +#include // For MAC_OS_X_VERSION_* +#endif + +// Used for string functions tests +static char global_string[] = "global"; +static size_t global_string_length = 6; + +// Input to a test is a zero-terminated string str with given length +// Accesses to the bytes to the left and to the right of str +// are presumed to produce OOB errors +void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) { + // Normal strlen calls + EXPECT_EQ(strlen(str), length); + if (length > 0) { + EXPECT_EQ(length - 1, strlen(str + 1)); + EXPECT_EQ(0U, strlen(str + length)); + } + // Arg of strlen is not malloced, OOB access + if (!is_global) { + // We don't insert RedZones to the left of global variables + EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5)); + } + EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0)); + // Overwrite terminator + str[length] = 'a'; + // String is not zero-terminated, strlen will lead to OOB access + EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0)); + // Restore terminator + str[length] = 0; +} +TEST(AddressSanitizer, StrLenOOBTest) { + // Check heap-allocated string + size_t length = Ident(10); + char *heap_string = Ident((char*)malloc(length + 1)); + char stack_string[10 + 1]; + break_optimization(&stack_string); + for (size_t i = 0; i < length; i++) { + heap_string[i] = 'a'; + stack_string[i] = 'b'; + } + heap_string[length] = 0; + stack_string[length] = 0; + StrLenOOBTestTemplate(heap_string, length, false); + // TODO(samsonov): Fix expected messages in StrLenOOBTestTemplate to + // make test for stack_string work. Or move it to output tests. + // StrLenOOBTestTemplate(stack_string, length, false); + StrLenOOBTestTemplate(global_string, global_string_length, true); + free(heap_string); +} + +TEST(AddressSanitizer, WcsLenTest) { + EXPECT_EQ(0U, wcslen(Ident(L""))); + size_t hello_len = 13; + size_t hello_size = (hello_len + 1) * sizeof(wchar_t); + EXPECT_EQ(hello_len, wcslen(Ident(L"Hello, World!"))); + wchar_t *heap_string = Ident((wchar_t*)malloc(hello_size)); + memcpy(heap_string, L"Hello, World!", hello_size); + EXPECT_EQ(hello_len, Ident(wcslen(heap_string))); + EXPECT_DEATH(Ident(wcslen(heap_string + 14)), RightOOBReadMessage(0)); + free(heap_string); +} + +#ifndef __APPLE__ +TEST(AddressSanitizer, StrNLenOOBTest) { + size_t size = Ident(123); + char *str = MallocAndMemsetString(size); + // Normal strnlen calls. + Ident(strnlen(str - 1, 0)); + Ident(strnlen(str, size)); + Ident(strnlen(str + size - 1, 1)); + str[size - 1] = '\0'; + Ident(strnlen(str, 2 * size)); + // Argument points to not allocated memory. + EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0)); + // Overwrite the terminating '\0' and hit unallocated memory. + str[size - 1] = 'z'; + EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0)); + free(str); +} +#endif + +TEST(AddressSanitizer, StrDupOOBTest) { + size_t size = Ident(42); + char *str = MallocAndMemsetString(size); + char *new_str; + // Normal strdup calls. + str[size - 1] = '\0'; + new_str = strdup(str); + free(new_str); + new_str = strdup(str + size - 1); + free(new_str); + // Argument points to not allocated memory. + EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0)); + // Overwrite the terminating '\0' and hit unallocated memory. + str[size - 1] = 'z'; + EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0)); + free(str); +} + +TEST(AddressSanitizer, StrCpyOOBTest) { + size_t to_size = Ident(30); + size_t from_size = Ident(6); // less than to_size + char *to = Ident((char*)malloc(to_size)); + char *from = Ident((char*)malloc(from_size)); + // Normal strcpy calls. + strcpy(from, "hello"); + strcpy(to, from); + strcpy(to + to_size - from_size, from); + // Length of "from" is too small. + EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0)); + // "to" or "from" points to not allocated memory. + EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1)); + EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0)); + // Overwrite the terminating '\0' character and hit unallocated memory. + from[from_size - 1] = '!'; + EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0)); + free(to); + free(from); +} + +TEST(AddressSanitizer, StrNCpyOOBTest) { + size_t to_size = Ident(20); + size_t from_size = Ident(6); // less than to_size + char *to = Ident((char*)malloc(to_size)); + // From is a zero-terminated string "hello\0" of length 6 + char *from = Ident((char*)malloc(from_size)); + strcpy(from, "hello"); + // copy 0 bytes + strncpy(to, from, 0); + strncpy(to - 1, from - 1, 0); + // normal strncpy calls + strncpy(to, from, from_size); + strncpy(to, from, to_size); + strncpy(to, from + from_size - 1, to_size); + strncpy(to + to_size - 1, from, 1); + // One of {to, from} points to not allocated memory + EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)), + LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)), + LeftOOBWriteMessage(1)); + EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)), + RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)), + RightOOBWriteMessage(0)); + // Length of "to" is too small + EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)), + RightOOBWriteMessage(0)); + EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)), + RightOOBWriteMessage(0)); + // Overwrite terminator in from + from[from_size - 1] = '!'; + // normal strncpy call + strncpy(to, from, from_size); + // Length of "from" is too small + EXPECT_DEATH(Ident(strncpy(to, from, to_size)), + RightOOBReadMessage(0)); + free(to); + free(from); +} + +// Users may have different definitions of "strchr" and "index", so provide +// function pointer typedefs and overload RunStrChrTest implementation. +// We can't use macro for RunStrChrTest body here, as this macro would +// confuse EXPECT_DEATH gtest macro. +typedef char*(*PointerToStrChr1)(const char*, int); +typedef char*(*PointerToStrChr2)(char*, int); + +USED static void RunStrChrTest(PointerToStrChr1 StrChr) { + size_t size = Ident(100); + char *str = MallocAndMemsetString(size); + str[10] = 'q'; + str[11] = '\0'; + EXPECT_EQ(str, StrChr(str, 'z')); + EXPECT_EQ(str + 10, StrChr(str, 'q')); + EXPECT_EQ(NULL, StrChr(str, 'a')); + // StrChr argument points to not allocated memory. + EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0)); + // Overwrite the terminator and hit not allocated memory. + str[11] = 'z'; + EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0)); + free(str); +} +USED static void RunStrChrTest(PointerToStrChr2 StrChr) { + size_t size = Ident(100); + char *str = MallocAndMemsetString(size); + str[10] = 'q'; + str[11] = '\0'; + EXPECT_EQ(str, StrChr(str, 'z')); + EXPECT_EQ(str + 10, StrChr(str, 'q')); + EXPECT_EQ(NULL, StrChr(str, 'a')); + // StrChr argument points to not allocated memory. + EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0)); + // Overwrite the terminator and hit not allocated memory. + str[11] = 'z'; + EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0)); + free(str); +} + +TEST(AddressSanitizer, StrChrAndIndexOOBTest) { + RunStrChrTest(&strchr); + RunStrChrTest(&index); +} + +TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) { + // strcmp + EXPECT_EQ(0, strcmp("", "")); + EXPECT_EQ(0, strcmp("abcd", "abcd")); + EXPECT_GT(0, strcmp("ab", "ac")); + EXPECT_GT(0, strcmp("abc", "abcd")); + EXPECT_LT(0, strcmp("acc", "abc")); + EXPECT_LT(0, strcmp("abcd", "abc")); + + // strncmp + EXPECT_EQ(0, strncmp("a", "b", 0)); + EXPECT_EQ(0, strncmp("abcd", "abcd", 10)); + EXPECT_EQ(0, strncmp("abcd", "abcef", 3)); + EXPECT_GT(0, strncmp("abcde", "abcfa", 4)); + EXPECT_GT(0, strncmp("a", "b", 5)); + EXPECT_GT(0, strncmp("bc", "bcde", 4)); + EXPECT_LT(0, strncmp("xyz", "xyy", 10)); + EXPECT_LT(0, strncmp("baa", "aaa", 1)); + EXPECT_LT(0, strncmp("zyx", "", 2)); + + // strcasecmp + EXPECT_EQ(0, strcasecmp("", "")); + EXPECT_EQ(0, strcasecmp("zzz", "zzz")); + EXPECT_EQ(0, strcasecmp("abCD", "ABcd")); + EXPECT_GT(0, strcasecmp("aB", "Ac")); + EXPECT_GT(0, strcasecmp("ABC", "ABCd")); + EXPECT_LT(0, strcasecmp("acc", "abc")); + EXPECT_LT(0, strcasecmp("ABCd", "abc")); + + // strncasecmp + EXPECT_EQ(0, strncasecmp("a", "b", 0)); + EXPECT_EQ(0, strncasecmp("abCD", "ABcd", 10)); + EXPECT_EQ(0, strncasecmp("abCd", "ABcef", 3)); + EXPECT_GT(0, strncasecmp("abcde", "ABCfa", 4)); + EXPECT_GT(0, strncasecmp("a", "B", 5)); + EXPECT_GT(0, strncasecmp("bc", "BCde", 4)); + EXPECT_LT(0, strncasecmp("xyz", "xyy", 10)); + EXPECT_LT(0, strncasecmp("Baa", "aaa", 1)); + EXPECT_LT(0, strncasecmp("zyx", "", 2)); + + // memcmp + EXPECT_EQ(0, memcmp("a", "b", 0)); + EXPECT_EQ(0, memcmp("ab\0c", "ab\0c", 4)); + EXPECT_GT(0, memcmp("\0ab", "\0ac", 3)); + EXPECT_GT(0, memcmp("abb\0", "abba", 4)); + EXPECT_LT(0, memcmp("ab\0cd", "ab\0c\0", 5)); + EXPECT_LT(0, memcmp("zza", "zyx", 3)); +} + +typedef int(*PointerToStrCmp)(const char*, const char*); +void RunStrCmpTest(PointerToStrCmp StrCmp) { + size_t size = Ident(100); + int fill = 'o'; + char *s1 = MallocAndMemsetString(size, fill); + char *s2 = MallocAndMemsetString(size, fill); + s1[size - 1] = '\0'; + s2[size - 1] = '\0'; + // Normal StrCmp calls + Ident(StrCmp(s1, s2)); + Ident(StrCmp(s1, s2 + size - 1)); + Ident(StrCmp(s1 + size - 1, s2 + size - 1)); + s1[size - 1] = 'z'; + s2[size - 1] = 'x'; + Ident(StrCmp(s1, s2)); + // One of arguments points to not allocated memory. + EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0)); + // Hit unallocated memory and die. + s1[size - 1] = fill; + EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0)); + free(s1); + free(s2); +} + +TEST(AddressSanitizer, StrCmpOOBTest) { + RunStrCmpTest(&strcmp); +} + +TEST(AddressSanitizer, StrCaseCmpOOBTest) { + RunStrCmpTest(&strcasecmp); +} + +typedef int(*PointerToStrNCmp)(const char*, const char*, size_t); +void RunStrNCmpTest(PointerToStrNCmp StrNCmp) { + size_t size = Ident(100); + char *s1 = MallocAndMemsetString(size); + char *s2 = MallocAndMemsetString(size); + s1[size - 1] = '\0'; + s2[size - 1] = '\0'; + // Normal StrNCmp calls + Ident(StrNCmp(s1, s2, size + 2)); + s1[size - 1] = 'z'; + s2[size - 1] = 'x'; + Ident(StrNCmp(s1 + size - 2, s2 + size - 2, size)); + s2[size - 1] = 'z'; + Ident(StrNCmp(s1 - 1, s2 - 1, 0)); + Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1)); + // One of arguments points to not allocated memory. + EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); + // Hit unallocated memory and die. + EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); + free(s1); + free(s2); +} + +TEST(AddressSanitizer, StrNCmpOOBTest) { + RunStrNCmpTest(&strncmp); +} + +TEST(AddressSanitizer, StrNCaseCmpOOBTest) { + RunStrNCmpTest(&strncasecmp); +} +TEST(AddressSanitizer, StrCatOOBTest) { + // strcat() reads strlen(to) bytes from |to| before concatenating. + size_t to_size = Ident(100); + char *to = MallocAndMemsetString(to_size); + to[0] = '\0'; + size_t from_size = Ident(20); + char *from = MallocAndMemsetString(from_size); + from[from_size - 1] = '\0'; + // Normal strcat calls. + strcat(to, from); + strcat(to, from); + strcat(to + from_size, from + from_size - 2); + // Passing an invalid pointer is an error even when concatenating an empty + // string. + EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1)); + // One of arguments points to not allocated memory. + EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1)); + EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0)); + EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0)); + + // "from" is not zero-terminated. + from[from_size - 1] = 'z'; + EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0)); + from[from_size - 1] = '\0'; + // "to" is not zero-terminated. + memset(to, 'z', to_size); + EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0)); + // "to" is too short to fit "from". + to[to_size - from_size + 1] = '\0'; + EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0)); + // length of "to" is just enough. + strcat(to, from + 1); + + free(to); + free(from); +} + +TEST(AddressSanitizer, StrNCatOOBTest) { + // strncat() reads strlen(to) bytes from |to| before concatenating. + size_t to_size = Ident(100); + char *to = MallocAndMemsetString(to_size); + to[0] = '\0'; + size_t from_size = Ident(20); + char *from = MallocAndMemsetString(from_size); + // Normal strncat calls. + strncat(to, from, 0); + strncat(to, from, from_size); + from[from_size - 1] = '\0'; + strncat(to, from, 2 * from_size); + // Catenating empty string with an invalid string is still an error. + EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1)); + strncat(to, from + from_size - 1, 10); + // One of arguments points to not allocated memory. + EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1)); + EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1)); + EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0)); + EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0)); + + memset(from, 'z', from_size); + memset(to, 'z', to_size); + to[0] = '\0'; + // "from" is too short. + EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0)); + // "to" is not zero-terminated. + EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0)); + // "to" is too short to fit "from". + to[0] = 'z'; + to[to_size - from_size + 1] = '\0'; + EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0)); + // "to" is just enough. + strncat(to, from, from_size - 2); + + free(to); + free(from); +} + +static string OverlapErrorMessage(const string &func) { + return func + "-param-overlap"; +} + +TEST(AddressSanitizer, StrArgsOverlapTest) { + size_t size = Ident(100); + char *str = Ident((char*)malloc(size)); + +// Do not check memcpy() on OS X 10.7 and later, where it actually aliases +// memmove(). +#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \ + (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) + // Check "memcpy". Use Ident() to avoid inlining. + memset(str, 'z', size); + Ident(memcpy)(str + 1, str + 11, 10); + Ident(memcpy)(str, str, 0); + EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy")); + EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy")); +#endif + + // We do not treat memcpy with to==from as a bug. + // See http://llvm.org/bugs/show_bug.cgi?id=11763. + // EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1), + // OverlapErrorMessage("memcpy")); + + // Check "strcpy". + memset(str, 'z', size); + str[9] = '\0'; + strcpy(str + 10, str); + EXPECT_DEATH(strcpy(str + 9, str), OverlapErrorMessage("strcpy")); + EXPECT_DEATH(strcpy(str, str + 4), OverlapErrorMessage("strcpy")); + strcpy(str, str + 5); + + // Check "strncpy". + memset(str, 'z', size); + strncpy(str, str + 10, 10); + EXPECT_DEATH(strncpy(str, str + 9, 10), OverlapErrorMessage("strncpy")); + EXPECT_DEATH(strncpy(str + 9, str, 10), OverlapErrorMessage("strncpy")); + str[10] = '\0'; + strncpy(str + 11, str, 20); + EXPECT_DEATH(strncpy(str + 10, str, 20), OverlapErrorMessage("strncpy")); + + // Check "strcat". + memset(str, 'z', size); + str[10] = '\0'; + str[20] = '\0'; + strcat(str, str + 10); + EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat")); + str[10] = '\0'; + strcat(str + 11, str); + EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat")); + EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat")); + EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat")); + + // Check "strncat". + memset(str, 'z', size); + str[10] = '\0'; + strncat(str, str + 10, 10); // from is empty + EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat")); + str[10] = '\0'; + str[20] = '\0'; + strncat(str + 5, str, 5); + str[10] = '\0'; + EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat")); + EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat")); + + free(str); +} + +void CallAtoi(const char *nptr) { + Ident(atoi(nptr)); +} +void CallAtol(const char *nptr) { + Ident(atol(nptr)); +} +void CallAtoll(const char *nptr) { + Ident(atoll(nptr)); +} +typedef void(*PointerToCallAtoi)(const char*); + +void RunAtoiOOBTest(PointerToCallAtoi Atoi) { + char *array = MallocAndMemsetString(10, '1'); + // Invalid pointer to the string. + EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1)); + EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1)); + // Die if a buffer doesn't have terminating NULL. + EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); + // Make last symbol a terminating NULL or other non-digit. + array[9] = '\0'; + Atoi(array); + array[9] = 'a'; + Atoi(array); + Atoi(array + 9); + // Sometimes we need to detect overflow if no digits are found. + memset(array, ' ', 10); + EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); + array[9] = '-'; + EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); + EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0)); + array[8] = '-'; + Atoi(array); + free(array); +} + +TEST(AddressSanitizer, AtoiAndFriendsOOBTest) { + RunAtoiOOBTest(&CallAtoi); + RunAtoiOOBTest(&CallAtol); + RunAtoiOOBTest(&CallAtoll); +} + +void CallStrtol(const char *nptr, char **endptr, int base) { + Ident(strtol(nptr, endptr, base)); +} +void CallStrtoll(const char *nptr, char **endptr, int base) { + Ident(strtoll(nptr, endptr, base)); +} +typedef void(*PointerToCallStrtol)(const char*, char**, int); + +void RunStrtolOOBTest(PointerToCallStrtol Strtol) { + char *array = MallocAndMemsetString(3); + char *endptr = NULL; + array[0] = '1'; + array[1] = '2'; + array[2] = '3'; + // Invalid pointer to the string. + EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0)); + EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1)); + // Buffer overflow if there is no terminating null (depends on base). + Strtol(array, &endptr, 3); + EXPECT_EQ(array + 2, endptr); + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); + array[2] = 'z'; + Strtol(array, &endptr, 35); + EXPECT_EQ(array + 2, endptr); + EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0)); + // Add terminating zero to get rid of overflow. + array[2] = '\0'; + Strtol(array, NULL, 36); + // Don't check for overflow if base is invalid. + Strtol(array - 1, NULL, -1); + Strtol(array + 3, NULL, 1); + // Sometimes we need to detect overflow if no digits are found. + array[0] = array[1] = array[2] = ' '; + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); + array[2] = '+'; + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); + array[2] = '-'; + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); + array[1] = '+'; + Strtol(array, NULL, 0); + array[1] = array[2] = 'z'; + Strtol(array, &endptr, 0); + EXPECT_EQ(array, endptr); + Strtol(array + 2, NULL, 0); + EXPECT_EQ(array, endptr); + free(array); +} + +TEST(AddressSanitizer, StrtollOOBTest) { + RunStrtolOOBTest(&CallStrtoll); +} +TEST(AddressSanitizer, StrtolOOBTest) { + RunStrtolOOBTest(&CallStrtol); +} + + diff --git a/projects/compiler-rt/lib/asan/tests/asan_test.cc b/projects/compiler-rt/lib/asan/tests/asan_test.cc new file mode 100644 index 00000000..6539ae8a --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_test.cc @@ -0,0 +1,1243 @@ +//===-- asan_test.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +NOINLINE void *malloc_fff(size_t size) { + void *res = malloc/**/(size); break_optimization(0); return res;} +NOINLINE void *malloc_eee(size_t size) { + void *res = malloc_fff(size); break_optimization(0); return res;} +NOINLINE void *malloc_ddd(size_t size) { + void *res = malloc_eee(size); break_optimization(0); return res;} +NOINLINE void *malloc_ccc(size_t size) { + void *res = malloc_ddd(size); break_optimization(0); return res;} +NOINLINE void *malloc_bbb(size_t size) { + void *res = malloc_ccc(size); break_optimization(0); return res;} +NOINLINE void *malloc_aaa(size_t size) { + void *res = malloc_bbb(size); break_optimization(0); return res;} + +#ifndef __APPLE__ +NOINLINE void *memalign_fff(size_t alignment, size_t size) { + void *res = memalign/**/(alignment, size); break_optimization(0); return res;} +NOINLINE void *memalign_eee(size_t alignment, size_t size) { + void *res = memalign_fff(alignment, size); break_optimization(0); return res;} +NOINLINE void *memalign_ddd(size_t alignment, size_t size) { + void *res = memalign_eee(alignment, size); break_optimization(0); return res;} +NOINLINE void *memalign_ccc(size_t alignment, size_t size) { + void *res = memalign_ddd(alignment, size); break_optimization(0); return res;} +NOINLINE void *memalign_bbb(size_t alignment, size_t size) { + void *res = memalign_ccc(alignment, size); break_optimization(0); return res;} +NOINLINE void *memalign_aaa(size_t alignment, size_t size) { + void *res = memalign_bbb(alignment, size); break_optimization(0); return res;} +#endif // __APPLE__ + + +NOINLINE void free_ccc(void *p) { free(p); break_optimization(0);} +NOINLINE void free_bbb(void *p) { free_ccc(p); break_optimization(0);} +NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);} + + +template +NOINLINE void uaf_test(int size, int off) { + char *p = (char *)malloc_aaa(size); + free_aaa(p); + for (int i = 1; i < 100; i++) + free_aaa(malloc_aaa(i)); + fprintf(stderr, "writing %ld byte(s) at %p with offset %d\n", + (long)sizeof(T), p, off); + asan_write((T*)(p + off)); +} + +TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) { +#if defined(__has_feature) && __has_feature(address_sanitizer) + bool asan = 1; +#elif defined(__SANITIZE_ADDRESS__) + bool asan = 1; +#else + bool asan = 0; +#endif + EXPECT_EQ(true, asan); +} + +TEST(AddressSanitizer, SimpleDeathTest) { + EXPECT_DEATH(exit(1), ""); +} + +TEST(AddressSanitizer, VariousMallocsTest) { + int *a = (int*)malloc(100 * sizeof(int)); + a[50] = 0; + free(a); + + int *r = (int*)malloc(10); + r = (int*)realloc(r, 2000 * sizeof(int)); + r[1000] = 0; + free(r); + + int *b = new int[100]; + b[50] = 0; + delete [] b; + + int *c = new int; + *c = 0; + delete c; + +#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__) + int *pm; + int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize); + EXPECT_EQ(0, pm_res); + free(pm); +#endif + +#if !defined(__APPLE__) + int *ma = (int*)memalign(kPageSize, kPageSize); + EXPECT_EQ(0U, (uintptr_t)ma % kPageSize); + ma[123] = 0; + free(ma); +#endif // __APPLE__ +} + +TEST(AddressSanitizer, CallocTest) { + int *a = (int*)calloc(100, sizeof(int)); + EXPECT_EQ(0, a[10]); + free(a); +} + +TEST(AddressSanitizer, CallocReturnsZeroMem) { + size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; + for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { + size_t size = sizes[s]; + for (size_t iter = 0; iter < 5; iter++) { + char *x = Ident((char*)calloc(1, size)); + EXPECT_EQ(x[0], 0); + EXPECT_EQ(x[size - 1], 0); + EXPECT_EQ(x[size / 2], 0); + EXPECT_EQ(x[size / 3], 0); + EXPECT_EQ(x[size / 4], 0); + memset(x, 0x42, size); + free(Ident(x)); + free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. + } + } +} + +TEST(AddressSanitizer, VallocTest) { + void *a = valloc(100); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); + free(a); +} + +#ifndef __APPLE__ +TEST(AddressSanitizer, PvallocTest) { + char *a = (char*)pvalloc(kPageSize + 100); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); + a[kPageSize + 101] = 1; // we should not report an error here. + free(a); + + a = (char*)pvalloc(0); // pvalloc(0) should allocate at least one page. + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); + a[101] = 1; // we should not report an error here. + free(a); +} +#endif // __APPLE__ + +void *TSDWorker(void *test_key) { + if (test_key) { + pthread_setspecific(*(pthread_key_t*)test_key, (void*)0xfeedface); + } + return NULL; +} + +void TSDDestructor(void *tsd) { + // Spawning a thread will check that the current thread id is not -1. + pthread_t th; + PTHREAD_CREATE(&th, NULL, TSDWorker, NULL); + PTHREAD_JOIN(th, NULL); +} + +// This tests triggers the thread-specific data destruction fiasco which occurs +// if we don't manage the TSD destructors ourselves. We create a new pthread +// key with a non-NULL destructor which is likely to be put after the destructor +// of AsanThread in the list of destructors. +// In this case the TSD for AsanThread will be destroyed before TSDDestructor +// is called for the child thread, and a CHECK will fail when we call +// pthread_create() to spawn the grandchild. +TEST(AddressSanitizer, DISABLED_TSDTest) { + pthread_t th; + pthread_key_t test_key; + pthread_key_create(&test_key, TSDDestructor); + PTHREAD_CREATE(&th, NULL, TSDWorker, &test_key); + PTHREAD_JOIN(th, NULL); + pthread_key_delete(test_key); +} + +TEST(AddressSanitizer, UAF_char) { + const char *uaf_string = "AddressSanitizer:.*heap-use-after-free"; + EXPECT_DEATH(uaf_test(1, 0), uaf_string); + EXPECT_DEATH(uaf_test(10, 0), uaf_string); + EXPECT_DEATH(uaf_test(10, 10), uaf_string); + EXPECT_DEATH(uaf_test(kLargeMalloc, 0), uaf_string); + EXPECT_DEATH(uaf_test(kLargeMalloc, kLargeMalloc / 2), uaf_string); +} + +TEST(AddressSanitizer, UAF_long_double) { + if (sizeof(long double) == sizeof(double)) return; + long double *p = Ident(new long double[10]); + EXPECT_DEATH(Ident(p)[12] = 0, "WRITE of size 1[06]"); + EXPECT_DEATH(Ident(p)[0] = Ident(p)[12], "READ of size 1[06]"); + delete [] Ident(p); +} + +struct Packed5 { + int x; + char c; +} __attribute__((packed)); + + +TEST(AddressSanitizer, UAF_Packed5) { + Packed5 *p = Ident(new Packed5[2]); + EXPECT_DEATH(p[0] = p[3], "READ of size 5"); + EXPECT_DEATH(p[3] = p[0], "WRITE of size 5"); + delete [] Ident(p); +} + +#if ASAN_HAS_BLACKLIST +TEST(AddressSanitizer, IgnoreTest) { + int *x = Ident(new int); + delete Ident(x); + *x = 0; +} +#endif // ASAN_HAS_BLACKLIST + +struct StructWithBitField { + int bf1:1; + int bf2:1; + int bf3:1; + int bf4:29; +}; + +TEST(AddressSanitizer, BitFieldPositiveTest) { + StructWithBitField *x = new StructWithBitField; + delete Ident(x); + EXPECT_DEATH(x->bf1 = 0, "use-after-free"); + EXPECT_DEATH(x->bf2 = 0, "use-after-free"); + EXPECT_DEATH(x->bf3 = 0, "use-after-free"); + EXPECT_DEATH(x->bf4 = 0, "use-after-free"); +} + +struct StructWithBitFields_8_24 { + int a:8; + int b:24; +}; + +TEST(AddressSanitizer, BitFieldNegativeTest) { + StructWithBitFields_8_24 *x = Ident(new StructWithBitFields_8_24); + x->a = 0; + x->b = 0; + delete Ident(x); +} + +#if ASAN_NEEDS_SEGV +namespace { + +const char kUnknownCrash[] = "AddressSanitizer: SEGV on unknown address"; +const char kOverriddenHandler[] = "ASan signal handler has been overridden\n"; + +TEST(AddressSanitizer, WildAddressTest) { + char *c = (char*)0x123; + EXPECT_DEATH(*c = 0, kUnknownCrash); +} + +void my_sigaction_sighandler(int, siginfo_t*, void*) { + fprintf(stderr, kOverriddenHandler); + exit(1); +} + +void my_signal_sighandler(int signum) { + fprintf(stderr, kOverriddenHandler); + exit(1); +} + +TEST(AddressSanitizer, SignalTest) { + struct sigaction sigact; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = my_sigaction_sighandler; + sigact.sa_flags = SA_SIGINFO; + // ASan should silently ignore sigaction()... + EXPECT_EQ(0, sigaction(SIGSEGV, &sigact, 0)); +#ifdef __APPLE__ + EXPECT_EQ(0, sigaction(SIGBUS, &sigact, 0)); +#endif + char *c = (char*)0x123; + EXPECT_DEATH(*c = 0, kUnknownCrash); + // ... and signal(). + EXPECT_EQ(0, signal(SIGSEGV, my_signal_sighandler)); + EXPECT_DEATH(*c = 0, kUnknownCrash); +} +} // namespace +#endif + +static void TestLargeMalloc(size_t size) { + char buff[1024]; + sprintf(buff, "is located 1 bytes to the left of %lu-byte", (long)size); + EXPECT_DEATH(Ident((char*)malloc(size))[-1] = 0, buff); +} + +TEST(AddressSanitizer, LargeMallocTest) { + const int max_size = (SANITIZER_WORDSIZE == 32) ? 1 << 26 : 1 << 28; + for (int i = 113; i < max_size; i = i * 2 + 13) { + TestLargeMalloc(i); + } +} + +TEST(AddressSanitizer, HugeMallocTest) { + if (SANITIZER_WORDSIZE != 64) return; + size_t n_megs = 4100; + TestLargeMalloc(n_megs << 20); +} + +#ifndef __APPLE__ +void MemalignRun(size_t align, size_t size, int idx) { + char *p = (char *)memalign(align, size); + Ident(p)[idx] = 0; + free(p); +} + +TEST(AddressSanitizer, memalign) { + for (int align = 16; align <= (1 << 23); align *= 2) { + size_t size = align * 5; + EXPECT_DEATH(MemalignRun(align, size, -1), + "is located 1 bytes to the left"); + EXPECT_DEATH(MemalignRun(align, size, size + 1), + "is located 1 bytes to the right"); + } +} +#endif + +void *ManyThreadsWorker(void *a) { + for (int iter = 0; iter < 100; iter++) { + for (size_t size = 100; size < 2000; size *= 2) { + free(Ident(malloc(size))); + } + } + return 0; +} + +TEST(AddressSanitizer, ManyThreadsTest) { + const size_t kNumThreads = + (SANITIZER_WORDSIZE == 32 || ASAN_AVOID_EXPENSIVE_TESTS) ? 30 : 1000; + pthread_t t[kNumThreads]; + for (size_t i = 0; i < kNumThreads; i++) { + PTHREAD_CREATE(&t[i], 0, ManyThreadsWorker, (void*)i); + } + for (size_t i = 0; i < kNumThreads; i++) { + PTHREAD_JOIN(t[i], 0); + } +} + +TEST(AddressSanitizer, ReallocTest) { + const int kMinElem = 5; + int *ptr = (int*)malloc(sizeof(int) * kMinElem); + ptr[3] = 3; + for (int i = 0; i < 10000; i++) { + ptr = (int*)realloc(ptr, + (my_rand() % 1000 + kMinElem) * sizeof(int)); + EXPECT_EQ(3, ptr[3]); + } + free(ptr); + // Realloc pointer returned by malloc(0). + int *ptr2 = Ident((int*)malloc(0)); + ptr2 = Ident((int*)realloc(ptr2, sizeof(*ptr2))); + *ptr2 = 42; + EXPECT_EQ(42, *ptr2); + free(ptr2); +} + +TEST(AddressSanitizer, ReallocFreedPointerTest) { + void *ptr = Ident(malloc(42)); + ASSERT_TRUE(NULL != ptr); + free(ptr); + EXPECT_DEATH(ptr = realloc(ptr, 77), "attempting double-free"); +} + +TEST(AddressSanitizer, ReallocInvalidPointerTest) { + void *ptr = Ident(malloc(42)); + EXPECT_DEATH(ptr = realloc((int*)ptr + 1, 77), "attempting free.*not malloc"); + free(ptr); +} + +TEST(AddressSanitizer, ZeroSizeMallocTest) { + // Test that malloc(0) and similar functions don't return NULL. + void *ptr = Ident(malloc(0)); + EXPECT_TRUE(NULL != ptr); + free(ptr); +#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__) + int pm_res = posix_memalign(&ptr, 1<<20, 0); + EXPECT_EQ(0, pm_res); + EXPECT_TRUE(NULL != ptr); + free(ptr); +#endif + int *int_ptr = new int[0]; + int *int_ptr2 = new int[0]; + EXPECT_TRUE(NULL != int_ptr); + EXPECT_TRUE(NULL != int_ptr2); + EXPECT_NE(int_ptr, int_ptr2); + delete[] int_ptr; + delete[] int_ptr2; +} + +#ifndef __APPLE__ +static const char *kMallocUsableSizeErrorMsg = + "AddressSanitizer: attempting to call malloc_usable_size()"; + +TEST(AddressSanitizer, MallocUsableSizeTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + EXPECT_EQ(0U, malloc_usable_size(NULL)); + EXPECT_EQ(kArraySize, malloc_usable_size(array)); + EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); + EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg); + EXPECT_DEATH(malloc_usable_size(array + kArraySize / 2), + kMallocUsableSizeErrorMsg); + free(array); + EXPECT_DEATH(malloc_usable_size(array), kMallocUsableSizeErrorMsg); + delete int_ptr; +} +#endif + +void WrongFree() { + int *x = (int*)malloc(100 * sizeof(int)); + // Use the allocated memory, otherwise Clang will optimize it out. + Ident(x); + free(x + 1); +} + +TEST(AddressSanitizer, WrongFreeTest) { + EXPECT_DEATH(WrongFree(), ASAN_PCRE_DOTALL + "ERROR: AddressSanitizer: attempting free.*not malloc" + ".*is located 4 bytes inside of 400-byte region" + ".*allocated by thread"); +} + +void DoubleFree() { + int *x = (int*)malloc(100 * sizeof(int)); + fprintf(stderr, "DoubleFree: x=%p\n", x); + free(x); + free(x); + fprintf(stderr, "should have failed in the second free(%p)\n", x); + abort(); +} + +TEST(AddressSanitizer, DoubleFreeTest) { + EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL + "ERROR: AddressSanitizer: attempting double-free" + ".*is located 0 bytes inside of 400-byte region" + ".*freed by thread T0 here" + ".*previously allocated by thread T0 here"); +} + +template +NOINLINE void SizedStackTest() { + char a[kSize]; + char *A = Ident((char*)&a); + for (size_t i = 0; i < kSize; i++) + A[i] = i; + EXPECT_DEATH(A[-1] = 0, ""); + EXPECT_DEATH(A[-20] = 0, ""); + EXPECT_DEATH(A[-31] = 0, ""); + EXPECT_DEATH(A[kSize] = 0, ""); + EXPECT_DEATH(A[kSize + 1] = 0, ""); + EXPECT_DEATH(A[kSize + 10] = 0, ""); + EXPECT_DEATH(A[kSize + 31] = 0, ""); +} + +TEST(AddressSanitizer, SimpleStackTest) { + SizedStackTest<1>(); + SizedStackTest<2>(); + SizedStackTest<3>(); + SizedStackTest<4>(); + SizedStackTest<5>(); + SizedStackTest<6>(); + SizedStackTest<7>(); + SizedStackTest<16>(); + SizedStackTest<25>(); + SizedStackTest<34>(); + SizedStackTest<43>(); + SizedStackTest<51>(); + SizedStackTest<62>(); + SizedStackTest<64>(); + SizedStackTest<128>(); +} + +TEST(AddressSanitizer, ManyStackObjectsTest) { + char XXX[10]; + char YYY[20]; + char ZZZ[30]; + Ident(XXX); + Ident(YYY); + EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ"); +} + +#if 0 // This test requires online symbolizer. +// Moved to lit_tests/stack-oob-frames.cc. +// Reenable here once we have online symbolizer by default. +NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { + char d[4] = {0}; + char *D = Ident(d); + switch (frame) { + case 3: a[5]++; break; + case 2: b[5]++; break; + case 1: c[5]++; break; + case 0: D[5]++; break; + } +} +NOINLINE static void Frame1(int frame, char *a, char *b) { + char c[4] = {0}; Frame0(frame, a, b, c); + break_optimization(0); +} +NOINLINE static void Frame2(int frame, char *a) { + char b[4] = {0}; Frame1(frame, a, b); + break_optimization(0); +} +NOINLINE static void Frame3(int frame) { + char a[4] = {0}; Frame2(frame, a); + break_optimization(0); +} + +TEST(AddressSanitizer, GuiltyStackFrame0Test) { + EXPECT_DEATH(Frame3(0), "located .*in frame <.*Frame0"); +} +TEST(AddressSanitizer, GuiltyStackFrame1Test) { + EXPECT_DEATH(Frame3(1), "located .*in frame <.*Frame1"); +} +TEST(AddressSanitizer, GuiltyStackFrame2Test) { + EXPECT_DEATH(Frame3(2), "located .*in frame <.*Frame2"); +} +TEST(AddressSanitizer, GuiltyStackFrame3Test) { + EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3"); +} +#endif + +NOINLINE void LongJmpFunc1(jmp_buf buf) { + // create three red zones for these two stack objects. + int a; + int b; + + int *A = Ident(&a); + int *B = Ident(&b); + *A = *B; + longjmp(buf, 1); +} + +NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) { + // create three red zones for these two stack objects. + int a; + int b; + + int *A = Ident(&a); + int *B = Ident(&b); + *A = *B; + __builtin_longjmp((void**)buf, 1); +} + +NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) { + // create three red zones for these two stack objects. + int a; + int b; + + int *A = Ident(&a); + int *B = Ident(&b); + *A = *B; + _longjmp(buf, 1); +} + +NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) { + // create three red zones for these two stack objects. + int a; + int b; + + int *A = Ident(&a); + int *B = Ident(&b); + *A = *B; + siglongjmp(buf, 1); +} + + +NOINLINE void TouchStackFunc() { + int a[100]; // long array will intersect with redzones from LongJmpFunc1. + int *A = Ident(a); + for (int i = 0; i < 100; i++) + A[i] = i*i; +} + +// Test that we handle longjmp and do not report fals positives on stack. +TEST(AddressSanitizer, LongJmpTest) { + static jmp_buf buf; + if (!setjmp(buf)) { + LongJmpFunc1(buf); + } else { + TouchStackFunc(); + } +} + +#if !defined(__ANDROID__) && \ + !defined(__powerpc64__) && !defined(__powerpc__) +// Does not work on Power: +// https://code.google.com/p/address-sanitizer/issues/detail?id=185 +TEST(AddressSanitizer, BuiltinLongJmpTest) { + static jmp_buf buf; + if (!__builtin_setjmp((void**)buf)) { + BuiltinLongJmpFunc1(buf); + } else { + TouchStackFunc(); + } +} +#endif // not defined(__ANDROID__) + +TEST(AddressSanitizer, UnderscopeLongJmpTest) { + static jmp_buf buf; + if (!_setjmp(buf)) { + UnderscopeLongJmpFunc1(buf); + } else { + TouchStackFunc(); + } +} + +TEST(AddressSanitizer, SigLongJmpTest) { + static sigjmp_buf buf; + if (!sigsetjmp(buf, 1)) { + SigLongJmpFunc1(buf); + } else { + TouchStackFunc(); + } +} + +#ifdef __EXCEPTIONS +NOINLINE void ThrowFunc() { + // create three red zones for these two stack objects. + int a; + int b; + + int *A = Ident(&a); + int *B = Ident(&b); + *A = *B; + ASAN_THROW(1); +} + +TEST(AddressSanitizer, CxxExceptionTest) { + if (ASAN_UAR) return; + // TODO(kcc): this test crashes on 32-bit for some reason... + if (SANITIZER_WORDSIZE == 32) return; + try { + ThrowFunc(); + } catch(...) {} + TouchStackFunc(); +} +#endif + +void *ThreadStackReuseFunc1(void *unused) { + // create three red zones for these two stack objects. + int a; + int b; + + int *A = Ident(&a); + int *B = Ident(&b); + *A = *B; + pthread_exit(0); + return 0; +} + +void *ThreadStackReuseFunc2(void *unused) { + TouchStackFunc(); + return 0; +} + +TEST(AddressSanitizer, ThreadStackReuseTest) { + pthread_t t; + PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc1, 0); + PTHREAD_JOIN(t, 0); + PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc2, 0); + PTHREAD_JOIN(t, 0); +} + +#if defined(__i686__) || defined(__x86_64__) +#include +TEST(AddressSanitizer, Store128Test) { + char *a = Ident((char*)malloc(Ident(12))); + char *p = a; + if (((uintptr_t)a % 16) != 0) + p = a + 8; + assert(((uintptr_t)p % 16) == 0); + __m128i value_wide = _mm_set1_epi16(0x1234); + EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), + "AddressSanitizer: heap-buffer-overflow"); + EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), + "WRITE of size 16"); + EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), + "located 0 bytes to the right of 12-byte"); + free(a); +} +#endif + +string RightOOBErrorMessage(int oob_distance, bool is_write) { + assert(oob_distance >= 0); + char expected_str[100]; + sprintf(expected_str, ASAN_PCRE_DOTALL + "buffer-overflow.*%s.*located %d bytes to the right", + is_write ? "WRITE" : "READ", oob_distance); + return string(expected_str); +} + +string RightOOBWriteMessage(int oob_distance) { + return RightOOBErrorMessage(oob_distance, /*is_write*/true); +} + +string RightOOBReadMessage(int oob_distance) { + return RightOOBErrorMessage(oob_distance, /*is_write*/false); +} + +string LeftOOBErrorMessage(int oob_distance, bool is_write) { + assert(oob_distance > 0); + char expected_str[100]; + sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the left", + is_write ? "WRITE" : "READ", oob_distance); + return string(expected_str); +} + +string LeftOOBWriteMessage(int oob_distance) { + return LeftOOBErrorMessage(oob_distance, /*is_write*/true); +} + +string LeftOOBReadMessage(int oob_distance) { + return LeftOOBErrorMessage(oob_distance, /*is_write*/false); +} + +string LeftOOBAccessMessage(int oob_distance) { + assert(oob_distance > 0); + char expected_str[100]; + sprintf(expected_str, "located %d bytes to the left", oob_distance); + return string(expected_str); +} + +char* MallocAndMemsetString(size_t size, char ch) { + char *s = Ident((char*)malloc(size)); + memset(s, ch, size); + return s; +} + +char* MallocAndMemsetString(size_t size) { + return MallocAndMemsetString(size, 'z'); +} + +#if defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__) +#define READ_TEST(READ_N_BYTES) \ + char *x = new char[10]; \ + int fd = open("/proc/self/stat", O_RDONLY); \ + ASSERT_GT(fd, 0); \ + EXPECT_DEATH(READ_N_BYTES, \ + ASAN_PCRE_DOTALL \ + "AddressSanitizer: heap-buffer-overflow" \ + ".* is located 0 bytes to the right of 10-byte region"); \ + close(fd); \ + delete [] x; \ + +TEST(AddressSanitizer, pread) { + READ_TEST(pread(fd, x, 15, 0)); +} + +TEST(AddressSanitizer, pread64) { + READ_TEST(pread64(fd, x, 15, 0)); +} + +TEST(AddressSanitizer, read) { + READ_TEST(read(fd, x, 15)); +} +#endif // defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__) + +// This test case fails +// Clang optimizes memcpy/memset calls which lead to unaligned access +TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) { + int size = Ident(4096); + char *s = Ident((char*)malloc(size)); + EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBWriteMessage(0)); + free(s); +} + +// TODO(samsonov): Add a test with malloc(0) +// TODO(samsonov): Add tests for str* and mem* functions. + +NOINLINE static int LargeFunction(bool do_bad_access) { + int *x = new int[100]; + x[0]++; + x[1]++; + x[2]++; + x[3]++; + x[4]++; + x[5]++; + x[6]++; + x[7]++; + x[8]++; + x[9]++; + + x[do_bad_access ? 100 : 0]++; int res = __LINE__; + + x[10]++; + x[11]++; + x[12]++; + x[13]++; + x[14]++; + x[15]++; + x[16]++; + x[17]++; + x[18]++; + x[19]++; + + delete x; + return res; +} + +// Test the we have correct debug info for the failing instruction. +// This test requires the in-process symbolizer to be enabled by default. +TEST(AddressSanitizer, DISABLED_LargeFunctionSymbolizeTest) { + int failing_line = LargeFunction(false); + char expected_warning[128]; + sprintf(expected_warning, "LargeFunction.*asan_test.*:%d", failing_line); + EXPECT_DEATH(LargeFunction(true), expected_warning); +} + +// Check that we unwind and symbolize correctly. +TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) { + int *a = (int*)malloc_aaa(sizeof(int)); + *a = 1; + free_aaa(a); + EXPECT_DEATH(*a = 1, "free_ccc.*free_bbb.*free_aaa.*" + "malloc_fff.*malloc_eee.*malloc_ddd"); +} + +static bool TryToSetThreadName(const char *name) { +#if defined(__linux__) && defined(PR_SET_NAME) + return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); +#else + return false; +#endif +} + +void *ThreadedTestAlloc(void *a) { + EXPECT_EQ(true, TryToSetThreadName("AllocThr")); + int **p = (int**)a; + *p = new int; + return 0; +} + +void *ThreadedTestFree(void *a) { + EXPECT_EQ(true, TryToSetThreadName("FreeThr")); + int **p = (int**)a; + delete *p; + return 0; +} + +void *ThreadedTestUse(void *a) { + EXPECT_EQ(true, TryToSetThreadName("UseThr")); + int **p = (int**)a; + **p = 1; + return 0; +} + +void ThreadedTestSpawn() { + pthread_t t; + int *x; + PTHREAD_CREATE(&t, 0, ThreadedTestAlloc, &x); + PTHREAD_JOIN(t, 0); + PTHREAD_CREATE(&t, 0, ThreadedTestFree, &x); + PTHREAD_JOIN(t, 0); + PTHREAD_CREATE(&t, 0, ThreadedTestUse, &x); + PTHREAD_JOIN(t, 0); +} + +TEST(AddressSanitizer, ThreadedTest) { + EXPECT_DEATH(ThreadedTestSpawn(), + ASAN_PCRE_DOTALL + "Thread T.*created" + ".*Thread T.*created" + ".*Thread T.*created"); +} + +void *ThreadedTestFunc(void *unused) { + // Check if prctl(PR_SET_NAME) is supported. Return if not. + if (!TryToSetThreadName("TestFunc")) + return 0; + EXPECT_DEATH(ThreadedTestSpawn(), + ASAN_PCRE_DOTALL + "WRITE .*thread T. .UseThr." + ".*freed by thread T. .FreeThr. here:" + ".*previously allocated by thread T. .AllocThr. here:" + ".*Thread T. .UseThr. created by T.*TestFunc" + ".*Thread T. .FreeThr. created by T" + ".*Thread T. .AllocThr. created by T" + ""); + return 0; +} + +TEST(AddressSanitizer, ThreadNamesTest) { + // Run ThreadedTestFunc in a separate thread because it tries to set a + // thread name and we don't want to change the main thread's name. + pthread_t t; + PTHREAD_CREATE(&t, 0, ThreadedTestFunc, 0); + PTHREAD_JOIN(t, 0); +} + +#if ASAN_NEEDS_SEGV +TEST(AddressSanitizer, ShadowGapTest) { +#if SANITIZER_WORDSIZE == 32 + char *addr = (char*)0x22000000; +#else +# if defined(__powerpc64__) + char *addr = (char*)0x024000800000; +# else + char *addr = (char*)0x0000100000080000; +# endif +#endif + EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown"); +} +#endif // ASAN_NEEDS_SEGV + +extern "C" { +NOINLINE static void UseThenFreeThenUse() { + char *x = Ident((char*)malloc(8)); + *x = 1; + free_aaa(x); + *x = 2; +} +} + +TEST(AddressSanitizer, UseThenFreeThenUseTest) { + EXPECT_DEATH(UseThenFreeThenUse(), "freed by thread"); +} + +TEST(AddressSanitizer, StrDupTest) { + free(strdup(Ident("123"))); +} + +// Currently we create and poison redzone at right of global variables. +static char static110[110]; +const char ConstGlob[7] = {1, 2, 3, 4, 5, 6, 7}; +static const char StaticConstGlob[3] = {9, 8, 7}; + +TEST(AddressSanitizer, GlobalTest) { + static char func_static15[15]; + + static char fs1[10]; + static char fs2[10]; + static char fs3[10]; + + glob5[Ident(0)] = 0; + glob5[Ident(1)] = 0; + glob5[Ident(2)] = 0; + glob5[Ident(3)] = 0; + glob5[Ident(4)] = 0; + + EXPECT_DEATH(glob5[Ident(5)] = 0, + "0 bytes to the right of global variable.*glob5.* size 5"); + EXPECT_DEATH(glob5[Ident(5+6)] = 0, + "6 bytes to the right of global variable.*glob5.* size 5"); + Ident(static110); // avoid optimizations + static110[Ident(0)] = 0; + static110[Ident(109)] = 0; + EXPECT_DEATH(static110[Ident(110)] = 0, + "0 bytes to the right of global variable"); + EXPECT_DEATH(static110[Ident(110+7)] = 0, + "7 bytes to the right of global variable"); + + Ident(func_static15); // avoid optimizations + func_static15[Ident(0)] = 0; + EXPECT_DEATH(func_static15[Ident(15)] = 0, + "0 bytes to the right of global variable"); + EXPECT_DEATH(func_static15[Ident(15 + 9)] = 0, + "9 bytes to the right of global variable"); + + Ident(fs1); + Ident(fs2); + Ident(fs3); + + // We don't create left redzones, so this is not 100% guaranteed to fail. + // But most likely will. + EXPECT_DEATH(fs2[Ident(-1)] = 0, "is located.*of global variable"); + + EXPECT_DEATH(Ident(Ident(ConstGlob)[8]), + "is located 1 bytes to the right of .*ConstGlob"); + EXPECT_DEATH(Ident(Ident(StaticConstGlob)[5]), + "is located 2 bytes to the right of .*StaticConstGlob"); + + // call stuff from another file. + GlobalsTest(0); +} + +TEST(AddressSanitizer, GlobalStringConstTest) { + static const char *zoo = "FOOBAR123"; + const char *p = Ident(zoo); + EXPECT_DEATH(Ident(p[15]), "is ascii string 'FOOBAR123'"); +} + +TEST(AddressSanitizer, FileNameInGlobalReportTest) { + static char zoo[10]; + const char *p = Ident(zoo); + // The file name should be present in the report. + EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test."); +} + +int *ReturnsPointerToALocalObject() { + int a = 0; + return Ident(&a); +} + +#if ASAN_UAR == 1 +TEST(AddressSanitizer, LocalReferenceReturnTest) { + int *(*f)() = Ident(ReturnsPointerToALocalObject); + int *p = f(); + // Call 'f' a few more times, 'p' should still be poisoned. + for (int i = 0; i < 32; i++) + f(); + EXPECT_DEATH(*p = 1, "AddressSanitizer: stack-use-after-return"); + EXPECT_DEATH(*p = 1, "is located.*in frame .*ReturnsPointerToALocal"); +} +#endif + +template +NOINLINE static void FuncWithStack() { + char x[kSize]; + Ident(x)[0] = 0; + Ident(x)[kSize-1] = 0; +} + +static void LotsOfStackReuse() { + int LargeStack[10000]; + Ident(LargeStack)[0] = 0; + for (int i = 0; i < 10000; i++) { + FuncWithStack<128 * 1>(); + FuncWithStack<128 * 2>(); + FuncWithStack<128 * 4>(); + FuncWithStack<128 * 8>(); + FuncWithStack<128 * 16>(); + FuncWithStack<128 * 32>(); + FuncWithStack<128 * 64>(); + FuncWithStack<128 * 128>(); + FuncWithStack<128 * 256>(); + FuncWithStack<128 * 512>(); + Ident(LargeStack)[0] = 0; + } +} + +TEST(AddressSanitizer, StressStackReuseTest) { + LotsOfStackReuse(); +} + +TEST(AddressSanitizer, ThreadedStressStackReuseTest) { + const int kNumThreads = 20; + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0); + } + for (int i = 0; i < kNumThreads; i++) { + PTHREAD_JOIN(t[i], 0); + } +} + +static void *PthreadExit(void *a) { + pthread_exit(0); + return 0; +} + +TEST(AddressSanitizer, PthreadExitTest) { + pthread_t t; + for (int i = 0; i < 1000; i++) { + PTHREAD_CREATE(&t, 0, PthreadExit, 0); + PTHREAD_JOIN(t, 0); + } +} + +#ifdef __EXCEPTIONS +NOINLINE static void StackReuseAndException() { + int large_stack[1000]; + Ident(large_stack); + ASAN_THROW(1); +} + +// TODO(kcc): support exceptions with use-after-return. +TEST(AddressSanitizer, DISABLED_StressStackReuseAndExceptionsTest) { + for (int i = 0; i < 10000; i++) { + try { + StackReuseAndException(); + } catch(...) { + } + } +} +#endif + +TEST(AddressSanitizer, MlockTest) { + EXPECT_EQ(0, mlockall(MCL_CURRENT)); + EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); + EXPECT_EQ(0, munlockall()); + EXPECT_EQ(0, munlock((void*)0x987, 0x654)); +} + +struct LargeStruct { + int foo[100]; +}; + +// Test for bug http://llvm.org/bugs/show_bug.cgi?id=11763. +// Struct copy should not cause asan warning even if lhs == rhs. +TEST(AddressSanitizer, LargeStructCopyTest) { + LargeStruct a; + *Ident(&a) = *Ident(&a); +} + +ATTRIBUTE_NO_SANITIZE_ADDRESS +static void NoSanitizeAddress() { + char *foo = new char[10]; + Ident(foo)[10] = 0; + delete [] foo; +} + +TEST(AddressSanitizer, AttributeNoSanitizeAddressTest) { + Ident(NoSanitizeAddress)(); +} + +// It doesn't work on Android, as calls to new/delete go through malloc/free. +// Neither it does on OS X, see +// https://code.google.com/p/address-sanitizer/issues/detail?id=131. +#if !defined(ANDROID) && !defined(__ANDROID__) && !defined(__APPLE__) +static string MismatchStr(const string &str) { + return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str; +} + +TEST(AddressSanitizer, AllocDeallocMismatch) { + EXPECT_DEATH(free(Ident(new int)), + MismatchStr("operator new vs free")); + EXPECT_DEATH(free(Ident(new int[2])), + MismatchStr("operator new \\[\\] vs free")); + EXPECT_DEATH(delete (Ident(new int[2])), + MismatchStr("operator new \\[\\] vs operator delete")); + EXPECT_DEATH(delete (Ident((int*)malloc(2 * sizeof(int)))), + MismatchStr("malloc vs operator delete")); + EXPECT_DEATH(delete [] (Ident(new int)), + MismatchStr("operator new vs operator delete \\[\\]")); + EXPECT_DEATH(delete [] (Ident((int*)malloc(2 * sizeof(int)))), + MismatchStr("malloc vs operator delete \\[\\]")); +} +#endif + +// ------------------ demo tests; run each one-by-one ------------- +// e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests +TEST(AddressSanitizer, DISABLED_DemoThreadedTest) { + ThreadedTestSpawn(); +} + +void *SimpleBugOnSTack(void *x = 0) { + char a[20]; + Ident(a)[20] = 0; + return 0; +} + +TEST(AddressSanitizer, DISABLED_DemoStackTest) { + SimpleBugOnSTack(); +} + +TEST(AddressSanitizer, DISABLED_DemoThreadStackTest) { + pthread_t t; + PTHREAD_CREATE(&t, 0, SimpleBugOnSTack, 0); + PTHREAD_JOIN(t, 0); +} + +TEST(AddressSanitizer, DISABLED_DemoUAFLowIn) { + uaf_test(10, 0); +} +TEST(AddressSanitizer, DISABLED_DemoUAFLowLeft) { + uaf_test(10, -2); +} +TEST(AddressSanitizer, DISABLED_DemoUAFLowRight) { + uaf_test(10, 10); +} + +TEST(AddressSanitizer, DISABLED_DemoUAFHigh) { + uaf_test(kLargeMalloc, 0); +} + +TEST(AddressSanitizer, DISABLED_DemoOOM) { + size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000); + printf("%p\n", malloc(size)); +} + +TEST(AddressSanitizer, DISABLED_DemoDoubleFreeTest) { + DoubleFree(); +} + +TEST(AddressSanitizer, DISABLED_DemoNullDerefTest) { + int *a = 0; + Ident(a)[10] = 0; +} + +TEST(AddressSanitizer, DISABLED_DemoFunctionStaticTest) { + static char a[100]; + static char b[100]; + static char c[100]; + Ident(a); + Ident(b); + Ident(c); + Ident(a)[5] = 0; + Ident(b)[105] = 0; + Ident(a)[5] = 0; +} + +TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) { + const size_t kAllocSize = (1 << 28) - 1024; + size_t total_size = 0; + while (true) { + char *x = (char*)malloc(kAllocSize); + memset(x, 0, kAllocSize); + total_size += kAllocSize; + fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x); + } +} + +// http://code.google.com/p/address-sanitizer/issues/detail?id=66 +TEST(AddressSanitizer, BufferOverflowAfterManyFrees) { + for (int i = 0; i < 1000000; i++) { + delete [] (Ident(new char [8644])); + } + char *x = new char[8192]; + EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer: heap-buffer-overflow"); + delete [] Ident(x); +} + + +// Test that instrumentation of stack allocations takes into account +// AllocSize of a type, and not its StoreSize (16 vs 10 bytes for long double). +// See http://llvm.org/bugs/show_bug.cgi?id=12047 for more details. +TEST(AddressSanitizer, LongDoubleNegativeTest) { + long double a, b; + static long double c; + memcpy(Ident(&a), Ident(&b), sizeof(long double)); + memcpy(Ident(&c), Ident(&b), sizeof(long double)); +} + +TEST(AddressSanitizer, pthread_getschedparam) { + int policy; + struct sched_param param; + EXPECT_DEATH( + pthread_getschedparam(pthread_self(), &policy, Ident(¶m) + 2), + "AddressSanitizer: stack-buffer-.*flow"); + EXPECT_DEATH( + pthread_getschedparam(pthread_self(), Ident(&policy) - 1, ¶m), + "AddressSanitizer: stack-buffer-.*flow"); + int res = pthread_getschedparam(pthread_self(), &policy, ¶m); + ASSERT_EQ(0, res); +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_test.ignore b/projects/compiler-rt/lib/asan/tests/asan_test.ignore new file mode 100644 index 00000000..ea5c2609 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_test.ignore @@ -0,0 +1,3 @@ +# blacklisted functions for instrumented ASan unit test +fun:*IgnoreTest* +fun:*SomeOtherFunc* diff --git a/projects/compiler-rt/lib/asan/tests/asan_test_config.h b/projects/compiler-rt/lib/asan/tests/asan_test_config.h new file mode 100644 index 00000000..6eb33ce4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_test_config.h @@ -0,0 +1,56 @@ +//===-- asan_test_config.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#if !defined(INCLUDED_FROM_ASAN_TEST_UTILS_H) +# error "This file should be included into asan_test_utils.h only" +#endif + +#ifndef ASAN_TEST_CONFIG_H +#define ASAN_TEST_CONFIG_H + +#include +#include +#include + +#if ASAN_USE_DEJAGNU_GTEST +# include "dejagnu-gtest.h" +#else +# include "gtest/gtest.h" +#endif + +using std::string; +using std::vector; +using std::map; + +#ifndef ASAN_UAR +# error "please define ASAN_UAR" +#endif + +#ifndef ASAN_HAS_EXCEPTIONS +# error "please define ASAN_HAS_EXCEPTIONS" +#endif + +#ifndef ASAN_HAS_BLACKLIST +# error "please define ASAN_HAS_BLACKLIST" +#endif + +#ifndef ASAN_NEEDS_SEGV +# error "please define ASAN_NEEDS_SEGV" +#endif + +#ifndef ASAN_AVOID_EXPENSIVE_TESTS +# define ASAN_AVOID_EXPENSIVE_TESTS 0 +#endif + +#define ASAN_PCRE_DOTALL "" + +#endif // ASAN_TEST_CONFIG_H diff --git a/projects/compiler-rt/lib/asan/tests/asan_test_main.cc b/projects/compiler-rt/lib/asan/tests/asan_test_main.cc new file mode 100644 index 00000000..1746c5f4 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_test_main.cc @@ -0,0 +1,19 @@ +//===-- asan_test_main.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" + +int main(int argc, char **argv) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/projects/compiler-rt/lib/asan/tests/asan_test_utils.h b/projects/compiler-rt/lib/asan/tests/asan_test_utils.h new file mode 100644 index 00000000..b6bf6b82 --- /dev/null +++ b/projects/compiler-rt/lib/asan/tests/asan_test_utils.h @@ -0,0 +1,107 @@ +//===-- asan_test_utils.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// + +#ifndef ASAN_TEST_UTILS_H +#define ASAN_TEST_UTILS_H + +#if !defined(ASAN_EXTERNAL_TEST_CONFIG) +# define INCLUDED_FROM_ASAN_TEST_UTILS_H +# include "asan_test_config.h" +# undef INCLUDED_FROM_ASAN_TEST_UTILS_H +#endif + +#include "sanitizer_test_utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +# include +# include +# include +# include +#include +#endif + +#ifndef __APPLE__ +#include +#endif + +// Check that pthread_create/pthread_join return success. +#define PTHREAD_CREATE(a, b, c, d) ASSERT_EQ(0, pthread_create(a, b, c, d)) +#define PTHREAD_JOIN(a, b) ASSERT_EQ(0, pthread_join(a, b)) + +#if ASAN_HAS_EXCEPTIONS +# define ASAN_THROW(x) throw (x) +#else +# define ASAN_THROW(x) +#endif + +typedef uint8_t U1; +typedef uint16_t U2; +typedef uint32_t U4; +typedef uint64_t U8; + +static const int kPageSize = 4096; + +const size_t kLargeMalloc = 1 << 24; + +extern void free_aaa(void *p); +extern void *malloc_aaa(size_t size); + +template +NOINLINE void asan_write(T *a) { + *a = 0; +} + +string RightOOBErrorMessage(int oob_distance, bool is_write); +string RightOOBWriteMessage(int oob_distance); +string RightOOBReadMessage(int oob_distance); +string LeftOOBErrorMessage(int oob_distance, bool is_write); +string LeftOOBWriteMessage(int oob_distance); +string LeftOOBReadMessage(int oob_distance); +string LeftOOBAccessMessage(int oob_distance); +char* MallocAndMemsetString(size_t size, char ch); +char* MallocAndMemsetString(size_t size); + +extern char glob1[1]; +extern char glob2[2]; +extern char glob3[3]; +extern char glob4[4]; +extern char glob5[5]; +extern char glob6[6]; +extern char glob7[7]; +extern char glob8[8]; +extern char glob9[9]; +extern char glob10[10]; +extern char glob11[11]; +extern char glob12[12]; +extern char glob13[13]; +extern char glob14[14]; +extern char glob15[15]; +extern char glob16[16]; +extern char glob17[17]; +extern char glob1000[1000]; +extern char glob10000[10000]; +extern char glob100000[100000]; +extern int GlobalsTest(int x); + +#endif // ASAN_TEST_UTILS_H diff --git a/projects/compiler-rt/lib/ashldi3.c b/projects/compiler-rt/lib/ashldi3.c new file mode 100644 index 00000000..eb4698ac --- /dev/null +++ b/projects/compiler-rt/lib/ashldi3.c @@ -0,0 +1,43 @@ +/* ====-- ashldi3.c - Implement __ashldi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ashldi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a << b */ + +/* Precondition: 0 <= b < bits_in_dword */ + +ARM_EABI_FNALIAS(llsl, ashldi3) + +COMPILER_RT_ABI di_int +__ashldi3(di_int a, si_int b) +{ + const int bits_in_word = (int)(sizeof(si_int) * CHAR_BIT); + dwords input; + dwords result; + input.all = a; + if (b & bits_in_word) /* bits_in_word <= b < bits_in_dword */ + { + result.s.low = 0; + result.s.high = input.s.low << (b - bits_in_word); + } + else /* 0 <= b < bits_in_word */ + { + if (b == 0) + return a; + result.s.low = input.s.low << b; + result.s.high = (input.s.high << b) | (input.s.low >> (bits_in_word - b)); + } + return result.all; +} diff --git a/projects/compiler-rt/lib/ashlti3.c b/projects/compiler-rt/lib/ashlti3.c new file mode 100644 index 00000000..4bd8219b --- /dev/null +++ b/projects/compiler-rt/lib/ashlti3.c @@ -0,0 +1,45 @@ +/* ===-- ashlti3.c - Implement __ashlti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ashlti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: a << b */ + +/* Precondition: 0 <= b < bits_in_tword */ + +ti_int +__ashlti3(ti_int a, si_int b) +{ + const int bits_in_dword = (int)(sizeof(di_int) * CHAR_BIT); + twords input; + twords result; + input.all = a; + if (b & bits_in_dword) /* bits_in_dword <= b < bits_in_tword */ + { + result.s.low = 0; + result.s.high = input.s.low << (b - bits_in_dword); + } + else /* 0 <= b < bits_in_dword */ + { + if (b == 0) + return a; + result.s.low = input.s.low << b; + result.s.high = (input.s.high << b) | (input.s.low >> (bits_in_dword - b)); + } + return result.all; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/ashrdi3.c b/projects/compiler-rt/lib/ashrdi3.c new file mode 100644 index 00000000..14c878bb --- /dev/null +++ b/projects/compiler-rt/lib/ashrdi3.c @@ -0,0 +1,44 @@ +/*===-- ashrdi3.c - Implement __ashrdi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ashrdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: arithmetic a >> b */ + +/* Precondition: 0 <= b < bits_in_dword */ + +ARM_EABI_FNALIAS(lasr, ashrdi3) + +COMPILER_RT_ABI di_int +__ashrdi3(di_int a, si_int b) +{ + const int bits_in_word = (int)(sizeof(si_int) * CHAR_BIT); + dwords input; + dwords result; + input.all = a; + if (b & bits_in_word) /* bits_in_word <= b < bits_in_dword */ + { + /* result.s.high = input.s.high < 0 ? -1 : 0 */ + result.s.high = input.s.high >> (bits_in_word - 1); + result.s.low = input.s.high >> (b - bits_in_word); + } + else /* 0 <= b < bits_in_word */ + { + if (b == 0) + return a; + result.s.high = input.s.high >> b; + result.s.low = (input.s.high << (bits_in_word - b)) | (input.s.low >> b); + } + return result.all; +} diff --git a/projects/compiler-rt/lib/ashrti3.c b/projects/compiler-rt/lib/ashrti3.c new file mode 100644 index 00000000..ed43641c --- /dev/null +++ b/projects/compiler-rt/lib/ashrti3.c @@ -0,0 +1,46 @@ +/* ===-- ashrti3.c - Implement __ashrti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ashrti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: arithmetic a >> b */ + +/* Precondition: 0 <= b < bits_in_tword */ + +ti_int +__ashrti3(ti_int a, si_int b) +{ + const int bits_in_dword = (int)(sizeof(di_int) * CHAR_BIT); + twords input; + twords result; + input.all = a; + if (b & bits_in_dword) /* bits_in_dword <= b < bits_in_tword */ + { + /* result.s.high = input.s.high < 0 ? -1 : 0 */ + result.s.high = input.s.high >> (bits_in_dword - 1); + result.s.low = input.s.high >> (b - bits_in_dword); + } + else /* 0 <= b < bits_in_dword */ + { + if (b == 0) + return a; + result.s.high = input.s.high >> b; + result.s.low = (input.s.high << (bits_in_dword - b)) | (input.s.low >> b); + } + return result.all; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/assembly.h b/projects/compiler-rt/lib/assembly.h new file mode 100644 index 00000000..3d8e50dc --- /dev/null +++ b/projects/compiler-rt/lib/assembly.h @@ -0,0 +1,73 @@ +/* ===-- assembly.h - compiler-rt assembler support macros -----------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file defines macros for use in compiler-rt assembler source. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef COMPILERRT_ASSEMBLY_H +#define COMPILERRT_ASSEMBLY_H + +#if defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) +#define SEPARATOR @ +#else +#define SEPARATOR ; +#endif + +#if defined(__APPLE__) +#define HIDDEN_DIRECTIVE .private_extern +#define LOCAL_LABEL(name) L_##name +#define FILE_LEVEL_DIRECTIVE .subsections_via_symbols +#else +#define HIDDEN_DIRECTIVE .hidden +#define LOCAL_LABEL(name) .L_##name +#define FILE_LEVEL_DIRECTIVE +#endif + +#define GLUE2(a, b) a ## b +#define GLUE(a, b) GLUE2(a, b) +#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) + +#ifdef VISIBILITY_HIDDEN +#define DECLARE_SYMBOL_VISIBILITY(name) \ + HIDDEN_DIRECTIVE SYMBOL_NAME(name) SEPARATOR +#else +#define DECLARE_SYMBOL_VISIBILITY(name) +#endif + +#define DEFINE_COMPILERRT_FUNCTION(name) \ + FILE_LEVEL_DIRECTIVE SEPARATOR \ + .globl SYMBOL_NAME(name) SEPARATOR \ + DECLARE_SYMBOL_VISIBILITY(name) \ + SYMBOL_NAME(name): + +#define DEFINE_COMPILERRT_PRIVATE_FUNCTION(name) \ + .globl SYMBOL_NAME(name) SEPARATOR \ + HIDDEN_DIRECTIVE SYMBOL_NAME(name) SEPARATOR \ + SYMBOL_NAME(name): + +#define DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(name) \ + .globl name SEPARATOR \ + HIDDEN_DIRECTIVE name SEPARATOR \ + name: + +#define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \ + .globl SYMBOL_NAME(name) SEPARATOR \ + .set SYMBOL_NAME(name), SYMBOL_NAME(target) SEPARATOR + +#if defined (__ARM_EABI__) +# define DEFINE_AEABI_FUNCTION_ALIAS(aeabi_name, name) \ + DEFINE_COMPILERRT_FUNCTION_ALIAS(aeabi_name, name) +#else +# define DEFINE_AEABI_FUNCTION_ALIAS(aeabi_name, name) +#endif + +#endif /* COMPILERRT_ASSEMBLY_H */ diff --git a/projects/compiler-rt/lib/atomic.c b/projects/compiler-rt/lib/atomic.c new file mode 100644 index 00000000..02429a65 --- /dev/null +++ b/projects/compiler-rt/lib/atomic.c @@ -0,0 +1,337 @@ +/*===-- atomic.c - Implement support functions for atomic operations.------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------=== + * + * atomic.c defines a set of functions for performing atomic accesses on + * arbitrary-sized memory locations. This design uses locks that should + * be fast in the uncontended case, for two reasons: + * + * 1) This code must work with C programs that do not link to anything + * (including pthreads) and so it should not depend on any pthread + * functions. + * 2) Atomic operations, rather than explicit mutexes, are most commonly used + * on code where contended operations are rate. + * + * To avoid needing a per-object lock, this code allocates an array of + * locks and hashes the object pointers to find the one that it should use. + * For operations that must be atomic on two locations, the lower lock is + * always acquired first, to avoid deadlock. + * + *===----------------------------------------------------------------------=== + */ + +#include +#include + +// Clang objects if you redefine a builtin. This little hack allows us to +// define a function with the same name as an intrinsic. +#if __APPLE__ +// mach-o has extra leading underscore +#pragma redefine_extname __atomic_load_c ___atomic_load +#pragma redefine_extname __atomic_store_c ___atomic_store +#pragma redefine_extname __atomic_exchange_c ___atomic_exchange +#pragma redefine_extname __atomic_compare_exchange_c ___atomic_compare_exchange +#else +#pragma redefine_extname __atomic_load_c __atomic_load +#pragma redefine_extname __atomic_store_c __atomic_store +#pragma redefine_extname __atomic_exchange_c __atomic_exchange +#pragma redefine_extname __atomic_compare_exchange_c __atomic_compare_exchange +#endif + +/// Number of locks. This allocates one page on 32-bit platforms, two on +/// 64-bit. This can be specified externally if a different trade between +/// memory usage and contention probability is required for a given platform. +#ifndef SPINLOCK_COUNT +#define SPINLOCK_COUNT (1<<10) +#endif +static const long SPINLOCK_MASK = SPINLOCK_COUNT - 1; + +//////////////////////////////////////////////////////////////////////////////// +// Platform-specific lock implementation. Falls back to spinlocks if none is +// defined. Each platform should define the Lock type, and corresponding +// lock() and unlock() functions. +//////////////////////////////////////////////////////////////////////////////// +#ifdef __FreeBSD__ +#include +#include +#include +#include +typedef struct _usem Lock; +inline static void unlock(Lock *l) { + __c11_atomic_store((_Atomic(uint32_t)*)&l->_count, 1, __ATOMIC_RELEASE); + __c11_atomic_thread_fence(__ATOMIC_SEQ_CST); + if (l->_has_waiters) + _umtx_op(l, UMTX_OP_SEM_WAKE, 1, 0, 0); +} +inline static void lock(Lock *l) { + uint32_t old = 1; + while (!__c11_atomic_compare_exchange_weak((_Atomic(uint32_t)*)&l->_count, &old, + 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { + _umtx_op(l, UMTX_OP_SEM_WAIT, 0, 0, 0); + old = 1; + } +} +/// locks for atomic operations +static Lock locks[SPINLOCK_COUNT] = { [0 ... SPINLOCK_COUNT-1] = {0,1,0} }; + +#elif defined(__APPLE__) +#include +typedef OSSpinLock Lock; +inline static void unlock(Lock *l) { + OSSpinLockUnlock(l); +} +/// Locks a lock. In the current implementation, this is potentially +/// unbounded in the contended case. +inline static void lock(Lock *l) { + OSSpinLockLock(l); +} +static Lock locks[SPINLOCK_COUNT]; // initialized to OS_SPINLOCK_INIT which is 0 + +#else +typedef _Atomic(uintptr_t) Lock; +/// Unlock a lock. This is a release operation. +inline static void unlock(Lock *l) { + __c11_atomic_store(l, 0, __ATOMIC_RELEASE); +} +/// Locks a lock. In the current implementation, this is potentially +/// unbounded in the contended case. +inline static void lock(Lock *l) { + uintptr_t old = 0; + while (!__c11_atomic_compare_exchange_weak(l, &old, 1, __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED)) + old = 0; +} +/// locks for atomic operations +static Lock locks[SPINLOCK_COUNT]; +#endif + + +/// Returns a lock to use for a given pointer. +static inline Lock *lock_for_pointer(void *ptr) { + intptr_t hash = (intptr_t)ptr; + // Disregard the lowest 4 bits. We want all values that may be part of the + // same memory operation to hash to the same value and therefore use the same + // lock. + hash >>= 4; + // Use the next bits as the basis for the hash + intptr_t low = hash & SPINLOCK_MASK; + // Now use the high(er) set of bits to perturb the hash, so that we don't + // get collisions from atomic fields in a single object + hash >>= 16; + hash ^= low; + // Return a pointer to the word to use + return locks + (hash & SPINLOCK_MASK); +} + +/// Macros for determining whether a size is lock free. Clang can not yet +/// codegen __atomic_is_lock_free(16), so for now we assume 16-byte values are +/// not lock free. +#define IS_LOCK_FREE_1 __c11_atomic_is_lock_free(1) +#define IS_LOCK_FREE_2 __c11_atomic_is_lock_free(2) +#define IS_LOCK_FREE_4 __c11_atomic_is_lock_free(4) +#define IS_LOCK_FREE_8 __c11_atomic_is_lock_free(8) +#define IS_LOCK_FREE_16 0 + +/// Macro that calls the compiler-generated lock-free versions of functions +/// when they exist. +#define LOCK_FREE_CASES() \ + do {\ + switch (size) {\ + case 2:\ + if (IS_LOCK_FREE_2) {\ + LOCK_FREE_ACTION(uint16_t);\ + }\ + case 4:\ + if (IS_LOCK_FREE_4) {\ + LOCK_FREE_ACTION(uint32_t);\ + }\ + case 8:\ + if (IS_LOCK_FREE_8) {\ + LOCK_FREE_ACTION(uint64_t);\ + }\ + case 16:\ + if (IS_LOCK_FREE_16) {\ + /* FIXME: __uint128_t isn't available on 32 bit platforms. + LOCK_FREE_ACTION(__uint128_t);*/\ + }\ + }\ + } while (0) + + +/// An atomic load operation. This is atomic with respect to the source +/// pointer only. +void __atomic_load_c(int size, void *src, void *dest, int model) { +#define LOCK_FREE_ACTION(type) \ + *((type*)dest) = __c11_atomic_load((_Atomic(type)*)src, model);\ + return; + LOCK_FREE_CASES(); +#undef LOCK_FREE_ACTION + Lock *l = lock_for_pointer(src); + lock(l); + memcpy(dest, src, size); + unlock(l); +} + +/// An atomic store operation. This is atomic with respect to the destination +/// pointer only. +void __atomic_store_c(int size, void *dest, void *src, int model) { +#define LOCK_FREE_ACTION(type) \ + __c11_atomic_store((_Atomic(type)*)dest, *(type*)dest, model);\ + return; + LOCK_FREE_CASES(); +#undef LOCK_FREE_ACTION + Lock *l = lock_for_pointer(dest); + lock(l); + memcpy(dest, src, size); + unlock(l); +} + +/// Atomic compare and exchange operation. If the value at *ptr is identical +/// to the value at *expected, then this copies value at *desired to *ptr. If +/// they are not, then this stores the current value from *ptr in *expected. +/// +/// This function returns 1 if the exchange takes place or 0 if it fails. +int __atomic_compare_exchange_c(int size, void *ptr, void *expected, + void *desired, int success, int failure) { +#define LOCK_FREE_ACTION(type) \ + return __c11_atomic_compare_exchange_strong((_Atomic(type)*)ptr, (type*)expected,\ + *(type*)desired, success, failure) + LOCK_FREE_CASES(); +#undef LOCK_FREE_ACTION + Lock *l = lock_for_pointer(ptr); + lock(l); + if (memcmp(ptr, expected, size) == 0) { + memcpy(ptr, desired, size); + unlock(l); + return 1; + } + memcpy(expected, ptr, size); + unlock(l); + return 0; +} + +/// Performs an atomic exchange operation between two pointers. This is atomic +/// with respect to the target address. +void __atomic_exchange_c(int size, void *ptr, void *val, void *old, int model) { +#define LOCK_FREE_ACTION(type) \ + *(type*)old = __c11_atomic_exchange((_Atomic(type)*)ptr, *(type*)val,\ + model);\ + return; + LOCK_FREE_CASES(); +#undef LOCK_FREE_ACTION + Lock *l = lock_for_pointer(ptr); + lock(l); + memcpy(old, ptr, size); + memcpy(ptr, val, size); + unlock(l); +} + +//////////////////////////////////////////////////////////////////////////////// +// Where the size is known at compile time, the compiler may emit calls to +// specialised versions of the above functions. +//////////////////////////////////////////////////////////////////////////////// +#define OPTIMISED_CASES\ + OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t)\ + OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t)\ + OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t)\ + OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t)\ + /* FIXME: __uint128_t isn't available on 32 bit platforms. + OPTIMISED_CASE(16, IS_LOCK_FREE_16, __uint128_t)*/\ + +#define OPTIMISED_CASE(n, lockfree, type)\ +type __atomic_load_##n(type *src, int model) {\ + if (lockfree)\ + return __c11_atomic_load((_Atomic(type)*)src, model);\ + Lock *l = lock_for_pointer(src);\ + lock(l);\ + type val = *src;\ + unlock(l);\ + return val;\ +} +OPTIMISED_CASES +#undef OPTIMISED_CASE + +#define OPTIMISED_CASE(n, lockfree, type)\ +void __atomic_store_##n(type *dest, type val, int model) {\ + if (lockfree) {\ + __c11_atomic_store((_Atomic(type)*)dest, val, model);\ + return;\ + }\ + Lock *l = lock_for_pointer(dest);\ + lock(l);\ + *dest = val;\ + unlock(l);\ + return;\ +} +OPTIMISED_CASES +#undef OPTIMISED_CASE + +#define OPTIMISED_CASE(n, lockfree, type)\ +type __atomic_exchange_##n(type *dest, type val, int model) {\ + if (lockfree)\ + return __c11_atomic_exchange((_Atomic(type)*)dest, val, model);\ + Lock *l = lock_for_pointer(dest);\ + lock(l);\ + type tmp = *dest;\ + *dest = val;\ + unlock(l);\ + return tmp;\ +} +OPTIMISED_CASES +#undef OPTIMISED_CASE + +#define OPTIMISED_CASE(n, lockfree, type)\ +int __atomic_compare_exchange_##n(type *ptr, type *expected, type desired,\ + int success, int failure) {\ + if (lockfree)\ + return __c11_atomic_compare_exchange_strong((_Atomic(type)*)ptr, expected, desired,\ + success, failure);\ + Lock *l = lock_for_pointer(ptr);\ + lock(l);\ + if (*ptr == *expected) {\ + *ptr = desired;\ + unlock(l);\ + return 1;\ + }\ + *expected = *ptr;\ + unlock(l);\ + return 0;\ +} +OPTIMISED_CASES +#undef OPTIMISED_CASE + +//////////////////////////////////////////////////////////////////////////////// +// Atomic read-modify-write operations for integers of various sizes. +//////////////////////////////////////////////////////////////////////////////// +#define ATOMIC_RMW(n, lockfree, type, opname, op) \ +type __atomic_fetch_##opname##_##n(type *ptr, type val, int model) {\ + if (lockfree) \ + return __c11_atomic_fetch_##opname((_Atomic(type)*)ptr, val, model);\ + Lock *l = lock_for_pointer(ptr);\ + lock(l);\ + type tmp = *ptr;\ + *ptr = tmp op val;\ + unlock(l);\ + return tmp;\ +} + +#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, add, +) +OPTIMISED_CASES +#undef OPTIMISED_CASE +#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, sub, -) +OPTIMISED_CASES +#undef OPTIMISED_CASE +#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, and, &) +OPTIMISED_CASES +#undef OPTIMISED_CASE +#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, or, |) +OPTIMISED_CASES +#undef OPTIMISED_CASE +#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, xor, ^) +OPTIMISED_CASES +#undef OPTIMISED_CASE diff --git a/projects/compiler-rt/lib/clear_cache.c b/projects/compiler-rt/lib/clear_cache.c new file mode 100644 index 00000000..b934fd4b --- /dev/null +++ b/projects/compiler-rt/lib/clear_cache.c @@ -0,0 +1,40 @@ +/* ===-- clear_cache.c - Implement __clear_cache ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __APPLE__ + #include +#endif + +/* + * The compiler generates calls to __clear_cache() when creating + * trampoline functions on the stack for use with nested functions. + * It is expected to invalidate the instruction cache for the + * specified range. + */ + +void __clear_cache(void* start, void* end) +{ +#if __i386__ || __x86_64__ +/* + * Intel processors have a unified instruction and data cache + * so there is nothing to do + */ +#else + #if __APPLE__ + /* On Darwin, sys_icache_invalidate() provides this functionality */ + sys_icache_invalidate(start, end-start); + #else + compilerrt_abort(); + #endif +#endif +} + diff --git a/projects/compiler-rt/lib/clzdi2.c b/projects/compiler-rt/lib/clzdi2.c new file mode 100644 index 00000000..b9e64da4 --- /dev/null +++ b/projects/compiler-rt/lib/clzdi2.c @@ -0,0 +1,29 @@ +/* ===-- clzdi2.c - Implement __clzdi2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __clzdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: the number of leading 0-bits */ + +/* Precondition: a != 0 */ + +COMPILER_RT_ABI si_int +__clzdi2(di_int a) +{ + dwords x; + x.all = a; + const si_int f = -(x.s.high == 0); + return __builtin_clz((x.s.high & ~f) | (x.s.low & f)) + + (f & ((si_int)(sizeof(si_int) * CHAR_BIT))); +} diff --git a/projects/compiler-rt/lib/clzsi2.c b/projects/compiler-rt/lib/clzsi2.c new file mode 100644 index 00000000..25b8ed2c --- /dev/null +++ b/projects/compiler-rt/lib/clzsi2.c @@ -0,0 +1,53 @@ +/* ===-- clzsi2.c - Implement __clzsi2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __clzsi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: the number of leading 0-bits */ + +/* Precondition: a != 0 */ + +COMPILER_RT_ABI si_int +__clzsi2(si_int a) +{ + su_int x = (su_int)a; + si_int t = ((x & 0xFFFF0000) == 0) << 4; /* if (x is small) t = 16 else 0 */ + x >>= 16 - t; /* x = [0 - 0xFFFF] */ + su_int r = t; /* r = [0, 16] */ + /* return r + clz(x) */ + t = ((x & 0xFF00) == 0) << 3; + x >>= 8 - t; /* x = [0 - 0xFF] */ + r += t; /* r = [0, 8, 16, 24] */ + /* return r + clz(x) */ + t = ((x & 0xF0) == 0) << 2; + x >>= 4 - t; /* x = [0 - 0xF] */ + r += t; /* r = [0, 4, 8, 12, 16, 20, 24, 28] */ + /* return r + clz(x) */ + t = ((x & 0xC) == 0) << 1; + x >>= 2 - t; /* x = [0 - 3] */ + r += t; /* r = [0 - 30] and is even */ + /* return r + clz(x) */ +/* switch (x) + * { + * case 0: + * return r + 2; + * case 1: + * return r + 1; + * case 2: + * case 3: + * return r; + * } + */ + return r + ((2 - x) & -((x & 2) == 0)); +} diff --git a/projects/compiler-rt/lib/clzti2.c b/projects/compiler-rt/lib/clzti2.c new file mode 100644 index 00000000..355c20e4 --- /dev/null +++ b/projects/compiler-rt/lib/clzti2.c @@ -0,0 +1,33 @@ +/* ===-- clzti2.c - Implement __clzti2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __clzti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: the number of leading 0-bits */ + +/* Precondition: a != 0 */ + +si_int +__clzti2(ti_int a) +{ + twords x; + x.all = a; + const di_int f = -(x.s.high == 0); + return __builtin_clzll((x.s.high & ~f) | (x.s.low & f)) + + ((si_int)f & ((si_int)(sizeof(di_int) * CHAR_BIT))); +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/cmpdi2.c b/projects/compiler-rt/lib/cmpdi2.c new file mode 100644 index 00000000..52634d9c --- /dev/null +++ b/projects/compiler-rt/lib/cmpdi2.c @@ -0,0 +1,51 @@ +/* ===-- cmpdi2.c - Implement __cmpdi2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __cmpdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: if (a < b) returns 0 +* if (a == b) returns 1 +* if (a > b) returns 2 +*/ + +COMPILER_RT_ABI si_int +__cmpdi2(di_int a, di_int b) +{ + dwords x; + x.all = a; + dwords y; + y.all = b; + if (x.s.high < y.s.high) + return 0; + if (x.s.high > y.s.high) + return 2; + if (x.s.low < y.s.low) + return 0; + if (x.s.low > y.s.low) + return 2; + return 1; +} + +#ifdef __ARM_EABI__ +/* Returns: if (a < b) returns -1 +* if (a == b) returns 0 +* if (a > b) returns 1 +*/ +COMPILER_RT_ABI si_int +__aeabi_lcmp(di_int a, di_int b) +{ + return __cmpdi2(a, b) - 1; +} +#endif + diff --git a/projects/compiler-rt/lib/cmpti2.c b/projects/compiler-rt/lib/cmpti2.c new file mode 100644 index 00000000..d0aec452 --- /dev/null +++ b/projects/compiler-rt/lib/cmpti2.c @@ -0,0 +1,42 @@ +/* ===-- cmpti2.c - Implement __cmpti2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __cmpti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: if (a < b) returns 0 + * if (a == b) returns 1 + * if (a > b) returns 2 + */ + +si_int +__cmpti2(ti_int a, ti_int b) +{ + twords x; + x.all = a; + twords y; + y.all = b; + if (x.s.high < y.s.high) + return 0; + if (x.s.high > y.s.high) + return 2; + if (x.s.low < y.s.low) + return 0; + if (x.s.low > y.s.low) + return 2; + return 1; +} + +#endif diff --git a/projects/compiler-rt/lib/comparedf2.c b/projects/compiler-rt/lib/comparedf2.c new file mode 100644 index 00000000..de67784d --- /dev/null +++ b/projects/compiler-rt/lib/comparedf2.c @@ -0,0 +1,134 @@ +//===-- lib/comparedf2.c - Double-precision comparisons -----------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// // This file implements the following soft-float comparison routines: +// +// __eqdf2 __gedf2 __unorddf2 +// __ledf2 __gtdf2 +// __ltdf2 +// __nedf2 +// +// The semantics of the routines grouped in each column are identical, so there +// is a single implementation for each, and wrappers to provide the other names. +// +// The main routines behave as follows: +// +// __ledf2(a,b) returns -1 if a < b +// 0 if a == b +// 1 if a > b +// 1 if either a or b is NaN +// +// __gedf2(a,b) returns -1 if a < b +// 0 if a == b +// 1 if a > b +// -1 if either a or b is NaN +// +// __unorddf2(a,b) returns 0 if both a and b are numbers +// 1 if either a or b is NaN +// +// Note that __ledf2( ) and __gedf2( ) are identical except in their handling of +// NaN values. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +enum LE_RESULT { + LE_LESS = -1, + LE_EQUAL = 0, + LE_GREATER = 1, + LE_UNORDERED = 1 +}; + +enum LE_RESULT __ledf2(fp_t a, fp_t b) { + + const srep_t aInt = toRep(a); + const srep_t bInt = toRep(b); + const rep_t aAbs = aInt & absMask; + const rep_t bAbs = bInt & absMask; + + // If either a or b is NaN, they are unordered. + if (aAbs > infRep || bAbs > infRep) return LE_UNORDERED; + + // If a and b are both zeros, they are equal. + if ((aAbs | bAbs) == 0) return LE_EQUAL; + + // If at least one of a and b is positive, we get the same result comparing + // a and b as signed integers as we would with a floating-point compare. + if ((aInt & bInt) >= 0) { + if (aInt < bInt) return LE_LESS; + else if (aInt == bInt) return LE_EQUAL; + else return LE_GREATER; + } + + // Otherwise, both are negative, so we need to flip the sense of the + // comparison to get the correct result. (This assumes a twos- or ones- + // complement integer representation; if integers are represented in a + // sign-magnitude representation, then this flip is incorrect). + else { + if (aInt > bInt) return LE_LESS; + else if (aInt == bInt) return LE_EQUAL; + else return LE_GREATER; + } +} + +enum GE_RESULT { + GE_LESS = -1, + GE_EQUAL = 0, + GE_GREATER = 1, + GE_UNORDERED = -1 // Note: different from LE_UNORDERED +}; + +enum GE_RESULT __gedf2(fp_t a, fp_t b) { + + const srep_t aInt = toRep(a); + const srep_t bInt = toRep(b); + const rep_t aAbs = aInt & absMask; + const rep_t bAbs = bInt & absMask; + + if (aAbs > infRep || bAbs > infRep) return GE_UNORDERED; + if ((aAbs | bAbs) == 0) return GE_EQUAL; + if ((aInt & bInt) >= 0) { + if (aInt < bInt) return GE_LESS; + else if (aInt == bInt) return GE_EQUAL; + else return GE_GREATER; + } else { + if (aInt > bInt) return GE_LESS; + else if (aInt == bInt) return GE_EQUAL; + else return GE_GREATER; + } +} + +ARM_EABI_FNALIAS(dcmpun, unorddf2) + +int __unorddf2(fp_t a, fp_t b) { + const rep_t aAbs = toRep(a) & absMask; + const rep_t bAbs = toRep(b) & absMask; + return aAbs > infRep || bAbs > infRep; +} + +// The following are alternative names for the preceeding routines. + +enum LE_RESULT __eqdf2(fp_t a, fp_t b) { + return __ledf2(a, b); +} + +enum LE_RESULT __ltdf2(fp_t a, fp_t b) { + return __ledf2(a, b); +} + +enum LE_RESULT __nedf2(fp_t a, fp_t b) { + return __ledf2(a, b); +} + +enum GE_RESULT __gtdf2(fp_t a, fp_t b) { + return __gedf2(a, b); +} + diff --git a/projects/compiler-rt/lib/comparesf2.c b/projects/compiler-rt/lib/comparesf2.c new file mode 100644 index 00000000..c1c3a479 --- /dev/null +++ b/projects/compiler-rt/lib/comparesf2.c @@ -0,0 +1,133 @@ +//===-- lib/comparesf2.c - Single-precision comparisons -----------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the following soft-fp_t comparison routines: +// +// __eqsf2 __gesf2 __unordsf2 +// __lesf2 __gtsf2 +// __ltsf2 +// __nesf2 +// +// The semantics of the routines grouped in each column are identical, so there +// is a single implementation for each, and wrappers to provide the other names. +// +// The main routines behave as follows: +// +// __lesf2(a,b) returns -1 if a < b +// 0 if a == b +// 1 if a > b +// 1 if either a or b is NaN +// +// __gesf2(a,b) returns -1 if a < b +// 0 if a == b +// 1 if a > b +// -1 if either a or b is NaN +// +// __unordsf2(a,b) returns 0 if both a and b are numbers +// 1 if either a or b is NaN +// +// Note that __lesf2( ) and __gesf2( ) are identical except in their handling of +// NaN values. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +enum LE_RESULT { + LE_LESS = -1, + LE_EQUAL = 0, + LE_GREATER = 1, + LE_UNORDERED = 1 +}; + +enum LE_RESULT __lesf2(fp_t a, fp_t b) { + + const srep_t aInt = toRep(a); + const srep_t bInt = toRep(b); + const rep_t aAbs = aInt & absMask; + const rep_t bAbs = bInt & absMask; + + // If either a or b is NaN, they are unordered. + if (aAbs > infRep || bAbs > infRep) return LE_UNORDERED; + + // If a and b are both zeros, they are equal. + if ((aAbs | bAbs) == 0) return LE_EQUAL; + + // If at least one of a and b is positive, we get the same result comparing + // a and b as signed integers as we would with a fp_ting-point compare. + if ((aInt & bInt) >= 0) { + if (aInt < bInt) return LE_LESS; + else if (aInt == bInt) return LE_EQUAL; + else return LE_GREATER; + } + + // Otherwise, both are negative, so we need to flip the sense of the + // comparison to get the correct result. (This assumes a twos- or ones- + // complement integer representation; if integers are represented in a + // sign-magnitude representation, then this flip is incorrect). + else { + if (aInt > bInt) return LE_LESS; + else if (aInt == bInt) return LE_EQUAL; + else return LE_GREATER; + } +} + +enum GE_RESULT { + GE_LESS = -1, + GE_EQUAL = 0, + GE_GREATER = 1, + GE_UNORDERED = -1 // Note: different from LE_UNORDERED +}; + +enum GE_RESULT __gesf2(fp_t a, fp_t b) { + + const srep_t aInt = toRep(a); + const srep_t bInt = toRep(b); + const rep_t aAbs = aInt & absMask; + const rep_t bAbs = bInt & absMask; + + if (aAbs > infRep || bAbs > infRep) return GE_UNORDERED; + if ((aAbs | bAbs) == 0) return GE_EQUAL; + if ((aInt & bInt) >= 0) { + if (aInt < bInt) return GE_LESS; + else if (aInt == bInt) return GE_EQUAL; + else return GE_GREATER; + } else { + if (aInt > bInt) return GE_LESS; + else if (aInt == bInt) return GE_EQUAL; + else return GE_GREATER; + } +} + +ARM_EABI_FNALIAS(fcmpun, unordsf2) + +int __unordsf2(fp_t a, fp_t b) { + const rep_t aAbs = toRep(a) & absMask; + const rep_t bAbs = toRep(b) & absMask; + return aAbs > infRep || bAbs > infRep; +} + +// The following are alternative names for the preceeding routines. + +enum LE_RESULT __eqsf2(fp_t a, fp_t b) { + return __lesf2(a, b); +} + +enum LE_RESULT __ltsf2(fp_t a, fp_t b) { + return __lesf2(a, b); +} + +enum LE_RESULT __nesf2(fp_t a, fp_t b) { + return __lesf2(a, b); +} + +enum GE_RESULT __gtsf2(fp_t a, fp_t b) { + return __gesf2(a, b); +} diff --git a/projects/compiler-rt/lib/ctzdi2.c b/projects/compiler-rt/lib/ctzdi2.c new file mode 100644 index 00000000..db3c6fdc --- /dev/null +++ b/projects/compiler-rt/lib/ctzdi2.c @@ -0,0 +1,29 @@ +/* ===-- ctzdi2.c - Implement __ctzdi2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ctzdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: the number of trailing 0-bits */ + +/* Precondition: a != 0 */ + +COMPILER_RT_ABI si_int +__ctzdi2(di_int a) +{ + dwords x; + x.all = a; + const si_int f = -(x.s.low == 0); + return __builtin_ctz((x.s.high & f) | (x.s.low & ~f)) + + (f & ((si_int)(sizeof(si_int) * CHAR_BIT))); +} diff --git a/projects/compiler-rt/lib/ctzsi2.c b/projects/compiler-rt/lib/ctzsi2.c new file mode 100644 index 00000000..c69486ea --- /dev/null +++ b/projects/compiler-rt/lib/ctzsi2.c @@ -0,0 +1,57 @@ +/* ===-- ctzsi2.c - Implement __ctzsi2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ctzsi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: the number of trailing 0-bits */ + +/* Precondition: a != 0 */ + +COMPILER_RT_ABI si_int +__ctzsi2(si_int a) +{ + su_int x = (su_int)a; + si_int t = ((x & 0x0000FFFF) == 0) << 4; /* if (x has no small bits) t = 16 else 0 */ + x >>= t; /* x = [0 - 0xFFFF] + higher garbage bits */ + su_int r = t; /* r = [0, 16] */ + /* return r + ctz(x) */ + t = ((x & 0x00FF) == 0) << 3; + x >>= t; /* x = [0 - 0xFF] + higher garbage bits */ + r += t; /* r = [0, 8, 16, 24] */ + /* return r + ctz(x) */ + t = ((x & 0x0F) == 0) << 2; + x >>= t; /* x = [0 - 0xF] + higher garbage bits */ + r += t; /* r = [0, 4, 8, 12, 16, 20, 24, 28] */ + /* return r + ctz(x) */ + t = ((x & 0x3) == 0) << 1; + x >>= t; + x &= 3; /* x = [0 - 3] */ + r += t; /* r = [0 - 30] and is even */ + /* return r + ctz(x) */ + +/* The branch-less return statement below is equivalent + * to the following switch statement: + * switch (x) + * { + * case 0: + * return r + 2; + * case 2: + * return r + 1; + * case 1: + * case 3: + * return r; + * } + */ + return r + ((2 - (x >> 1)) & -((x & 1) == 0)); +} diff --git a/projects/compiler-rt/lib/ctzti2.c b/projects/compiler-rt/lib/ctzti2.c new file mode 100644 index 00000000..66dc01b7 --- /dev/null +++ b/projects/compiler-rt/lib/ctzti2.c @@ -0,0 +1,33 @@ +/* ===-- ctzti2.c - Implement __ctzti2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ctzti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: the number of trailing 0-bits */ + +/* Precondition: a != 0 */ + +si_int +__ctzti2(ti_int a) +{ + twords x; + x.all = a; + const di_int f = -(x.s.low == 0); + return __builtin_ctzll((x.s.high & f) | (x.s.low & ~f)) + + ((si_int)f & ((si_int)(sizeof(di_int) * CHAR_BIT))); +} + +#endif diff --git a/projects/compiler-rt/lib/dfsan/CMakeLists.txt b/projects/compiler-rt/lib/dfsan/CMakeLists.txt new file mode 100644 index 00000000..b952799f --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/CMakeLists.txt @@ -0,0 +1,44 @@ +include_directories(..) + +# Runtime library sources and build flags. +set(DFSAN_RTL_SOURCES + dfsan.cc + dfsan_custom.cc + dfsan_interceptors.cc) +set(DFSAN_RTL_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + # Prevent clang from generating libc calls. + -ffreestanding) + +# Static runtime library. +set(DFSAN_RUNTIME_LIBRARIES) +set(arch "x86_64") +if(CAN_TARGET_${arch}) + add_compiler_rt_static_runtime(clang_rt.dfsan-${arch} ${arch} + SOURCES ${DFSAN_RTL_SOURCES} + $ + $ + $ + CFLAGS ${DFSAN_RTL_CFLAGS} -fPIE) + add_compiler_rt_static_runtime(clang_rt.dfsan-libc-${arch} ${arch} + SOURCES ${DFSAN_RTL_SOURCES} + $ + CFLAGS ${DFSAN_RTL_CFLAGS} -fPIC -DDFSAN_NOLIBC) + add_sanitizer_rt_symbols(clang_rt.dfsan-${arch} dfsan.syms.extra) + list(APPEND DFSAN_RUNTIME_LIBRARIES clang_rt.dfsan-${arch} + clang_rt.dfsan-${arch}-symbols) +endif() + +add_custom_target(dfsan_abilist ALL + SOURCES ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt) +add_custom_command(OUTPUT ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt + VERBATIM + COMMAND + cat ${CMAKE_CURRENT_SOURCE_DIR}/done_abilist.txt + ${CMAKE_CURRENT_SOURCE_DIR}/libc_ubuntu1204_abilist.txt + > ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt + DEPENDS done_abilist.txt libc_ubuntu1204_abilist.txt) +install(FILES ${CLANG_RESOURCE_DIR}/dfsan_abilist.txt + DESTINATION ${LIBCLANG_INSTALL_PATH}) + +add_subdirectory(lit_tests) diff --git a/projects/compiler-rt/lib/dfsan/Makefile.mk b/projects/compiler-rt/lib/dfsan/Makefile.mk new file mode 100644 index 00000000..4aeaac42 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/Makefile.mk @@ -0,0 +1,23 @@ +#===- lib/dfsan/Makefile.mk --------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := dfsan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the dfsan functions. +DfsanFunctions := $(Sources:%.cc=%) diff --git a/projects/compiler-rt/lib/dfsan/dfsan.cc b/projects/compiler-rt/lib/dfsan/dfsan.cc new file mode 100644 index 00000000..72d05c68 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/dfsan.cc @@ -0,0 +1,265 @@ +//===-- dfsan.cc ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// DataFlowSanitizer runtime. This file defines the public interface to +// DataFlowSanitizer as well as the definition of certain runtime functions +// called automatically by the compiler (specifically the instrumentation pass +// in llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp). +// +// The public interface is defined in include/sanitizer/dfsan_interface.h whose +// functions are prefixed dfsan_ while the compiler interface functions are +// prefixed __dfsan_. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" + +#include "dfsan/dfsan.h" + +using namespace __dfsan; + +typedef atomic_uint16_t atomic_dfsan_label; +static const dfsan_label kInitializingLabel = -1; + +static const uptr kNumLabels = 1 << (sizeof(dfsan_label) * 8); + +static atomic_dfsan_label __dfsan_last_label; +static dfsan_label_info __dfsan_label_info[kNumLabels]; + +Flags __dfsan::flags_data; + +SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_retval_tls; +SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64]; + +// On Linux/x86_64, memory is laid out as follows: +// +// +--------------------+ 0x800000000000 (top of memory) +// | application memory | +// +--------------------+ 0x700000008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x200200000000 (kUnusedAddr) +// | union table | +// +--------------------+ 0x200000000000 (kUnionTableAddr) +// | shadow memory | +// +--------------------+ 0x000000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x000000000000 +// +// To derive a shadow memory address from an application memory address, +// bits 44-46 are cleared to bring the address into the range +// [0x000000008000,0x100000000000). Then the address is shifted left by 1 to +// account for the double byte representation of shadow labels and move the +// address into the shadow memory range. See the function shadow_for below. + +typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels]; + +static const uptr kShadowAddr = 0x10000; +static const uptr kUnionTableAddr = 0x200000000000; +static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); +static const uptr kAppAddr = 0x700000008000; + +static atomic_dfsan_label *union_table(dfsan_label l1, dfsan_label l2) { + return &(*(dfsan_union_table_t *) kUnionTableAddr)[l1][l2]; +} + +// Resolves the union of two unequal labels. Nonequality is a precondition for +// this function (the instrumentation pass inlines the equality test). +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +dfsan_label __dfsan_union(dfsan_label l1, dfsan_label l2) { + DCHECK_NE(l1, l2); + + if (l1 == 0) + return l2; + if (l2 == 0) + return l1; + + if (l1 > l2) + Swap(l1, l2); + + atomic_dfsan_label *table_ent = union_table(l1, l2); + // We need to deal with the case where two threads concurrently request + // a union of the same pair of labels. If the table entry is uninitialized, + // (i.e. 0) use a compare-exchange to set the entry to kInitializingLabel + // (i.e. -1) to mark that we are initializing it. + dfsan_label label = 0; + if (atomic_compare_exchange_strong(table_ent, &label, kInitializingLabel, + memory_order_acquire)) { + // Check whether l2 subsumes l1. We don't need to check whether l1 + // subsumes l2 because we are guaranteed here that l1 < l2, and (at least + // in the cases we are interested in) a label may only subsume labels + // created earlier (i.e. with a lower numerical value). + if (__dfsan_label_info[l2].l1 == l1 || + __dfsan_label_info[l2].l2 == l1) { + label = l2; + } else { + label = + atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; + CHECK_NE(label, kInitializingLabel); + __dfsan_label_info[label].l1 = l1; + __dfsan_label_info[label].l2 = l2; + } + atomic_store(table_ent, label, memory_order_release); + } else if (label == kInitializingLabel) { + // Another thread is initializing the entry. Wait until it is finished. + do { + internal_sched_yield(); + label = atomic_load(table_ent, memory_order_acquire); + } while (label == kInitializingLabel); + } + return label; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +dfsan_label __dfsan_union_load(const dfsan_label *ls, uptr n) { + dfsan_label label = ls[0]; + for (uptr i = 1; i != n; ++i) { + dfsan_label next_label = ls[i]; + if (label != next_label) + label = __dfsan_union(label, next_label); + } + return label; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __dfsan_unimplemented(char *fname) { + if (flags().warn_unimplemented) + Report("WARNING: DataFlowSanitizer: call to uninstrumented function %s\n", + fname); +} + +// Use '-mllvm -dfsan-debug-nonzero-labels' and break on this function +// to try to figure out where labels are being introduced in a nominally +// label-free program. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_nonzero_label() { + if (flags().warn_nonzero_labels) + Report("WARNING: DataFlowSanitizer: saw nonzero label\n"); +} + +// Like __dfsan_union, but for use from the client or custom functions. Hence +// the equality comparison is done here before calling __dfsan_union. +SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +dfsan_union(dfsan_label l1, dfsan_label l2) { + if (l1 == l2) + return l1; + return __dfsan_union(l1, l2); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +dfsan_label dfsan_create_label(const char *desc, void *userdata) { + dfsan_label label = + atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; + CHECK_NE(label, kInitializingLabel); + __dfsan_label_info[label].l1 = __dfsan_label_info[label].l2 = 0; + __dfsan_label_info[label].desc = desc; + __dfsan_label_info[label].userdata = userdata; + return label; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __dfsan_set_label(dfsan_label label, void *addr, uptr size) { + for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) + *labelp = label; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void dfsan_set_label(dfsan_label label, void *addr, uptr size) { + __dfsan_set_label(label, addr, size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void dfsan_add_label(dfsan_label label, void *addr, uptr size) { + for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) + if (*labelp != label) + *labelp = __dfsan_union(*labelp, label); +} + +// Unlike the other dfsan interface functions the behavior of this function +// depends on the label of one of its arguments. Hence it is implemented as a +// custom function. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +__dfsw_dfsan_get_label(long data, dfsan_label data_label, + dfsan_label *ret_label) { + *ret_label = 0; + return data_label; +} + +SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +dfsan_read_label(const void *addr, uptr size) { + if (size == 0) + return 0; + return __dfsan_union_load(shadow_for(addr), size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label) { + return &__dfsan_label_info[label]; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int +dfsan_has_label(dfsan_label label, dfsan_label elem) { + if (label == elem) + return true; + const dfsan_label_info *info = dfsan_get_label_info(label); + if (info->l1 != 0) { + return dfsan_has_label(info->l1, elem) || dfsan_has_label(info->l2, elem); + } else { + return false; + } +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label +dfsan_has_label_with_desc(dfsan_label label, const char *desc) { + const dfsan_label_info *info = dfsan_get_label_info(label); + if (info->l1 != 0) { + return dfsan_has_label_with_desc(info->l1, desc) || + dfsan_has_label_with_desc(info->l2, desc); + } else { + return internal_strcmp(desc, info->desc) == 0; + } +} + +static void InitializeFlags(Flags &f, const char *env) { + f.warn_unimplemented = true; + f.warn_nonzero_labels = false; + + ParseFlag(env, &f.warn_unimplemented, "warn_unimplemented"); + ParseFlag(env, &f.warn_nonzero_labels, "warn_nonzero_labels"); +} + +#ifdef DFSAN_NOLIBC +extern "C" void dfsan_init() { +#else +static void dfsan_init(int argc, char **argv, char **envp) { +#endif + MmapFixedNoReserve(kShadowAddr, kUnusedAddr - kShadowAddr); + + // Protect the region of memory we don't use, to preserve the one-to-one + // mapping from application to shadow memory. But if ASLR is disabled, Linux + // will load our executable in the middle of our unused region. This mostly + // works so long as the program doesn't use too much memory. We support this + // case by disabling memory protection when ASLR is disabled. + uptr init_addr = (uptr)&dfsan_init; + if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr)) + Mprotect(kUnusedAddr, kAppAddr - kUnusedAddr); + + InitializeFlags(flags(), GetEnv("DFSAN_OPTIONS")); + + InitializeInterceptors(); +} + +#ifndef DFSAN_NOLIBC +__attribute__((section(".preinit_array"), used)) +static void (*dfsan_init_ptr)(int, char **, char **) = dfsan_init; +#endif diff --git a/projects/compiler-rt/lib/dfsan/dfsan.h b/projects/compiler-rt/lib/dfsan/dfsan.h new file mode 100644 index 00000000..693e0c5e --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/dfsan.h @@ -0,0 +1,67 @@ +//===-- dfsan.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// Private DFSan header. +//===----------------------------------------------------------------------===// + +#ifndef DFSAN_H +#define DFSAN_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +// Copy declarations from public sanitizer/dfsan_interface.h header here. +typedef u16 dfsan_label; + +struct dfsan_label_info { + dfsan_label l1; + dfsan_label l2; + const char *desc; + void *userdata; +}; + +extern "C" { +void dfsan_set_label(dfsan_label label, void *addr, uptr size); +dfsan_label dfsan_read_label(const void *addr, uptr size); +dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2); +} // extern "C" + +template +void dfsan_set_label(dfsan_label label, T &data) { // NOLINT + dfsan_set_label(label, (void *)&data, sizeof(T)); +} + +namespace __dfsan { + +void InitializeInterceptors(); + +inline dfsan_label *shadow_for(void *ptr) { + return (dfsan_label *) ((((uptr) ptr) & ~0x700000000000) << 1); +} + +inline const dfsan_label *shadow_for(const void *ptr) { + return shadow_for(const_cast(ptr)); +} + +struct Flags { + // Whether to warn on unimplemented functions. + bool warn_unimplemented; + // Whether to warn on non-zero labels. + bool warn_nonzero_labels; +}; + +extern Flags flags_data; +inline Flags &flags() { + return flags_data; +} + +} // namespace __dfsan + +#endif // DFSAN_H diff --git a/projects/compiler-rt/lib/dfsan/dfsan.syms.extra b/projects/compiler-rt/lib/dfsan/dfsan.syms.extra new file mode 100644 index 00000000..0d507eef --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/dfsan.syms.extra @@ -0,0 +1,3 @@ +dfsan_* +__dfsan_* +__dfsw_* diff --git a/projects/compiler-rt/lib/dfsan/dfsan_custom.cc b/projects/compiler-rt/lib/dfsan/dfsan_custom.cc new file mode 100644 index 00000000..6a132db0 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/dfsan_custom.cc @@ -0,0 +1,341 @@ +//===-- dfsan.cc ----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// This file defines the custom functions listed in done_abilist.txt. +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_linux.h" + +#include "dfsan/dfsan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace __dfsan; + +extern "C" { + +SANITIZER_INTERFACE_ATTRIBUTE int +__dfsw_stat(const char *path, struct stat *buf, dfsan_label path_label, + dfsan_label buf_label, dfsan_label *ret_label) { + int ret = stat(path, buf); + if (ret == 0) + dfsan_set_label(0, buf, sizeof(struct stat)); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_fstat(int fd, struct stat *buf, + dfsan_label fd_label, + dfsan_label buf_label, + dfsan_label *ret_label) { + int ret = fstat(fd, buf); + if (ret == 0) + dfsan_set_label(0, buf, sizeof(struct stat)); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strchr(const char *s, int c, + dfsan_label s_label, + dfsan_label c_label, + dfsan_label *ret_label) { + for (size_t i = 0;; ++i) { + if (s[i] == c || s[i] == 0) { + *ret_label = dfsan_union(dfsan_read_label(s, i+1), c_label); + return s[i] == 0 ? 0 : const_cast(s+i); + } + } +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_memcmp(const void *s1, const void *s2, + size_t n, dfsan_label s1_label, + dfsan_label s2_label, + dfsan_label n_label, + dfsan_label *ret_label) { + const char *cs1 = (const char *) s1, *cs2 = (const char *) s2; + for (size_t i = 0; i != n; ++i) { + if (cs1[i] != cs2[i]) { + *ret_label = dfsan_union(dfsan_read_label(cs1, i+1), + dfsan_read_label(cs2, i+1)); + return cs1[i] - cs2[i]; + } + } + *ret_label = dfsan_union(dfsan_read_label(cs1, n), + dfsan_read_label(cs2, n)); + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strcmp(const char *s1, const char *s2, + dfsan_label s1_label, + dfsan_label s2_label, + dfsan_label *ret_label) { + for (size_t i = 0;; ++i) { + if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int +__dfsw_strcasecmp(const char *s1, const char *s2, dfsan_label s1_label, + dfsan_label s2_label, dfsan_label *ret_label) { + for (size_t i = 0;; ++i) { + if (tolower(s1[i]) != tolower(s2[i]) || s1[i] == 0 || s2[i] == 0) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strncmp(const char *s1, const char *s2, + size_t n, dfsan_label s1_label, + dfsan_label s2_label, + dfsan_label n_label, + dfsan_label *ret_label) { + if (n == 0) { + *ret_label = 0; + return 0; + } + + for (size_t i = 0;; ++i) { + if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0 || i == n-1) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE int +__dfsw_strncasecmp(const char *s1, const char *s2, size_t n, + dfsan_label s1_label, dfsan_label s2_label, + dfsan_label n_label, dfsan_label *ret_label) { + if (n == 0) { + *ret_label = 0; + return 0; + } + + for (size_t i = 0;; ++i) { + if (tolower(s1[i]) != tolower(s2[i]) || s1[i] == 0 || s2[i] == 0 || + i == n - 1) { + *ret_label = dfsan_union(dfsan_read_label(s1, i+1), + dfsan_read_label(s2, i+1)); + return s1[i] - s2[i]; + } + } + return 0; +} + +SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_calloc(size_t nmemb, size_t size, + dfsan_label nmemb_label, + dfsan_label size_label, + dfsan_label *ret_label) { + void *p = calloc(nmemb, size); + dfsan_set_label(0, p, nmemb * size); + *ret_label = 0; + return p; +} + +SANITIZER_INTERFACE_ATTRIBUTE size_t +__dfsw_strlen(const char *s, dfsan_label s_label, dfsan_label *ret_label) { + size_t ret = strlen(s); + *ret_label = dfsan_read_label(s, ret+1); + return ret; +} + + +static void *dfsan_memcpy(void *dest, const void *src, size_t n) { + dfsan_label *sdest = shadow_for(dest), *ssrc = shadow_for((void *)src); + internal_memcpy((void *)sdest, (void *)ssrc, n * sizeof(dfsan_label)); + return internal_memcpy(dest, src, n); +} + +static void dfsan_memset(void *s, int c, dfsan_label c_label, size_t n) { + internal_memset(s, c, n); + dfsan_set_label(c_label, s, n); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *__dfsw_memcpy(void *dest, const void *src, size_t n, + dfsan_label dest_label, dfsan_label src_label, + dfsan_label n_label, dfsan_label *ret_label) { + *ret_label = 0; + return dfsan_memcpy(dest, src, n); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *__dfsw_memset(void *s, int c, size_t n, + dfsan_label s_label, dfsan_label c_label, + dfsan_label n_label, dfsan_label *ret_label) { + dfsan_memset(s, c, c_label, n); + *ret_label = 0; + return s; +} + +SANITIZER_INTERFACE_ATTRIBUTE char * +__dfsw_strdup(const char *s, dfsan_label s_label, dfsan_label *ret_label) { + size_t len = strlen(s); + void *p = malloc(len+1); + dfsan_memcpy(p, s, len+1); + *ret_label = 0; + return static_cast(p); +} + +SANITIZER_INTERFACE_ATTRIBUTE char * +__dfsw_strncpy(char *s1, const char *s2, size_t n, dfsan_label s1_label, + dfsan_label s2_label, dfsan_label n_label, + dfsan_label *ret_label) { + size_t len = strlen(s2); + if (len < n) { + dfsan_memcpy(s1, s2, len+1); + dfsan_memset(s1+len+1, 0, 0, n-len-1); + } else { + dfsan_memcpy(s1, s2, n); + } + + *ret_label = 0; + return s1; +} + +SANITIZER_INTERFACE_ATTRIBUTE ssize_t +__dfsw_pread(int fd, void *buf, size_t count, off_t offset, + dfsan_label fd_label, dfsan_label buf_label, + dfsan_label count_label, dfsan_label offset_label, + dfsan_label *ret_label) { + ssize_t ret = pread(fd, buf, count, offset); + if (ret > 0) + dfsan_set_label(0, buf, ret); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE ssize_t +__dfsw_read(int fd, void *buf, size_t count, + dfsan_label fd_label, dfsan_label buf_label, + dfsan_label count_label, + dfsan_label *ret_label) { + ssize_t ret = read(fd, buf, count); + if (ret > 0) + dfsan_set_label(0, buf, ret); + *ret_label = 0; + return ret; +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_clock_gettime(clockid_t clk_id, + struct timespec *tp, + dfsan_label clk_id_label, + dfsan_label tp_label, + dfsan_label *ret_label) { + int ret = clock_gettime(clk_id, tp); + if (ret == 0) + dfsan_set_label(0, tp, sizeof(struct timespec)); + *ret_label = 0; + return ret; +} + +static void unpoison(const void *ptr, uptr size) { + dfsan_set_label(0, const_cast(ptr), size); +} + +// dlopen() ultimately calls mmap() down inside the loader, which generally +// doesn't participate in dynamic symbol resolution. Therefore we won't +// intercept its calls to mmap, and we have to hook it here. +SANITIZER_INTERFACE_ATTRIBUTE void * +__dfsw_dlopen(const char *filename, int flag, dfsan_label filename_label, + dfsan_label flag_label, dfsan_label *ret_label) { + link_map *map = (link_map *)dlopen(filename, flag); + if (map) + ForEachMappedRegion(map, unpoison); + *ret_label = 0; + return (void *)map; +} + +struct pthread_create_info { + void *(*start_routine_trampoline)(void *, void *, dfsan_label, dfsan_label *); + void *start_routine; + void *arg; +}; + +static void *pthread_create_cb(void *p) { + pthread_create_info pci(*(pthread_create_info *)p); + free(p); + dfsan_label ret_label; + return pci.start_routine_trampoline(pci.start_routine, pci.arg, 0, + &ret_label); +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_pthread_create( + pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine_trampoline)(void *, void *, dfsan_label, + dfsan_label *), + void *start_routine, void *arg, dfsan_label thread_label, + dfsan_label attr_label, dfsan_label start_routine_label, + dfsan_label arg_label, dfsan_label *ret_label) { + pthread_create_info *pci = + (pthread_create_info *)malloc(sizeof(pthread_create_info)); + pci->start_routine_trampoline = start_routine_trampoline; + pci->start_routine = start_routine; + pci->arg = arg; + int rv = pthread_create(thread, attr, pthread_create_cb, (void *)pci); + if (rv != 0) + free(pci); + *ret_label = 0; + return rv; +} + +struct dl_iterate_phdr_info { + int (*callback_trampoline)(void *callback, struct dl_phdr_info *info, + size_t size, void *data, dfsan_label info_label, + dfsan_label size_label, dfsan_label data_label, + dfsan_label *ret_label); + void *callback; + void *data; +}; + +int dl_iterate_phdr_cb(struct dl_phdr_info *info, size_t size, void *data) { + dl_iterate_phdr_info *dipi = (dl_iterate_phdr_info *)data; + dfsan_set_label(0, *info); + dfsan_set_label(0, (void *)info->dlpi_name, strlen(info->dlpi_name) + 1); + dfsan_set_label(0, (void *)info->dlpi_phdr, + sizeof(*info->dlpi_phdr) * info->dlpi_phnum); + dfsan_label ret_label; + return dipi->callback_trampoline(dipi->callback, info, size, dipi->data, 0, 0, + 0, &ret_label); +} + +SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_dl_iterate_phdr( + int (*callback_trampoline)(void *callback, struct dl_phdr_info *info, + size_t size, void *data, dfsan_label info_label, + dfsan_label size_label, dfsan_label data_label, + dfsan_label *ret_label), + void *callback, void *data, dfsan_label callback_label, + dfsan_label data_label, dfsan_label *ret_label) { + dl_iterate_phdr_info dipi = { callback_trampoline, callback, data }; + *ret_label = 0; + return dl_iterate_phdr(dl_iterate_phdr_cb, &dipi); +} + +} diff --git a/projects/compiler-rt/lib/dfsan/dfsan_interceptors.cc b/projects/compiler-rt/lib/dfsan/dfsan_interceptors.cc new file mode 100644 index 00000000..8b7d64e2 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/dfsan_interceptors.cc @@ -0,0 +1,44 @@ +//===-- dfsan_interceptors.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// Interceptors for standard library functions. +//===----------------------------------------------------------------------===// + +#include "dfsan/dfsan.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" + +INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF_T offset) { + void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + dfsan_set_label(0, res, RoundUpTo(length, GetPageSize())); + return res; +} + +INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF64_T offset) { + void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + dfsan_set_label(0, res, RoundUpTo(length, GetPageSize())); + return res; +} + +namespace __dfsan { +void InitializeInterceptors() { + static int inited = 0; + CHECK_EQ(inited, 0); + + INTERCEPT_FUNCTION(mmap); + INTERCEPT_FUNCTION(mmap64); + inited = 1; +} +} // namespace __dfsan diff --git a/projects/compiler-rt/lib/dfsan/done_abilist.txt b/projects/compiler-rt/lib/dfsan/done_abilist.txt new file mode 100644 index 00000000..27df3e9b --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/done_abilist.txt @@ -0,0 +1,127 @@ +fun:main=uninstrumented +fun:main=discard + +# DFSan interface functions. +fun:dfsan_union=uninstrumented +fun:dfsan_union=discard + +fun:dfsan_create_label=uninstrumented +fun:dfsan_create_label=discard + +fun:dfsan_set_label=uninstrumented +fun:dfsan_set_label=discard + +fun:dfsan_add_label=uninstrumented +fun:dfsan_add_label=discard + +fun:dfsan_get_label=uninstrumented +fun:dfsan_get_label=custom + +fun:dfsan_read_label=uninstrumented +fun:dfsan_read_label=discard + +fun:dfsan_get_label_info=uninstrumented +fun:dfsan_get_label_info=discard + +fun:dfsan_has_label=uninstrumented +fun:dfsan_has_label=discard + +fun:dfsan_has_label_with_desc=uninstrumented +fun:dfsan_has_label_with_desc=discard + +# glibc functions. +fun:malloc=discard +fun:realloc=discard +fun:free=discard +fun:isalpha=functional +fun:isdigit=functional +fun:isprint=functional +fun:isxdigit=functional +fun:isalnum=functional +fun:ispunct=functional +fun:isspace=functional +fun:tolower=functional +fun:toupper=functional +fun:exp=functional +fun:exp2=functional +fun:log=functional +fun:sqrt=functional +fun:__cxa_atexit=discard +fun:open=discard +fun:pthread_key_create=discard +fun:getenv=discard +fun:__ctype_b_loc=discard +fun:__errno_location=discard +fun:mmap=discard +fun:munmap=discard +fun:write=discard +fun:close=discard +fun:pthread_equal=discard +fun:pthread_getspecific=discard +fun:pthread_setspecific=discard +fun:pthread_mutex_destroy=discard +fun:pthread_mutexattr_init=discard +fun:pthread_mutexattr_settype=discard +fun:pthread_mutex_init=discard +fun:pthread_mutex_lock=discard +fun:pthread_mutex_trylock=discard +fun:pthread_mutex_unlock=discard +fun:pthread_mutexattr_destroy=discard +fun:pthread_once=discard +fun:pthread_key_delete=discard +fun:pthread_self=discard +fun:printf=discard +fun:fprintf=discard +fun:fputs=discard +fun:fputc=discard +fun:fopen=discard +fun:fseek=discard +fun:lseek=discard +fun:ftell=discard +fun:fclose=discard +fun:dladdr=discard +fun:getpagesize=discard +fun:sched_getcpu=discard +fun:sched_getaffinity=discard +fun:sched_setaffinity=discard +fun:syscall=discard +fun:sem_init=discard +fun:sem_post=discard +fun:sem_wait=discard +fun:sched_yield=discard +fun:uselocale=discard +fun:rand=discard +fun:random=discard +fun:sleep=discard + +fun:stat=custom +fun:fstat=custom +fun:memcmp=custom +fun:memcpy=custom +fun:memset=custom +fun:strcmp=custom +fun:strdup=custom +fun:strncmp=custom +fun:strncpy=custom +fun:strcasecmp=custom +fun:strncasecmp=custom +fun:strchr=custom +fun:strlen=custom +fun:calloc=custom +fun:dlopen=custom +fun:read=custom +fun:pread=custom +fun:clock_gettime=custom +fun:pthread_create=custom +fun:dl_iterate_phdr=custom + +# TODO: custom +fun:snprintf=discard +fun:vsnprintf=discard +fun:asprintf=discard +fun:qsort=discard +fun:strtoll=discard +fun:strtoull=discard +fun:sigemptyset=discard +fun:sigaction=discard +fun:gettimeofday=discard diff --git a/projects/compiler-rt/lib/dfsan/libc_ubuntu1204_abilist.txt b/projects/compiler-rt/lib/dfsan/libc_ubuntu1204_abilist.txt new file mode 100644 index 00000000..5fc8194c --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/libc_ubuntu1204_abilist.txt @@ -0,0 +1,3685 @@ +fun:_Exit=uninstrumented +fun:_IO_adjust_column=uninstrumented +fun:_IO_adjust_wcolumn=uninstrumented +fun:_IO_default_doallocate=uninstrumented +fun:_IO_default_finish=uninstrumented +fun:_IO_default_pbackfail=uninstrumented +fun:_IO_default_uflow=uninstrumented +fun:_IO_default_xsgetn=uninstrumented +fun:_IO_default_xsputn=uninstrumented +fun:_IO_do_write=uninstrumented +fun:_IO_doallocbuf=uninstrumented +fun:_IO_fclose=uninstrumented +fun:_IO_fdopen=uninstrumented +fun:_IO_feof=uninstrumented +fun:_IO_ferror=uninstrumented +fun:_IO_fflush=uninstrumented +fun:_IO_fgetpos=uninstrumented +fun:_IO_fgetpos64=uninstrumented +fun:_IO_fgets=uninstrumented +fun:_IO_file_attach=uninstrumented +fun:_IO_file_close=uninstrumented +fun:_IO_file_close_it=uninstrumented +fun:_IO_file_doallocate=uninstrumented +fun:_IO_file_finish=uninstrumented +fun:_IO_file_fopen=uninstrumented +fun:_IO_file_init=uninstrumented +fun:_IO_file_open=uninstrumented +fun:_IO_file_overflow=uninstrumented +fun:_IO_file_read=uninstrumented +fun:_IO_file_seek=uninstrumented +fun:_IO_file_seekoff=uninstrumented +fun:_IO_file_setbuf=uninstrumented +fun:_IO_file_stat=uninstrumented +fun:_IO_file_sync=uninstrumented +fun:_IO_file_underflow=uninstrumented +fun:_IO_file_write=uninstrumented +fun:_IO_file_xsputn=uninstrumented +fun:_IO_flockfile=uninstrumented +fun:_IO_flush_all=uninstrumented +fun:_IO_flush_all_linebuffered=uninstrumented +fun:_IO_fopen=uninstrumented +fun:_IO_fprintf=uninstrumented +fun:_IO_fputs=uninstrumented +fun:_IO_fread=uninstrumented +fun:_IO_free_backup_area=uninstrumented +fun:_IO_free_wbackup_area=uninstrumented +fun:_IO_fsetpos=uninstrumented +fun:_IO_fsetpos64=uninstrumented +fun:_IO_ftell=uninstrumented +fun:_IO_ftrylockfile=uninstrumented +fun:_IO_funlockfile=uninstrumented +fun:_IO_fwrite=uninstrumented +fun:_IO_getc=uninstrumented +fun:_IO_getline=uninstrumented +fun:_IO_getline_info=uninstrumented +fun:_IO_gets=uninstrumented +fun:_IO_init=uninstrumented +fun:_IO_init_marker=uninstrumented +fun:_IO_init_wmarker=uninstrumented +fun:_IO_iter_begin=uninstrumented +fun:_IO_iter_end=uninstrumented +fun:_IO_iter_file=uninstrumented +fun:_IO_iter_next=uninstrumented +fun:_IO_least_wmarker=uninstrumented +fun:_IO_link_in=uninstrumented +fun:_IO_list_lock=uninstrumented +fun:_IO_list_resetlock=uninstrumented +fun:_IO_list_unlock=uninstrumented +fun:_IO_marker_delta=uninstrumented +fun:_IO_marker_difference=uninstrumented +fun:_IO_padn=uninstrumented +fun:_IO_peekc_locked=uninstrumented +fun:_IO_popen=uninstrumented +fun:_IO_printf=uninstrumented +fun:_IO_proc_close=uninstrumented +fun:_IO_proc_open=uninstrumented +fun:_IO_putc=uninstrumented +fun:_IO_puts=uninstrumented +fun:_IO_remove_marker=uninstrumented +fun:_IO_seekmark=uninstrumented +fun:_IO_seekoff=uninstrumented +fun:_IO_seekpos=uninstrumented +fun:_IO_seekwmark=uninstrumented +fun:_IO_setb=uninstrumented +fun:_IO_setbuffer=uninstrumented +fun:_IO_setvbuf=uninstrumented +fun:_IO_sgetn=uninstrumented +fun:_IO_sprintf=uninstrumented +fun:_IO_sputbackc=uninstrumented +fun:_IO_sputbackwc=uninstrumented +fun:_IO_sscanf=uninstrumented +fun:_IO_str_init_readonly=uninstrumented +fun:_IO_str_init_static=uninstrumented +fun:_IO_str_overflow=uninstrumented +fun:_IO_str_pbackfail=uninstrumented +fun:_IO_str_seekoff=uninstrumented +fun:_IO_str_underflow=uninstrumented +fun:_IO_sungetc=uninstrumented +fun:_IO_sungetwc=uninstrumented +fun:_IO_switch_to_get_mode=uninstrumented +fun:_IO_switch_to_main_wget_area=uninstrumented +fun:_IO_switch_to_wbackup_area=uninstrumented +fun:_IO_switch_to_wget_mode=uninstrumented +fun:_IO_un_link=uninstrumented +fun:_IO_ungetc=uninstrumented +fun:_IO_unsave_markers=uninstrumented +fun:_IO_unsave_wmarkers=uninstrumented +fun:_IO_vfprintf=uninstrumented +fun:_IO_vfscanf=uninstrumented +fun:_IO_vsprintf=uninstrumented +fun:_IO_wdefault_doallocate=uninstrumented +fun:_IO_wdefault_finish=uninstrumented +fun:_IO_wdefault_pbackfail=uninstrumented +fun:_IO_wdefault_uflow=uninstrumented +fun:_IO_wdefault_xsgetn=uninstrumented +fun:_IO_wdefault_xsputn=uninstrumented +fun:_IO_wdo_write=uninstrumented +fun:_IO_wdoallocbuf=uninstrumented +fun:_IO_wfile_overflow=uninstrumented +fun:_IO_wfile_seekoff=uninstrumented +fun:_IO_wfile_sync=uninstrumented +fun:_IO_wfile_underflow=uninstrumented +fun:_IO_wfile_xsputn=uninstrumented +fun:_IO_wmarker_delta=uninstrumented +fun:_IO_wsetb=uninstrumented +fun:_L_cond_lock_1021=uninstrumented +fun:_L_cond_lock_874=uninstrumented +fun:_L_cond_lock_918=uninstrumented +fun:_L_lock_1006=uninstrumented +fun:_L_lock_125=uninstrumented +fun:_L_lock_1281=uninstrumented +fun:_L_lock_13=uninstrumented +fun:_L_lock_133=uninstrumented +fun:_L_lock_1392=uninstrumented +fun:_L_lock_175=uninstrumented +fun:_L_lock_19=uninstrumented +fun:_L_lock_2009=uninstrumented +fun:_L_lock_21=uninstrumented +fun:_L_lock_211=uninstrumented +fun:_L_lock_2143=uninstrumented +fun:_L_lock_227=uninstrumented +fun:_L_lock_23=uninstrumented +fun:_L_lock_2338=uninstrumented +fun:_L_lock_26=uninstrumented +fun:_L_lock_29=uninstrumented +fun:_L_lock_3116=uninstrumented +fun:_L_lock_32=uninstrumented +fun:_L_lock_3335=uninstrumented +fun:_L_lock_34=uninstrumented +fun:_L_lock_37=uninstrumented +fun:_L_lock_40=uninstrumented +fun:_L_lock_4016=uninstrumented +fun:_L_lock_4410=uninstrumented +fun:_L_lock_451=uninstrumented +fun:_L_lock_4590=uninstrumented +fun:_L_lock_466=uninstrumented +fun:_L_lock_641=uninstrumented +fun:_L_lock_858=uninstrumented +fun:_L_lock_903=uninstrumented +fun:_L_robust_cond_lock_164=uninstrumented +fun:_L_robust_lock_160=uninstrumented +fun:_L_robust_timedlock_361=uninstrumented +fun:_L_robust_unlock_189=uninstrumented +fun:_L_timedlock_1043=uninstrumented +fun:_L_timedlock_108=uninstrumented +fun:_L_timedlock_292=uninstrumented +fun:_L_unlock_114=uninstrumented +fun:_L_unlock_1157=uninstrumented +fun:_L_unlock_1355=uninstrumented +fun:_L_unlock_137=uninstrumented +fun:_L_unlock_157=uninstrumented +fun:_L_unlock_16=uninstrumented +fun:_L_unlock_170=uninstrumented +fun:_L_unlock_174=uninstrumented +fun:_L_unlock_177=uninstrumented +fun:_L_unlock_1946=uninstrumented +fun:_L_unlock_197=uninstrumented +fun:_L_unlock_2117=uninstrumented +fun:_L_unlock_2318=uninstrumented +fun:_L_unlock_2384=uninstrumented +fun:_L_unlock_27=uninstrumented +fun:_L_unlock_3119=uninstrumented +fun:_L_unlock_312=uninstrumented +fun:_L_unlock_3464=uninstrumented +fun:_L_unlock_37=uninstrumented +fun:_L_unlock_3744=uninstrumented +fun:_L_unlock_4037=uninstrumented +fun:_L_unlock_43=uninstrumented +fun:_L_unlock_4353=uninstrumented +fun:_L_unlock_4432=uninstrumented +fun:_L_unlock_4525=uninstrumented +fun:_L_unlock_4619=uninstrumented +fun:_L_unlock_494=uninstrumented +fun:_L_unlock_54=uninstrumented +fun:_L_unlock_544=uninstrumented +fun:_L_unlock_61=uninstrumented +fun:_L_unlock_62=uninstrumented +fun:_L_unlock_64=uninstrumented +fun:_L_unlock_644=uninstrumented +fun:_L_unlock_698=uninstrumented +fun:_L_unlock_708=uninstrumented +fun:_L_unlock_88=uninstrumented +fun:_Unwind_Backtrace=uninstrumented +fun:_Unwind_DeleteException=uninstrumented +fun:_Unwind_FindEnclosingFunction=uninstrumented +fun:_Unwind_Find_FDE=uninstrumented +fun:_Unwind_ForcedUnwind=uninstrumented +fun:_Unwind_GetCFA=uninstrumented +fun:_Unwind_GetDataRelBase=uninstrumented +fun:_Unwind_GetGR=uninstrumented +fun:_Unwind_GetIP=uninstrumented +fun:_Unwind_GetIPInfo=uninstrumented +fun:_Unwind_GetLanguageSpecificData=uninstrumented +fun:_Unwind_GetRegionStart=uninstrumented +fun:_Unwind_GetTextRelBase=uninstrumented +fun:_Unwind_RaiseException=uninstrumented +fun:_Unwind_Resume=uninstrumented +fun:_Unwind_Resume_or_Rethrow=uninstrumented +fun:_Unwind_SetGR=uninstrumented +fun:_Unwind_SetIP=uninstrumented +fun:__GI___nptl_create_event=uninstrumented +fun:__GI___nptl_death_event=uninstrumented +fun:__GI___pthread_cleanup_upto=uninstrumented +fun:__GI___pthread_register_cancel=uninstrumented +fun:__GI___pthread_unregister_cancel=uninstrumented +fun:__GI___pthread_unwind=uninstrumented +fun:__GI___pthread_unwind_next=uninstrumented +fun:__absvdi2=uninstrumented +fun:__absvsi2=uninstrumented +fun:__absvti2=uninstrumented +fun:__accept=uninstrumented +fun:__accept_nocancel=uninstrumented +fun:__acos_finite=uninstrumented +fun:__acosf_finite=uninstrumented +fun:__acosh_finite=uninstrumented +fun:__acoshf_finite=uninstrumented +fun:__acoshl_finite=uninstrumented +fun:__acosl_finite=uninstrumented +fun:__addtf3=uninstrumented +fun:__addvdi3=uninstrumented +fun:__addvsi3=uninstrumented +fun:__addvti3=uninstrumented +fun:__adjtimex=uninstrumented +fun:__arch_prctl=uninstrumented +fun:__argz_count=uninstrumented +fun:__argz_next=uninstrumented +fun:__argz_stringify=uninstrumented +fun:__ashlti3=uninstrumented +fun:__ashrti3=uninstrumented +fun:__asin_finite=uninstrumented +fun:__asinf_finite=uninstrumented +fun:__asinl_finite=uninstrumented +fun:__asprintf=uninstrumented +fun:__asprintf_chk=uninstrumented +fun:__assert=uninstrumented +fun:__assert_fail=uninstrumented +fun:__assert_perror_fail=uninstrumented +fun:__atan2_finite=uninstrumented +fun:__atan2f_finite=uninstrumented +fun:__atan2l_finite=uninstrumented +fun:__atanh_finite=uninstrumented +fun:__atanhf_finite=uninstrumented +fun:__atanhl_finite=uninstrumented +fun:__b64_ntop=uninstrumented +fun:__b64_pton=uninstrumented +fun:__backtrace=uninstrumented +fun:__backtrace_symbols=uninstrumented +fun:__backtrace_symbols_fd=uninstrumented +fun:__bid128_abs=uninstrumented +fun:__bid128_add=uninstrumented +fun:__bid128_class=uninstrumented +fun:__bid128_copy=uninstrumented +fun:__bid128_copySign=uninstrumented +fun:__bid128_div=uninstrumented +fun:__bid128_fma=uninstrumented +fun:__bid128_from_int32=uninstrumented +fun:__bid128_from_int64=uninstrumented +fun:__bid128_from_uint32=uninstrumented +fun:__bid128_from_uint64=uninstrumented +fun:__bid128_isCanonical=uninstrumented +fun:__bid128_isFinite=uninstrumented +fun:__bid128_isInf=uninstrumented +fun:__bid128_isNaN=uninstrumented +fun:__bid128_isNormal=uninstrumented +fun:__bid128_isSignaling=uninstrumented +fun:__bid128_isSigned=uninstrumented +fun:__bid128_isSubnormal=uninstrumented +fun:__bid128_isZero=uninstrumented +fun:__bid128_mul=uninstrumented +fun:__bid128_negate=uninstrumented +fun:__bid128_quiet_equal=uninstrumented +fun:__bid128_quiet_greater=uninstrumented +fun:__bid128_quiet_greater_equal=uninstrumented +fun:__bid128_quiet_greater_unordered=uninstrumented +fun:__bid128_quiet_less=uninstrumented +fun:__bid128_quiet_less_equal=uninstrumented +fun:__bid128_quiet_less_unordered=uninstrumented +fun:__bid128_quiet_not_equal=uninstrumented +fun:__bid128_quiet_not_greater=uninstrumented +fun:__bid128_quiet_not_less=uninstrumented +fun:__bid128_quiet_ordered=uninstrumented +fun:__bid128_quiet_unordered=uninstrumented +fun:__bid128_radix=uninstrumented +fun:__bid128_sameQuantum=uninstrumented +fun:__bid128_signaling_greater=uninstrumented +fun:__bid128_signaling_greater_equal=uninstrumented +fun:__bid128_signaling_greater_unordered=uninstrumented +fun:__bid128_signaling_less=uninstrumented +fun:__bid128_signaling_less_equal=uninstrumented +fun:__bid128_signaling_less_unordered=uninstrumented +fun:__bid128_signaling_not_greater=uninstrumented +fun:__bid128_signaling_not_less=uninstrumented +fun:__bid128_sub=uninstrumented +fun:__bid128_to_bid32=uninstrumented +fun:__bid128_to_bid64=uninstrumented +fun:__bid128_to_binary128=uninstrumented +fun:__bid128_to_binary32=uninstrumented +fun:__bid128_to_binary64=uninstrumented +fun:__bid128_to_binary80=uninstrumented +fun:__bid128_to_int32_ceil=uninstrumented +fun:__bid128_to_int32_floor=uninstrumented +fun:__bid128_to_int32_int=uninstrumented +fun:__bid128_to_int32_rnint=uninstrumented +fun:__bid128_to_int32_rninta=uninstrumented +fun:__bid128_to_int32_xceil=uninstrumented +fun:__bid128_to_int32_xfloor=uninstrumented +fun:__bid128_to_int32_xint=uninstrumented +fun:__bid128_to_int32_xrnint=uninstrumented +fun:__bid128_to_int32_xrninta=uninstrumented +fun:__bid128_to_int64_ceil=uninstrumented +fun:__bid128_to_int64_floor=uninstrumented +fun:__bid128_to_int64_int=uninstrumented +fun:__bid128_to_int64_rnint=uninstrumented +fun:__bid128_to_int64_rninta=uninstrumented +fun:__bid128_to_int64_xceil=uninstrumented +fun:__bid128_to_int64_xfloor=uninstrumented +fun:__bid128_to_int64_xint=uninstrumented +fun:__bid128_to_int64_xrnint=uninstrumented +fun:__bid128_to_int64_xrninta=uninstrumented +fun:__bid128_to_uint32_ceil=uninstrumented +fun:__bid128_to_uint32_floor=uninstrumented +fun:__bid128_to_uint32_int=uninstrumented +fun:__bid128_to_uint32_rnint=uninstrumented +fun:__bid128_to_uint32_rninta=uninstrumented +fun:__bid128_to_uint32_xceil=uninstrumented +fun:__bid128_to_uint32_xfloor=uninstrumented +fun:__bid128_to_uint32_xint=uninstrumented +fun:__bid128_to_uint32_xrnint=uninstrumented +fun:__bid128_to_uint32_xrninta=uninstrumented +fun:__bid128_to_uint64_ceil=uninstrumented +fun:__bid128_to_uint64_floor=uninstrumented +fun:__bid128_to_uint64_int=uninstrumented +fun:__bid128_to_uint64_rnint=uninstrumented +fun:__bid128_to_uint64_rninta=uninstrumented +fun:__bid128_to_uint64_xceil=uninstrumented +fun:__bid128_to_uint64_xfloor=uninstrumented +fun:__bid128_to_uint64_xint=uninstrumented +fun:__bid128_to_uint64_xrnint=uninstrumented +fun:__bid128_to_uint64_xrninta=uninstrumented +fun:__bid128_totalOrder=uninstrumented +fun:__bid128_totalOrderMag=uninstrumented +fun:__bid128dd_add=uninstrumented +fun:__bid128dd_div=uninstrumented +fun:__bid128dd_mul=uninstrumented +fun:__bid128dd_sub=uninstrumented +fun:__bid128ddd_fma=uninstrumented +fun:__bid128ddq_fma=uninstrumented +fun:__bid128dq_add=uninstrumented +fun:__bid128dq_div=uninstrumented +fun:__bid128dq_mul=uninstrumented +fun:__bid128dq_sub=uninstrumented +fun:__bid128dqd_fma=uninstrumented +fun:__bid128dqq_fma=uninstrumented +fun:__bid128qd_add=uninstrumented +fun:__bid128qd_div=uninstrumented +fun:__bid128qd_mul=uninstrumented +fun:__bid128qd_sub=uninstrumented +fun:__bid128qdd_fma=uninstrumented +fun:__bid128qdq_fma=uninstrumented +fun:__bid128qqd_fma=uninstrumented +fun:__bid32_to_bid128=uninstrumented +fun:__bid32_to_bid64=uninstrumented +fun:__bid32_to_binary128=uninstrumented +fun:__bid32_to_binary32=uninstrumented +fun:__bid32_to_binary64=uninstrumented +fun:__bid32_to_binary80=uninstrumented +fun:__bid64_abs=uninstrumented +fun:__bid64_add=uninstrumented +fun:__bid64_class=uninstrumented +fun:__bid64_copy=uninstrumented +fun:__bid64_copySign=uninstrumented +fun:__bid64_div=uninstrumented +fun:__bid64_from_int32=uninstrumented +fun:__bid64_from_int64=uninstrumented +fun:__bid64_from_uint32=uninstrumented +fun:__bid64_from_uint64=uninstrumented +fun:__bid64_isCanonical=uninstrumented +fun:__bid64_isFinite=uninstrumented +fun:__bid64_isInf=uninstrumented +fun:__bid64_isNaN=uninstrumented +fun:__bid64_isNormal=uninstrumented +fun:__bid64_isSignaling=uninstrumented +fun:__bid64_isSigned=uninstrumented +fun:__bid64_isSubnormal=uninstrumented +fun:__bid64_isZero=uninstrumented +fun:__bid64_mul=uninstrumented +fun:__bid64_negate=uninstrumented +fun:__bid64_quiet_equal=uninstrumented +fun:__bid64_quiet_greater=uninstrumented +fun:__bid64_quiet_greater_equal=uninstrumented +fun:__bid64_quiet_greater_unordered=uninstrumented +fun:__bid64_quiet_less=uninstrumented +fun:__bid64_quiet_less_equal=uninstrumented +fun:__bid64_quiet_less_unordered=uninstrumented +fun:__bid64_quiet_not_equal=uninstrumented +fun:__bid64_quiet_not_greater=uninstrumented +fun:__bid64_quiet_not_less=uninstrumented +fun:__bid64_quiet_ordered=uninstrumented +fun:__bid64_quiet_unordered=uninstrumented +fun:__bid64_radix=uninstrumented +fun:__bid64_sameQuantum=uninstrumented +fun:__bid64_signaling_greater=uninstrumented +fun:__bid64_signaling_greater_equal=uninstrumented +fun:__bid64_signaling_greater_unordered=uninstrumented +fun:__bid64_signaling_less=uninstrumented +fun:__bid64_signaling_less_equal=uninstrumented +fun:__bid64_signaling_less_unordered=uninstrumented +fun:__bid64_signaling_not_greater=uninstrumented +fun:__bid64_signaling_not_less=uninstrumented +fun:__bid64_sub=uninstrumented +fun:__bid64_to_bid128=uninstrumented +fun:__bid64_to_bid32=uninstrumented +fun:__bid64_to_binary128=uninstrumented +fun:__bid64_to_binary32=uninstrumented +fun:__bid64_to_binary64=uninstrumented +fun:__bid64_to_binary80=uninstrumented +fun:__bid64_to_int32_ceil=uninstrumented +fun:__bid64_to_int32_floor=uninstrumented +fun:__bid64_to_int32_int=uninstrumented +fun:__bid64_to_int32_rnint=uninstrumented +fun:__bid64_to_int32_rninta=uninstrumented +fun:__bid64_to_int32_xceil=uninstrumented +fun:__bid64_to_int32_xfloor=uninstrumented +fun:__bid64_to_int32_xint=uninstrumented +fun:__bid64_to_int32_xrnint=uninstrumented +fun:__bid64_to_int32_xrninta=uninstrumented +fun:__bid64_to_int64_ceil=uninstrumented +fun:__bid64_to_int64_floor=uninstrumented +fun:__bid64_to_int64_int=uninstrumented +fun:__bid64_to_int64_rnint=uninstrumented +fun:__bid64_to_int64_rninta=uninstrumented +fun:__bid64_to_int64_xceil=uninstrumented +fun:__bid64_to_int64_xfloor=uninstrumented +fun:__bid64_to_int64_xint=uninstrumented +fun:__bid64_to_int64_xrnint=uninstrumented +fun:__bid64_to_int64_xrninta=uninstrumented +fun:__bid64_to_uint32_ceil=uninstrumented +fun:__bid64_to_uint32_floor=uninstrumented +fun:__bid64_to_uint32_int=uninstrumented +fun:__bid64_to_uint32_rnint=uninstrumented +fun:__bid64_to_uint32_rninta=uninstrumented +fun:__bid64_to_uint32_xceil=uninstrumented +fun:__bid64_to_uint32_xfloor=uninstrumented +fun:__bid64_to_uint32_xint=uninstrumented +fun:__bid64_to_uint32_xrnint=uninstrumented +fun:__bid64_to_uint32_xrninta=uninstrumented +fun:__bid64_to_uint64_ceil=uninstrumented +fun:__bid64_to_uint64_floor=uninstrumented +fun:__bid64_to_uint64_int=uninstrumented +fun:__bid64_to_uint64_rnint=uninstrumented +fun:__bid64_to_uint64_rninta=uninstrumented +fun:__bid64_to_uint64_xceil=uninstrumented +fun:__bid64_to_uint64_xfloor=uninstrumented +fun:__bid64_to_uint64_xint=uninstrumented +fun:__bid64_to_uint64_xrnint=uninstrumented +fun:__bid64_to_uint64_xrninta=uninstrumented +fun:__bid64_totalOrder=uninstrumented +fun:__bid64_totalOrderMag=uninstrumented +fun:__bid64ddq_fma=uninstrumented +fun:__bid64dq_add=uninstrumented +fun:__bid64dq_div=uninstrumented +fun:__bid64dq_mul=uninstrumented +fun:__bid64dq_sub=uninstrumented +fun:__bid64dqd_fma=uninstrumented +fun:__bid64dqq_fma=uninstrumented +fun:__bid64qd_add=uninstrumented +fun:__bid64qd_div=uninstrumented +fun:__bid64qd_mul=uninstrumented +fun:__bid64qd_sub=uninstrumented +fun:__bid64qdd_fma=uninstrumented +fun:__bid64qdq_fma=uninstrumented +fun:__bid64qq_add=uninstrumented +fun:__bid64qq_div=uninstrumented +fun:__bid64qq_mul=uninstrumented +fun:__bid64qq_sub=uninstrumented +fun:__bid64qqd_fma=uninstrumented +fun:__bid64qqq_fma=uninstrumented +fun:__bid_adddd3=uninstrumented +fun:__bid_addsd3=uninstrumented +fun:__bid_addtd3=uninstrumented +fun:__bid_divdd3=uninstrumented +fun:__bid_divsd3=uninstrumented +fun:__bid_divtd3=uninstrumented +fun:__bid_eqdd2=uninstrumented +fun:__bid_eqsd2=uninstrumented +fun:__bid_eqtd2=uninstrumented +fun:__bid_extendddtd2=uninstrumented +fun:__bid_extendddtf=uninstrumented +fun:__bid_extendddxf=uninstrumented +fun:__bid_extenddfdd=uninstrumented +fun:__bid_extenddftd=uninstrumented +fun:__bid_extendsddd2=uninstrumented +fun:__bid_extendsddf=uninstrumented +fun:__bid_extendsdtd2=uninstrumented +fun:__bid_extendsdtf=uninstrumented +fun:__bid_extendsdxf=uninstrumented +fun:__bid_extendsfdd=uninstrumented +fun:__bid_extendsfsd=uninstrumented +fun:__bid_extendsftd=uninstrumented +fun:__bid_extendtftd=uninstrumented +fun:__bid_extendxftd=uninstrumented +fun:__bid_fixdddi=uninstrumented +fun:__bid_fixddsi=uninstrumented +fun:__bid_fixsddi=uninstrumented +fun:__bid_fixsdsi=uninstrumented +fun:__bid_fixtddi=uninstrumented +fun:__bid_fixtdsi=uninstrumented +fun:__bid_fixunsdddi=uninstrumented +fun:__bid_fixunsddsi=uninstrumented +fun:__bid_fixunssddi=uninstrumented +fun:__bid_fixunssdsi=uninstrumented +fun:__bid_fixunstddi=uninstrumented +fun:__bid_fixunstdsi=uninstrumented +fun:__bid_floatdidd=uninstrumented +fun:__bid_floatdisd=uninstrumented +fun:__bid_floatditd=uninstrumented +fun:__bid_floatsidd=uninstrumented +fun:__bid_floatsisd=uninstrumented +fun:__bid_floatsitd=uninstrumented +fun:__bid_floatunsdidd=uninstrumented +fun:__bid_floatunsdisd=uninstrumented +fun:__bid_floatunsditd=uninstrumented +fun:__bid_floatunssidd=uninstrumented +fun:__bid_floatunssisd=uninstrumented +fun:__bid_floatunssitd=uninstrumented +fun:__bid_gedd2=uninstrumented +fun:__bid_gesd2=uninstrumented +fun:__bid_getd2=uninstrumented +fun:__bid_gtdd2=uninstrumented +fun:__bid_gtsd2=uninstrumented +fun:__bid_gttd2=uninstrumented +fun:__bid_ledd2=uninstrumented +fun:__bid_lesd2=uninstrumented +fun:__bid_letd2=uninstrumented +fun:__bid_ltdd2=uninstrumented +fun:__bid_ltsd2=uninstrumented +fun:__bid_lttd2=uninstrumented +fun:__bid_muldd3=uninstrumented +fun:__bid_mulsd3=uninstrumented +fun:__bid_multd3=uninstrumented +fun:__bid_nedd2=uninstrumented +fun:__bid_nesd2=uninstrumented +fun:__bid_netd2=uninstrumented +fun:__bid_round128_19_38=uninstrumented +fun:__bid_round192_39_57=uninstrumented +fun:__bid_round256_58_76=uninstrumented +fun:__bid_round64_2_18=uninstrumented +fun:__bid_subdd3=uninstrumented +fun:__bid_subsd3=uninstrumented +fun:__bid_subtd3=uninstrumented +fun:__bid_truncdddf=uninstrumented +fun:__bid_truncddsd2=uninstrumented +fun:__bid_truncddsf=uninstrumented +fun:__bid_truncdfsd=uninstrumented +fun:__bid_truncsdsf=uninstrumented +fun:__bid_trunctddd2=uninstrumented +fun:__bid_trunctddf=uninstrumented +fun:__bid_trunctdsd2=uninstrumented +fun:__bid_trunctdsf=uninstrumented +fun:__bid_trunctdtf=uninstrumented +fun:__bid_trunctdxf=uninstrumented +fun:__bid_trunctfdd=uninstrumented +fun:__bid_trunctfsd=uninstrumented +fun:__bid_truncxfdd=uninstrumented +fun:__bid_truncxfsd=uninstrumented +fun:__bid_unorddd2=uninstrumented +fun:__bid_unordsd2=uninstrumented +fun:__bid_unordtd2=uninstrumented +fun:__binary128_to_bid128=uninstrumented +fun:__binary128_to_bid32=uninstrumented +fun:__binary128_to_bid64=uninstrumented +fun:__binary32_to_bid128=uninstrumented +fun:__binary32_to_bid32=uninstrumented +fun:__binary32_to_bid64=uninstrumented +fun:__binary64_to_bid128=uninstrumented +fun:__binary64_to_bid32=uninstrumented +fun:__binary64_to_bid64=uninstrumented +fun:__binary80_to_bid128=uninstrumented +fun:__binary80_to_bid32=uninstrumented +fun:__binary80_to_bid64=uninstrumented +fun:__bsd_getpgrp=uninstrumented +fun:__bswapdi2=uninstrumented +fun:__bswapsi2=uninstrumented +fun:__bzero=uninstrumented +fun:__chk_fail=uninstrumented +fun:__clear_cache=uninstrumented +fun:__clog10=uninstrumented +fun:__clog10f=uninstrumented +fun:__clog10l=uninstrumented +fun:__clone=uninstrumented +fun:__close=uninstrumented +fun:__close_nocancel=uninstrumented +fun:__clzdi2=uninstrumented +fun:__clzti2=uninstrumented +fun:__cmpti2=uninstrumented +fun:__cmsg_nxthdr=uninstrumented +fun:__condvar_cleanup1=uninstrumented +fun:__condvar_cleanup2=uninstrumented +fun:__confstr_chk=uninstrumented +fun:__connect=uninstrumented +fun:__connect_internal=uninstrumented +fun:__connect_nocancel=uninstrumented +fun:__cosh_finite=uninstrumented +fun:__coshf_finite=uninstrumented +fun:__coshl_finite=uninstrumented +fun:__create_ib_request=uninstrumented +fun:__ctype_b_loc=uninstrumented +fun:__ctype_get_mb_cur_max=uninstrumented +fun:__ctype_init=uninstrumented +fun:__ctype_tolower_loc=uninstrumented +fun:__ctype_toupper_loc=uninstrumented +fun:__ctzdi2=uninstrumented +fun:__ctzti2=uninstrumented +fun:__cxa_at_quick_exit=uninstrumented +fun:__cxa_atexit=uninstrumented +fun:__cxa_finalize=uninstrumented +fun:__cyg_profile_func_enter=uninstrumented +fun:__cyg_profile_func_exit=uninstrumented +fun:__dcgettext=uninstrumented +fun:__deallocate_stack=uninstrumented +fun:__default_morecore=uninstrumented +fun:__deregister_frame=uninstrumented +fun:__deregister_frame_info=uninstrumented +fun:__deregister_frame_info_bases=uninstrumented +fun:__determine_cpumask_size=uninstrumented +fun:__dfp_clear_except=uninstrumented +fun:__dfp_get_round=uninstrumented +fun:__dfp_raise_except=uninstrumented +fun:__dfp_set_round=uninstrumented +fun:__dfp_test_except=uninstrumented +fun:__dgettext=uninstrumented +fun:__divdc3=uninstrumented +fun:__divsc3=uninstrumented +fun:__divtc3=uninstrumented +fun:__divtf3=uninstrumented +fun:__divti3=uninstrumented +fun:__divxc3=uninstrumented +fun:__dn_comp=uninstrumented +fun:__dn_count_labels=uninstrumented +fun:__dn_expand=uninstrumented +fun:__dn_skipname=uninstrumented +fun:__do_global_ctors_aux=uninstrumented +fun:__do_global_dtors_aux=uninstrumented +fun:__do_niscall3=uninstrumented +fun:__dprintf_chk=uninstrumented +fun:__dup2=uninstrumented +fun:__duplocale=uninstrumented +fun:__dyn_pthread_atfork=uninstrumented +fun:__emutls_get_address=uninstrumented +fun:__emutls_register_common=uninstrumented +fun:__enable_execute_stack=uninstrumented +fun:__endmntent=uninstrumented +fun:__eprintf=uninstrumented +fun:__eqtf2=uninstrumented +fun:__errno_location=uninstrumented +fun:__exp10_finite=uninstrumented +fun:__exp10f_finite=uninstrumented +fun:__exp10l_finite=uninstrumented +fun:__exp2_finite=uninstrumented +fun:__exp2f_finite=uninstrumented +fun:__exp2l_finite=uninstrumented +fun:__exp_finite=uninstrumented +fun:__expf_finite=uninstrumented +fun:__expl_finite=uninstrumented +fun:__extenddftf2=uninstrumented +fun:__extendsftf2=uninstrumented +fun:__extendxftf2=uninstrumented +fun:__fbufsize=uninstrumented +fun:__fcntl=uninstrumented +fun:__fcntl_nocancel=uninstrumented +fun:__fdelt_chk=uninstrumented +fun:__fdelt_warn=uninstrumented +fun:__fentry__=uninstrumented +fun:__ffs=uninstrumented +fun:__ffsdi2=uninstrumented +fun:__ffsti2=uninstrumented +fun:__fgets_chk=uninstrumented +fun:__fgets_unlocked_chk=uninstrumented +fun:__fgetws_chk=uninstrumented +fun:__fgetws_unlocked_chk=uninstrumented +fun:__find_in_stack_list=uninstrumented +fun:__find_thread_by_id=uninstrumented +fun:__finite=uninstrumented +fun:__finitef=uninstrumented +fun:__finitel=uninstrumented +fun:__fixdfti=uninstrumented +fun:__fixsfti=uninstrumented +fun:__fixtfdi=uninstrumented +fun:__fixtfsi=uninstrumented +fun:__fixtfti=uninstrumented +fun:__fixunsdfdi=uninstrumented +fun:__fixunsdfti=uninstrumented +fun:__fixunssfdi=uninstrumented +fun:__fixunssfti=uninstrumented +fun:__fixunstfdi=uninstrumented +fun:__fixunstfsi=uninstrumented +fun:__fixunstfti=uninstrumented +fun:__fixunsxfdi=uninstrumented +fun:__fixunsxfti=uninstrumented +fun:__fixxfti=uninstrumented +fun:__flbf=uninstrumented +fun:__floatditf=uninstrumented +fun:__floatsitf=uninstrumented +fun:__floattidf=uninstrumented +fun:__floattisf=uninstrumented +fun:__floattitf=uninstrumented +fun:__floattixf=uninstrumented +fun:__floatunditf=uninstrumented +fun:__floatunsitf=uninstrumented +fun:__floatuntidf=uninstrumented +fun:__floatuntisf=uninstrumented +fun:__floatuntitf=uninstrumented +fun:__floatuntixf=uninstrumented +fun:__flockfile=uninstrumented +fun:__fmod_finite=uninstrumented +fun:__fmodf_finite=uninstrumented +fun:__fmodl_finite=uninstrumented +fun:__follow_path=uninstrumented +fun:__fork=uninstrumented +fun:__fortify_fail=uninstrumented +fun:__fp_nquery=uninstrumented +fun:__fp_query=uninstrumented +fun:__fp_resstat=uninstrumented +fun:__fpclassify=uninstrumented +fun:__fpclassifyf=uninstrumented +fun:__fpclassifyl=uninstrumented +fun:__fpending=uninstrumented +fun:__fprintf_chk=uninstrumented +fun:__fpurge=uninstrumented +fun:__fread_chk=uninstrumented +fun:__fread_unlocked_chk=uninstrumented +fun:__freadable=uninstrumented +fun:__freading=uninstrumented +fun:__free_fdresult=uninstrumented +fun:__free_stacks=uninstrumented +fun:__free_tcb=uninstrumented +fun:__freelocale=uninstrumented +fun:__fsetlocking=uninstrumented +fun:__fstat=uninstrumented +fun:__fsync_nocancel=uninstrumented +fun:__ftrylockfile=uninstrumented +fun:__funlockfile=uninstrumented +fun:__fwprintf_chk=uninstrumented +fun:__fwritable=uninstrumented +fun:__fwriting=uninstrumented +fun:__fxstat=uninstrumented +fun:__fxstat64=uninstrumented +fun:__fxstatat=uninstrumented +fun:__fxstatat64=uninstrumented +fun:__gai_sigqueue=uninstrumented +fun:__gamma_r_finite=uninstrumented +fun:__gammaf_r_finite=uninstrumented +fun:__gammal_r_finite=uninstrumented +fun:__gcc_bcmp=uninstrumented +fun:__gcc_personality_v0=uninstrumented +fun:__gconv_get_alias_db=uninstrumented +fun:__gconv_get_cache=uninstrumented +fun:__gconv_get_modules_db=uninstrumented +fun:__generic_findstack=uninstrumented +fun:__generic_morestack=uninstrumented +fun:__generic_morestack_set_initial_sp=uninstrumented +fun:__generic_releasestack=uninstrumented +fun:__get_cpu_features=uninstrumented +fun:__getcwd_chk=uninstrumented +fun:__getdelim=uninstrumented +fun:__getdomainname_chk=uninstrumented +fun:__getf2=uninstrumented +fun:__getgroups_chk=uninstrumented +fun:__gethostname_chk=uninstrumented +fun:__getlogin_r_chk=uninstrumented +fun:__getmntent_r=uninstrumented +fun:__getpagesize=uninstrumented +fun:__getpgid=uninstrumented +fun:__getpid=uninstrumented +fun:__gets_chk=uninstrumented +fun:__gettimeofday=uninstrumented +fun:__getwd_chk=uninstrumented +fun:__gmtime_r=uninstrumented +fun:__gttf2=uninstrumented +fun:__h_errno_location=uninstrumented +fun:__hostalias=uninstrumented +fun:__hypot_finite=uninstrumented +fun:__hypotf_finite=uninstrumented +fun:__hypotl_finite=uninstrumented +fun:__init_sched_fifo_prio=uninstrumented +fun:__internal_endnetgrent=uninstrumented +fun:__internal_getnetgrent_r=uninstrumented +fun:__internal_setnetgrent=uninstrumented +fun:__isalnum_l=uninstrumented +fun:__isalpha_l=uninstrumented +fun:__isascii_l=uninstrumented +fun:__isblank_l=uninstrumented +fun:__iscntrl_l=uninstrumented +fun:__isctype=uninstrumented +fun:__isdigit_l=uninstrumented +fun:__isgraph_l=uninstrumented +fun:__isinf=uninstrumented +fun:__isinff=uninstrumented +fun:__isinfl=uninstrumented +fun:__islower_l=uninstrumented +fun:__isnan=uninstrumented +fun:__isnanf=uninstrumented +fun:__isnanl=uninstrumented +fun:__isoc99_fscanf=uninstrumented +fun:__isoc99_fwscanf=uninstrumented +fun:__isoc99_scanf=uninstrumented +fun:__isoc99_sscanf=uninstrumented +fun:__isoc99_swscanf=uninstrumented +fun:__isoc99_vfscanf=uninstrumented +fun:__isoc99_vfwscanf=uninstrumented +fun:__isoc99_vscanf=uninstrumented +fun:__isoc99_vsscanf=uninstrumented +fun:__isoc99_vswscanf=uninstrumented +fun:__isoc99_vwscanf=uninstrumented +fun:__isoc99_wscanf=uninstrumented +fun:__isprint_l=uninstrumented +fun:__ispunct_l=uninstrumented +fun:__isspace_l=uninstrumented +fun:__isupper_l=uninstrumented +fun:__iswalnum_l=uninstrumented +fun:__iswalpha_l=uninstrumented +fun:__iswblank_l=uninstrumented +fun:__iswcntrl_l=uninstrumented +fun:__iswctype=uninstrumented +fun:__iswctype_l=uninstrumented +fun:__iswdigit_l=uninstrumented +fun:__iswgraph_l=uninstrumented +fun:__iswlower_l=uninstrumented +fun:__iswprint_l=uninstrumented +fun:__iswpunct_l=uninstrumented +fun:__iswspace_l=uninstrumented +fun:__iswupper_l=uninstrumented +fun:__iswxdigit_l=uninstrumented +fun:__isxdigit_l=uninstrumented +fun:__ivaliduser=uninstrumented +fun:__j0_finite=uninstrumented +fun:__j0f_finite=uninstrumented +fun:__j0l_finite=uninstrumented +fun:__j1_finite=uninstrumented +fun:__j1f_finite=uninstrumented +fun:__j1l_finite=uninstrumented +fun:__jn_finite=uninstrumented +fun:__jnf_finite=uninstrumented +fun:__jnl_finite=uninstrumented +fun:__letf2=uninstrumented +fun:__lgamma_r_finite=uninstrumented +fun:__lgammaf_r_finite=uninstrumented +fun:__lgammal_r_finite=uninstrumented +fun:__libc_accept=uninstrumented +fun:__libc_alloca_cutoff=uninstrumented +fun:__libc_allocate_rtsig=uninstrumented +fun:__libc_allocate_rtsig_private=uninstrumented +fun:__libc_calloc=uninstrumented +fun:__libc_clntudp_bufcreate=uninstrumented +fun:__libc_close=uninstrumented +fun:__libc_connect=uninstrumented +fun:__libc_csu_fini=uninstrumented +fun:__libc_csu_init=uninstrumented +fun:__libc_current_sigrtmax=uninstrumented +fun:__libc_current_sigrtmax_private=uninstrumented +fun:__libc_current_sigrtmin=uninstrumented +fun:__libc_current_sigrtmin_private=uninstrumented +fun:__libc_dl_error_tsd=uninstrumented +fun:__libc_dlclose=uninstrumented +fun:__libc_dlopen_mode=uninstrumented +fun:__libc_dlsym=uninstrumented +fun:__libc_fatal=uninstrumented +fun:__libc_fcntl=uninstrumented +fun:__libc_fork=uninstrumented +fun:__libc_free=uninstrumented +fun:__libc_freeres=uninstrumented +fun:__libc_fsync=uninstrumented +fun:__libc_init_first=uninstrumented +fun:__libc_longjmp=uninstrumented +fun:__libc_lseek=uninstrumented +fun:__libc_lseek64=uninstrumented +fun:__libc_mallinfo=uninstrumented +fun:__libc_malloc=uninstrumented +fun:__libc_mallopt=uninstrumented +fun:__libc_memalign=uninstrumented +fun:__libc_msync=uninstrumented +fun:__libc_nanosleep=uninstrumented +fun:__libc_open=uninstrumented +fun:__libc_pause=uninstrumented +fun:__libc_pread=uninstrumented +fun:__libc_pread64=uninstrumented +fun:__libc_pthread_init=uninstrumented +fun:__libc_pvalloc=uninstrumented +fun:__libc_pwrite=uninstrumented +fun:__libc_pwrite64=uninstrumented +fun:__libc_read=uninstrumented +fun:__libc_realloc=uninstrumented +fun:__libc_recv=uninstrumented +fun:__libc_recvfrom=uninstrumented +fun:__libc_recvmsg=uninstrumented +fun:__libc_res_nquery=uninstrumented +fun:__libc_res_nsearch=uninstrumented +fun:__libc_rpc_getport=uninstrumented +fun:__libc_sa_len=uninstrumented +fun:__libc_send=uninstrumented +fun:__libc_sendmsg=uninstrumented +fun:__libc_sendto=uninstrumented +fun:__libc_sigaction=uninstrumented +fun:__libc_siglongjmp=uninstrumented +fun:__libc_sigsuspend=uninstrumented +fun:__libc_sigwait=uninstrumented +fun:__libc_start_main=uninstrumented +fun:__libc_system=uninstrumented +fun:__libc_tcdrain=uninstrumented +fun:__libc_thread_freeres=uninstrumented +fun:__libc_valloc=uninstrumented +fun:__libc_wait=uninstrumented +fun:__libc_waitpid=uninstrumented +fun:__libc_write=uninstrumented +fun:__lll_lock_wait=uninstrumented +fun:__lll_lock_wait_private=uninstrumented +fun:__lll_robust_lock_wait=uninstrumented +fun:__lll_robust_timedlock_wait=uninstrumented +fun:__lll_timedlock_wait=uninstrumented +fun:__lll_timedwait_tid=uninstrumented +fun:__lll_unlock_wake=uninstrumented +fun:__lll_unlock_wake_private=uninstrumented +fun:__llseek=uninstrumented +fun:__loc_aton=uninstrumented +fun:__loc_ntoa=uninstrumented +fun:__log10_finite=uninstrumented +fun:__log10f_finite=uninstrumented +fun:__log10l_finite=uninstrumented +fun:__log2_finite=uninstrumented +fun:__log2f_finite=uninstrumented +fun:__log2l_finite=uninstrumented +fun:__log_finite=uninstrumented +fun:__logf_finite=uninstrumented +fun:__logl_finite=uninstrumented +fun:__longjmp_chk=uninstrumented +fun:__lseek=uninstrumented +fun:__lseek64=uninstrumented +fun:__lseek_nocancel=uninstrumented +fun:__lshrti3=uninstrumented +fun:__lstat=uninstrumented +fun:__lttf2=uninstrumented +fun:__lxstat=uninstrumented +fun:__lxstat64=uninstrumented +fun:__make_stacks_executable=uninstrumented +fun:__mbrlen=uninstrumented +fun:__mbrtowc=uninstrumented +fun:__mbsnrtowcs_chk=uninstrumented +fun:__mbsrtowcs_chk=uninstrumented +fun:__mbstowcs_chk=uninstrumented +fun:__memcpy_chk=uninstrumented +fun:__memmove_chk=uninstrumented +fun:__mempcpy=uninstrumented +fun:__mempcpy_chk=uninstrumented +fun:__mempcpy_small=uninstrumented +fun:__memset_chk=uninstrumented +fun:__mknod=uninstrumented +fun:__modti3=uninstrumented +fun:__monstartup=uninstrumented +fun:__morestack=uninstrumented +fun:__morestack_allocate_stack_space=uninstrumented +fun:__morestack_block_signals=uninstrumented +fun:__morestack_fail=uninstrumented +fun:__morestack_large_model=uninstrumented +fun:__morestack_load_mmap=uninstrumented +fun:__morestack_non_split=uninstrumented +fun:__morestack_release_segments=uninstrumented +fun:__morestack_unblock_signals=uninstrumented +fun:__mq_open_2=uninstrumented +fun:__msgrcv=uninstrumented +fun:__msgrcv_nocancel=uninstrumented +fun:__msgsnd=uninstrumented +fun:__msgsnd_nocancel=uninstrumented +fun:__msync_nocancel=uninstrumented +fun:__muldc3=uninstrumented +fun:__mulsc3=uninstrumented +fun:__multc3=uninstrumented +fun:__multf3=uninstrumented +fun:__multi3=uninstrumented +fun:__mulvdi3=uninstrumented +fun:__mulvsi3=uninstrumented +fun:__mulvti3=uninstrumented +fun:__mulxc3=uninstrumented +fun:__nanosleep=uninstrumented +fun:__nanosleep_nocancel=uninstrumented +fun:__negtf2=uninstrumented +fun:__negti2=uninstrumented +fun:__negvdi2=uninstrumented +fun:__negvsi2=uninstrumented +fun:__negvti2=uninstrumented +fun:__netf2=uninstrumented +fun:__new_sem_destroy=uninstrumented +fun:__new_sem_getvalue=uninstrumented +fun:__new_sem_init=uninstrumented +fun:__newlocale=uninstrumented +fun:__nis_default_access=uninstrumented +fun:__nis_default_group=uninstrumented +fun:__nis_default_owner=uninstrumented +fun:__nis_default_ttl=uninstrumented +fun:__nis_finddirectory=uninstrumented +fun:__nis_hash=uninstrumented +fun:__nisbind_connect=uninstrumented +fun:__nisbind_create=uninstrumented +fun:__nisbind_destroy=uninstrumented +fun:__nisbind_next=uninstrumented +fun:__nl_langinfo_l=uninstrumented +fun:__nptl_create_event=uninstrumented +fun:__nptl_deallocate_tsd=uninstrumented +fun:__nptl_death_event=uninstrumented +fun:__nptl_main=uninstrumented +fun:__nptl_set_robust=uninstrumented +fun:__nptl_setxid=uninstrumented +fun:__ns_get16=uninstrumented +fun:__ns_get32=uninstrumented +fun:__ns_name_ntop=uninstrumented +fun:__ns_name_unpack=uninstrumented +fun:__nss_configure_lookup=uninstrumented +fun:__nss_database_lookup=uninstrumented +fun:__nss_disable_nscd=uninstrumented +fun:__nss_group_lookup=uninstrumented +fun:__nss_group_lookup2=uninstrumented +fun:__nss_hostname_digits_dots=uninstrumented +fun:__nss_hosts_lookup=uninstrumented +fun:__nss_hosts_lookup2=uninstrumented +fun:__nss_lookup=uninstrumented +fun:__nss_lookup_function=uninstrumented +fun:__nss_next=uninstrumented +fun:__nss_next2=uninstrumented +fun:__nss_passwd_lookup=uninstrumented +fun:__nss_passwd_lookup2=uninstrumented +fun:__nss_services_lookup2=uninstrumented +fun:__obstack_printf_chk=uninstrumented +fun:__obstack_vprintf_chk=uninstrumented +fun:__open=uninstrumented +fun:__open64=uninstrumented +fun:__open64_2=uninstrumented +fun:__open_2=uninstrumented +fun:__open_catalog=uninstrumented +fun:__open_nocancel=uninstrumented +fun:__openat64_2=uninstrumented +fun:__openat_2=uninstrumented +fun:__overflow=uninstrumented +fun:__p_cdname=uninstrumented +fun:__p_cdnname=uninstrumented +fun:__p_class=uninstrumented +fun:__p_fqname=uninstrumented +fun:__p_fqnname=uninstrumented +fun:__p_option=uninstrumented +fun:__p_query=uninstrumented +fun:__p_rcode=uninstrumented +fun:__p_secstodate=uninstrumented +fun:__p_time=uninstrumented +fun:__p_type=uninstrumented +fun:__paritydi2=uninstrumented +fun:__parityti2=uninstrumented +fun:__pause_nocancel=uninstrumented +fun:__pipe=uninstrumented +fun:__poll=uninstrumented +fun:__popcountdi2=uninstrumented +fun:__popcountti2=uninstrumented +fun:__posix_getopt=uninstrumented +fun:__pow_finite=uninstrumented +fun:__powf_finite=uninstrumented +fun:__powidf2=uninstrumented +fun:__powisf2=uninstrumented +fun:__powitf2=uninstrumented +fun:__powixf2=uninstrumented +fun:__powl_finite=uninstrumented +fun:__pread=uninstrumented +fun:__pread64=uninstrumented +fun:__pread64_chk=uninstrumented +fun:__pread_chk=uninstrumented +fun:__pread_nocancel=uninstrumented +fun:__prepare_niscall=uninstrumented +fun:__printf_chk=uninstrumented +fun:__printf_fp=uninstrumented +fun:__profile_frequency=uninstrumented +fun:__pthread_atfork=uninstrumented +fun:__pthread_attr_destroy=uninstrumented +fun:__pthread_attr_getaffinity_new=uninstrumented +fun:__pthread_attr_getaffinity_old=uninstrumented +fun:__pthread_attr_getdetachstate=uninstrumented +fun:__pthread_attr_getinheritsched=uninstrumented +fun:__pthread_attr_getschedparam=uninstrumented +fun:__pthread_attr_getschedpolicy=uninstrumented +fun:__pthread_attr_getscope=uninstrumented +fun:__pthread_attr_getstack=uninstrumented +fun:__pthread_attr_getstackaddr=uninstrumented +fun:__pthread_attr_getstacksize=uninstrumented +fun:__pthread_attr_init_2_1=uninstrumented +fun:__pthread_attr_setaffinity_new=uninstrumented +fun:__pthread_attr_setaffinity_old=uninstrumented +fun:__pthread_attr_setdetachstate=uninstrumented +fun:__pthread_attr_setinheritsched=uninstrumented +fun:__pthread_attr_setschedparam=uninstrumented +fun:__pthread_attr_setschedpolicy=uninstrumented +fun:__pthread_attr_setscope=uninstrumented +fun:__pthread_attr_setstack=uninstrumented +fun:__pthread_attr_setstackaddr=uninstrumented +fun:__pthread_attr_setstacksize=uninstrumented +fun:__pthread_cleanup_pop=uninstrumented +fun:__pthread_cleanup_pop_restore=uninstrumented +fun:__pthread_cleanup_push=uninstrumented +fun:__pthread_cleanup_push_defer=uninstrumented +fun:__pthread_cleanup_routine=uninstrumented +fun:__pthread_cleanup_upto=uninstrumented +fun:__pthread_clock_gettime=uninstrumented +fun:__pthread_clock_settime=uninstrumented +fun:__pthread_cond_broadcast=uninstrumented +fun:__pthread_cond_broadcast_2_0=uninstrumented +fun:__pthread_cond_destroy=uninstrumented +fun:__pthread_cond_destroy_2_0=uninstrumented +fun:__pthread_cond_init=uninstrumented +fun:__pthread_cond_init_2_0=uninstrumented +fun:__pthread_cond_signal=uninstrumented +fun:__pthread_cond_signal_2_0=uninstrumented +fun:__pthread_cond_timedwait=uninstrumented +fun:__pthread_cond_timedwait_2_0=uninstrumented +fun:__pthread_cond_wait=uninstrumented +fun:__pthread_cond_wait_2_0=uninstrumented +fun:__pthread_condattr_destroy=uninstrumented +fun:__pthread_condattr_init=uninstrumented +fun:__pthread_create_2_1=uninstrumented +fun:__pthread_current_priority=uninstrumented +fun:__pthread_disable_asynccancel=uninstrumented +fun:__pthread_enable_asynccancel=uninstrumented +fun:__pthread_equal=uninstrumented +fun:__pthread_exit=uninstrumented +fun:__pthread_get_minstack=uninstrumented +fun:__pthread_getaffinity_new=uninstrumented +fun:__pthread_getaffinity_np=uninstrumented +fun:__pthread_getaffinity_old=uninstrumented +fun:__pthread_getschedparam=uninstrumented +fun:__pthread_getspecific=uninstrumented +fun:__pthread_getspecific_internal=uninstrumented +fun:__pthread_init_static_tls=uninstrumented +fun:__pthread_initialize_minimal=uninstrumented +fun:__pthread_initialize_minimal_internal=uninstrumented +fun:__pthread_key_create=uninstrumented +fun:__pthread_key_create_internal=uninstrumented +fun:__pthread_kill=uninstrumented +fun:__pthread_kill_other_threads_np=uninstrumented +fun:__pthread_mutex_cond_lock=uninstrumented +fun:__pthread_mutex_cond_lock_adjust=uninstrumented +fun:__pthread_mutex_cond_lock_full=uninstrumented +fun:__pthread_mutex_destroy=uninstrumented +fun:__pthread_mutex_destroy_internal=uninstrumented +fun:__pthread_mutex_init=uninstrumented +fun:__pthread_mutex_init_internal=uninstrumented +fun:__pthread_mutex_lock=uninstrumented +fun:__pthread_mutex_lock_full=uninstrumented +fun:__pthread_mutex_lock_internal=uninstrumented +fun:__pthread_mutex_trylock=uninstrumented +fun:__pthread_mutex_unlock=uninstrumented +fun:__pthread_mutex_unlock_full=uninstrumented +fun:__pthread_mutex_unlock_internal=uninstrumented +fun:__pthread_mutex_unlock_usercnt=uninstrumented +fun:__pthread_mutexattr_destroy=uninstrumented +fun:__pthread_mutexattr_init=uninstrumented +fun:__pthread_mutexattr_settype=uninstrumented +fun:__pthread_once=uninstrumented +fun:__pthread_once_internal=uninstrumented +fun:__pthread_register_cancel=uninstrumented +fun:__pthread_register_cancel_defer=uninstrumented +fun:__pthread_rwlock_destroy=uninstrumented +fun:__pthread_rwlock_init=uninstrumented +fun:__pthread_rwlock_rdlock=uninstrumented +fun:__pthread_rwlock_rdlock_internal=uninstrumented +fun:__pthread_rwlock_tryrdlock=uninstrumented +fun:__pthread_rwlock_trywrlock=uninstrumented +fun:__pthread_rwlock_unlock=uninstrumented +fun:__pthread_rwlock_unlock_internal=uninstrumented +fun:__pthread_rwlock_wrlock=uninstrumented +fun:__pthread_rwlock_wrlock_internal=uninstrumented +fun:__pthread_self=uninstrumented +fun:__pthread_setaffinity_new=uninstrumented +fun:__pthread_setaffinity_old=uninstrumented +fun:__pthread_setcancelstate=uninstrumented +fun:__pthread_setcanceltype=uninstrumented +fun:__pthread_setschedparam=uninstrumented +fun:__pthread_setspecific=uninstrumented +fun:__pthread_setspecific_internal=uninstrumented +fun:__pthread_tpp_change_priority=uninstrumented +fun:__pthread_unregister_cancel=uninstrumented +fun:__pthread_unregister_cancel_restore=uninstrumented +fun:__pthread_unwind=uninstrumented +fun:__pthread_unwind_next=uninstrumented +fun:__ptsname_r_chk=uninstrumented +fun:__putlong=uninstrumented +fun:__putshort=uninstrumented +fun:__pwrite=uninstrumented +fun:__pwrite64=uninstrumented +fun:__pwrite_nocancel=uninstrumented +fun:__rawmemchr=uninstrumented +fun:__read=uninstrumented +fun:__read_chk=uninstrumented +fun:__read_nocancel=uninstrumented +fun:__readlink_chk=uninstrumented +fun:__readlinkat_chk=uninstrumented +fun:__realpath_chk=uninstrumented +fun:__reclaim_stacks=uninstrumented +fun:__recv=uninstrumented +fun:__recv_chk=uninstrumented +fun:__recvfrom=uninstrumented +fun:__recvfrom_chk=uninstrumented +fun:__recvfrom_nocancel=uninstrumented +fun:__recvmsg=uninstrumented +fun:__recvmsg_nocancel=uninstrumented +fun:__register_atfork=uninstrumented +fun:__register_frame=uninstrumented +fun:__register_frame_info=uninstrumented +fun:__register_frame_info_bases=uninstrumented +fun:__register_frame_info_table=uninstrumented +fun:__register_frame_info_table_bases=uninstrumented +fun:__register_frame_table=uninstrumented +fun:__remainder_finite=uninstrumented +fun:__remainderf_finite=uninstrumented +fun:__remainderl_finite=uninstrumented +fun:__res_close=uninstrumented +fun:__res_dnok=uninstrumented +fun:__res_hnok=uninstrumented +fun:__res_hostalias=uninstrumented +fun:__res_iclose=uninstrumented +fun:__res_init=uninstrumented +fun:__res_isourserver=uninstrumented +fun:__res_mailok=uninstrumented +fun:__res_maybe_init=uninstrumented +fun:__res_mkquery=uninstrumented +fun:__res_nameinquery=uninstrumented +fun:__res_nclose=uninstrumented +fun:__res_ninit=uninstrumented +fun:__res_nmkquery=uninstrumented +fun:__res_nquery=uninstrumented +fun:__res_nquerydomain=uninstrumented +fun:__res_nsearch=uninstrumented +fun:__res_nsend=uninstrumented +fun:__res_ownok=uninstrumented +fun:__res_queriesmatch=uninstrumented +fun:__res_query=uninstrumented +fun:__res_querydomain=uninstrumented +fun:__res_randomid=uninstrumented +fun:__res_search=uninstrumented +fun:__res_send=uninstrumented +fun:__res_state=uninstrumented +fun:__restore_rt=uninstrumented +fun:__rpc_thread_createerr=uninstrumented +fun:__rpc_thread_svc_fdset=uninstrumented +fun:__rpc_thread_svc_max_pollfd=uninstrumented +fun:__rpc_thread_svc_pollfd=uninstrumented +fun:__sbrk=uninstrumented +fun:__scalb_finite=uninstrumented +fun:__scalbf_finite=uninstrumented +fun:__scalbl_finite=uninstrumented +fun:__sched_cpualloc=uninstrumented +fun:__sched_cpucount=uninstrumented +fun:__sched_cpufree=uninstrumented +fun:__sched_get_priority_max=uninstrumented +fun:__sched_get_priority_min=uninstrumented +fun:__sched_getparam=uninstrumented +fun:__sched_getscheduler=uninstrumented +fun:__sched_setscheduler=uninstrumented +fun:__sched_yield=uninstrumented +fun:__secure_getenv=uninstrumented +fun:__select=uninstrumented +fun:__sem_search=uninstrumented +fun:__send=uninstrumented +fun:__sendmsg=uninstrumented +fun:__sendmsg_nocancel=uninstrumented +fun:__sendto=uninstrumented +fun:__sendto_nocancel=uninstrumented +fun:__setmntent=uninstrumented +fun:__setpgid=uninstrumented +fun:__sigaction=uninstrumented +fun:__sigaddset=uninstrumented +fun:__sigdelset=uninstrumented +fun:__sigismember=uninstrumented +fun:__signbit=uninstrumented +fun:__signbitf=uninstrumented +fun:__signbitl=uninstrumented +fun:__sigpause=uninstrumented +fun:__sigsetjmp=uninstrumented +fun:__sigsuspend=uninstrumented +fun:__sigsuspend_nocancel=uninstrumented +fun:__sigwait=uninstrumented +fun:__sinh_finite=uninstrumented +fun:__sinhf_finite=uninstrumented +fun:__sinhl_finite=uninstrumented +fun:__snprintf_chk=uninstrumented +fun:__splitstack_find=uninstrumented +fun:__sprintf_chk=uninstrumented +fun:__sqrt_finite=uninstrumented +fun:__sqrtf_finite=uninstrumented +fun:__sqrtl_finite=uninstrumented +fun:__stack_chk_fail=uninstrumented +fun:__stack_chk_fail_local=uninstrumented +fun:__stack_split_initialize=uninstrumented +fun:__stat=uninstrumented +fun:__statfs=uninstrumented +fun:__stpcpy=uninstrumented +fun:__stpcpy_chk=uninstrumented +fun:__stpcpy_small=uninstrumented +fun:__stpncpy=uninstrumented +fun:__stpncpy_chk=uninstrumented +fun:__strcasecmp=uninstrumented +fun:__strcasecmp_l=uninstrumented +fun:__strcasestr=uninstrumented +fun:__strcat_chk=uninstrumented +fun:__strcoll_l=uninstrumented +fun:__strcpy_chk=uninstrumented +fun:__strcpy_small=uninstrumented +fun:__strcspn_c1=uninstrumented +fun:__strcspn_c2=uninstrumented +fun:__strcspn_c3=uninstrumented +fun:__strdup=uninstrumented +fun:__strerror_r=uninstrumented +fun:__strfmon_l=uninstrumented +fun:__strftime_l=uninstrumented +fun:__strncasecmp_l=uninstrumented +fun:__strncat_chk=uninstrumented +fun:__strncpy_chk=uninstrumented +fun:__strndup=uninstrumented +fun:__strpbrk_c2=uninstrumented +fun:__strpbrk_c3=uninstrumented +fun:__strsep_1c=uninstrumented +fun:__strsep_2c=uninstrumented +fun:__strsep_3c=uninstrumented +fun:__strsep_g=uninstrumented +fun:__strspn_c1=uninstrumented +fun:__strspn_c2=uninstrumented +fun:__strspn_c3=uninstrumented +fun:__strtod_internal=uninstrumented +fun:__strtod_l=uninstrumented +fun:__strtof_internal=uninstrumented +fun:__strtof_l=uninstrumented +fun:__strtok_r=uninstrumented +fun:__strtok_r_1c=uninstrumented +fun:__strtol_internal=uninstrumented +fun:__strtol_l=uninstrumented +fun:__strtold_internal=uninstrumented +fun:__strtold_l=uninstrumented +fun:__strtoll_internal=uninstrumented +fun:__strtoll_l=uninstrumented +fun:__strtoul_internal=uninstrumented +fun:__strtoul_l=uninstrumented +fun:__strtoull_internal=uninstrumented +fun:__strtoull_l=uninstrumented +fun:__strverscmp=uninstrumented +fun:__strxfrm_l=uninstrumented +fun:__subtf3=uninstrumented +fun:__subvdi3=uninstrumented +fun:__subvsi3=uninstrumented +fun:__subvti3=uninstrumented +fun:__swprintf_chk=uninstrumented +fun:__sym_ntop=uninstrumented +fun:__sym_ntos=uninstrumented +fun:__sym_ston=uninstrumented +fun:__sysconf=uninstrumented +fun:__sysctl=uninstrumented +fun:__syslog_chk=uninstrumented +fun:__sysv_signal=uninstrumented +fun:__tls_get_addr=uninstrumented +fun:__toascii_l=uninstrumented +fun:__tolower_l=uninstrumented +fun:__toupper_l=uninstrumented +fun:__towctrans=uninstrumented +fun:__towctrans_l=uninstrumented +fun:__towlower_l=uninstrumented +fun:__towupper_l=uninstrumented +fun:__trunctfdf2=uninstrumented +fun:__trunctfsf2=uninstrumented +fun:__trunctfxf2=uninstrumented +fun:__ttyname_r_chk=uninstrumented +fun:__ucmpti2=uninstrumented +fun:__udiv_w_sdiv=uninstrumented +fun:__udivmodti4=uninstrumented +fun:__udivti3=uninstrumented +fun:__uflow=uninstrumented +fun:__umodti3=uninstrumented +fun:__underflow=uninstrumented +fun:__unordtf2=uninstrumented +fun:__unwind_freeres=uninstrumented +fun:__uselocale=uninstrumented +fun:__vasprintf_chk=uninstrumented +fun:__vdprintf_chk=uninstrumented +fun:__vfork=uninstrumented +fun:__vfprintf_chk=uninstrumented +fun:__vfscanf=uninstrumented +fun:__vfwprintf_chk=uninstrumented +fun:__vprintf_chk=uninstrumented +fun:__vsnprintf=uninstrumented +fun:__vsnprintf_chk=uninstrumented +fun:__vsprintf_chk=uninstrumented +fun:__vsscanf=uninstrumented +fun:__vswprintf_chk=uninstrumented +fun:__vsyslog_chk=uninstrumented +fun:__vwprintf_chk=uninstrumented +fun:__wait=uninstrumented +fun:__wait_lookup_done=uninstrumented +fun:__waitpid=uninstrumented +fun:__warn_memset_zero_len=uninstrumented +fun:__wcpcpy_chk=uninstrumented +fun:__wcpncpy_chk=uninstrumented +fun:__wcrtomb_chk=uninstrumented +fun:__wcscasecmp_l=uninstrumented +fun:__wcscat_chk=uninstrumented +fun:__wcscoll_l=uninstrumented +fun:__wcscpy_chk=uninstrumented +fun:__wcsftime_l=uninstrumented +fun:__wcsncasecmp_l=uninstrumented +fun:__wcsncat_chk=uninstrumented +fun:__wcsncpy_chk=uninstrumented +fun:__wcsnrtombs_chk=uninstrumented +fun:__wcsrtombs_chk=uninstrumented +fun:__wcstod_internal=uninstrumented +fun:__wcstod_l=uninstrumented +fun:__wcstof_internal=uninstrumented +fun:__wcstof_l=uninstrumented +fun:__wcstol_internal=uninstrumented +fun:__wcstol_l=uninstrumented +fun:__wcstold_internal=uninstrumented +fun:__wcstold_l=uninstrumented +fun:__wcstoll_internal=uninstrumented +fun:__wcstoll_l=uninstrumented +fun:__wcstombs_chk=uninstrumented +fun:__wcstoul_internal=uninstrumented +fun:__wcstoul_l=uninstrumented +fun:__wcstoull_internal=uninstrumented +fun:__wcstoull_l=uninstrumented +fun:__wcsxfrm_l=uninstrumented +fun:__wctomb_chk=uninstrumented +fun:__wctrans_l=uninstrumented +fun:__wctype_l=uninstrumented +fun:__where_is_shmfs=uninstrumented +fun:__wmemcpy_chk=uninstrumented +fun:__wmemmove_chk=uninstrumented +fun:__wmempcpy_chk=uninstrumented +fun:__wmemset_chk=uninstrumented +fun:__woverflow=uninstrumented +fun:__wprintf_chk=uninstrumented +fun:__wrap_pthread_create=uninstrumented +fun:__write=uninstrumented +fun:__write_nocancel=uninstrumented +fun:__wuflow=uninstrumented +fun:__wunderflow=uninstrumented +fun:__xmknod=uninstrumented +fun:__xmknodat=uninstrumented +fun:__xpg_basename=uninstrumented +fun:__xpg_sigpause=uninstrumented +fun:__xpg_strerror_r=uninstrumented +fun:__xstat=uninstrumented +fun:__xstat64=uninstrumented +fun:__y0_finite=uninstrumented +fun:__y0f_finite=uninstrumented +fun:__y0l_finite=uninstrumented +fun:__y1_finite=uninstrumented +fun:__y1f_finite=uninstrumented +fun:__y1l_finite=uninstrumented +fun:__yn_finite=uninstrumented +fun:__ynf_finite=uninstrumented +fun:__ynl_finite=uninstrumented +fun:__yp_check=uninstrumented +fun:_authenticate=uninstrumented +fun:_dl_addr=uninstrumented +fun:_dl_allocate_tls=uninstrumented +fun:_dl_allocate_tls_init=uninstrumented +fun:_dl_deallocate_tls=uninstrumented +fun:_dl_debug_state=uninstrumented +fun:_dl_get_tls_static_info=uninstrumented +fun:_dl_make_stack_executable=uninstrumented +fun:_dl_mcount=uninstrumented +fun:_dl_mcount_wrapper=uninstrumented +fun:_dl_mcount_wrapper_check=uninstrumented +fun:_dl_rtld_di_serinfo=uninstrumented +fun:_dl_sym=uninstrumented +fun:_dl_tls_setup=uninstrumented +fun:_dl_vsym=uninstrumented +fun:_exit=uninstrumented +fun:_fini=uninstrumented +fun:_flushlbf=uninstrumented +fun:_gethtbyaddr=uninstrumented +fun:_gethtbyname=uninstrumented +fun:_gethtbyname2=uninstrumented +fun:_gethtent=uninstrumented +fun:_getlong=uninstrumented +fun:_getshort=uninstrumented +fun:_init=uninstrumented +fun:_longjmp=uninstrumented +fun:_mcleanup=uninstrumented +fun:_mcount=uninstrumented +fun:_nsl_default_nss=uninstrumented +fun:_nss_files_parse_grent=uninstrumented +fun:_nss_files_parse_pwent=uninstrumented +fun:_nss_files_parse_sgent=uninstrumented +fun:_nss_files_parse_spent=uninstrumented +fun:_obstack_allocated_p=uninstrumented +fun:_obstack_begin=uninstrumented +fun:_obstack_begin_1=uninstrumented +fun:_obstack_free=uninstrumented +fun:_obstack_memory_used=uninstrumented +fun:_obstack_newchunk=uninstrumented +fun:_pthread_cleanup_pop=uninstrumented +fun:_pthread_cleanup_pop_restore=uninstrumented +fun:_pthread_cleanup_push=uninstrumented +fun:_pthread_cleanup_push_defer=uninstrumented +fun:_rpc_dtablesize=uninstrumented +fun:_seterr_reply=uninstrumented +fun:_sethtent=uninstrumented +fun:_setjmp=uninstrumented +fun:_tolower=uninstrumented +fun:_toupper=uninstrumented +fun:_xdr_ib_request=uninstrumented +fun:_xdr_nis_result=uninstrumented +fun:a64l=uninstrumented +fun:abort=uninstrumented +fun:abs=uninstrumented +fun:accept=uninstrumented +fun:accept4=uninstrumented +fun:access=uninstrumented +fun:acct=uninstrumented +fun:acos=uninstrumented +fun:acosf=uninstrumented +fun:acosh=uninstrumented +fun:acoshf=uninstrumented +fun:acoshl=uninstrumented +fun:acosl=uninstrumented +fun:add_and_round.constprop.0=uninstrumented +fun:addmntent=uninstrumented +fun:addseverity=uninstrumented +fun:adjtime=uninstrumented +fun:adjtimex=uninstrumented +fun:advance=uninstrumented +fun:aio_cancel=uninstrumented +fun:aio_cancel64=uninstrumented +fun:aio_error=uninstrumented +fun:aio_error64=uninstrumented +fun:aio_fsync=uninstrumented +fun:aio_fsync64=uninstrumented +fun:aio_init=uninstrumented +fun:aio_read=uninstrumented +fun:aio_read64=uninstrumented +fun:aio_return=uninstrumented +fun:aio_return64=uninstrumented +fun:aio_suspend=uninstrumented +fun:aio_suspend64=uninstrumented +fun:aio_write=uninstrumented +fun:aio_write64=uninstrumented +fun:alarm=uninstrumented +fun:alphasort=uninstrumented +fun:alphasort64=uninstrumented +fun:arch_prctl=uninstrumented +fun:argp_error=uninstrumented +fun:argp_failure=uninstrumented +fun:argp_help=uninstrumented +fun:argp_parse=uninstrumented +fun:argp_state_help=uninstrumented +fun:argp_usage=uninstrumented +fun:argz_add=uninstrumented +fun:argz_add_sep=uninstrumented +fun:argz_append=uninstrumented +fun:argz_count=uninstrumented +fun:argz_create=uninstrumented +fun:argz_create_sep=uninstrumented +fun:argz_delete=uninstrumented +fun:argz_extract=uninstrumented +fun:argz_insert=uninstrumented +fun:argz_next=uninstrumented +fun:argz_replace=uninstrumented +fun:argz_stringify=uninstrumented +fun:asctime=uninstrumented +fun:asctime_r=uninstrumented +fun:asin=uninstrumented +fun:asinf=uninstrumented +fun:asinh=uninstrumented +fun:asinhf=uninstrumented +fun:asinhl=uninstrumented +fun:asinl=uninstrumented +fun:asprintf=uninstrumented +fun:at_quick_exit=uninstrumented +fun:atan=uninstrumented +fun:atan2=uninstrumented +fun:atan2f=uninstrumented +fun:atan2l=uninstrumented +fun:atanf=uninstrumented +fun:atanh=uninstrumented +fun:atanhf=uninstrumented +fun:atanhl=uninstrumented +fun:atanl=uninstrumented +fun:atexit=uninstrumented +fun:atof=uninstrumented +fun:atoi=uninstrumented +fun:atol=uninstrumented +fun:atoll=uninstrumented +fun:authdes_create=uninstrumented +fun:authdes_getucred=uninstrumented +fun:authdes_pk_create=uninstrumented +fun:authnone_create=uninstrumented +fun:authunix_create=uninstrumented +fun:authunix_create_default=uninstrumented +fun:backtrace=uninstrumented +fun:backtrace_symbols=uninstrumented +fun:backtrace_symbols_fd=uninstrumented +fun:basename=uninstrumented +fun:bcmp=uninstrumented +fun:bcopy=uninstrumented +fun:bdflush=uninstrumented +fun:bid128_ext_fma=uninstrumented +fun:bind=uninstrumented +fun:bind_textdomain_codeset=uninstrumented +fun:bindresvport=uninstrumented +fun:bindtextdomain=uninstrumented +fun:brk=uninstrumented +fun:bsd_signal=uninstrumented +fun:bsearch=uninstrumented +fun:btowc=uninstrumented +fun:bzero=uninstrumented +fun:cabs=uninstrumented +fun:cabsf=uninstrumented +fun:cabsl=uninstrumented +fun:cacos=uninstrumented +fun:cacosf=uninstrumented +fun:cacosh=uninstrumented +fun:cacoshf=uninstrumented +fun:cacoshl=uninstrumented +fun:cacosl=uninstrumented +fun:calloc=uninstrumented +fun:callrpc=uninstrumented +fun:canonicalize_file_name=uninstrumented +fun:capget=uninstrumented +fun:capset=uninstrumented +fun:carg=uninstrumented +fun:cargf=uninstrumented +fun:cargl=uninstrumented +fun:casin=uninstrumented +fun:casinf=uninstrumented +fun:casinh=uninstrumented +fun:casinhf=uninstrumented +fun:casinhl=uninstrumented +fun:casinl=uninstrumented +fun:catan=uninstrumented +fun:catanf=uninstrumented +fun:catanh=uninstrumented +fun:catanhf=uninstrumented +fun:catanhl=uninstrumented +fun:catanl=uninstrumented +fun:catclose=uninstrumented +fun:catgets=uninstrumented +fun:catopen=uninstrumented +fun:cbc_crypt=uninstrumented +fun:cbrt=uninstrumented +fun:cbrtf=uninstrumented +fun:cbrtl=uninstrumented +fun:ccos=uninstrumented +fun:ccosf=uninstrumented +fun:ccosh=uninstrumented +fun:ccoshf=uninstrumented +fun:ccoshl=uninstrumented +fun:ccosl=uninstrumented +fun:ceil=uninstrumented +fun:ceilf=uninstrumented +fun:ceill=uninstrumented +fun:cexp=uninstrumented +fun:cexpf=uninstrumented +fun:cexpl=uninstrumented +fun:cfgetispeed=uninstrumented +fun:cfgetospeed=uninstrumented +fun:cfmakeraw=uninstrumented +fun:cfree=uninstrumented +fun:cfsetispeed=uninstrumented +fun:cfsetospeed=uninstrumented +fun:cfsetspeed=uninstrumented +fun:chdir=uninstrumented +fun:check_add_mapping=uninstrumented +fun:chflags=uninstrumented +fun:chmod=uninstrumented +fun:chown=uninstrumented +fun:chroot=uninstrumented +fun:cimag=uninstrumented +fun:cimagf=uninstrumented +fun:cimagl=uninstrumented +fun:cleanup=uninstrumented +fun:clear_once_control=uninstrumented +fun:clearenv=uninstrumented +fun:clearerr=uninstrumented +fun:clearerr_unlocked=uninstrumented +fun:clnt_broadcast=uninstrumented +fun:clnt_create=uninstrumented +fun:clnt_pcreateerror=uninstrumented +fun:clnt_perrno=uninstrumented +fun:clnt_perror=uninstrumented +fun:clnt_spcreateerror=uninstrumented +fun:clnt_sperrno=uninstrumented +fun:clnt_sperror=uninstrumented +fun:clntraw_create=uninstrumented +fun:clnttcp_create=uninstrumented +fun:clntudp_bufcreate=uninstrumented +fun:clntudp_create=uninstrumented +fun:clntunix_create=uninstrumented +fun:clock=uninstrumented +fun:clock_adjtime=uninstrumented +fun:clock_getcpuclockid=uninstrumented +fun:clock_getres=uninstrumented +fun:clock_gettime=uninstrumented +fun:clock_nanosleep=uninstrumented +fun:clock_settime=uninstrumented +fun:clog=uninstrumented +fun:clog10=uninstrumented +fun:clog10f=uninstrumented +fun:clog10l=uninstrumented +fun:clogf=uninstrumented +fun:clogl=uninstrumented +fun:clone=uninstrumented +fun:close=uninstrumented +fun:closedir=uninstrumented +fun:closelog=uninstrumented +fun:confstr=uninstrumented +fun:conj=uninstrumented +fun:conjf=uninstrumented +fun:conjl=uninstrumented +fun:connect=uninstrumented +fun:copysign=uninstrumented +fun:copysignf=uninstrumented +fun:copysignl=uninstrumented +fun:cos=uninstrumented +fun:cosf=uninstrumented +fun:cosh=uninstrumented +fun:coshf=uninstrumented +fun:coshl=uninstrumented +fun:cosl=uninstrumented +fun:cpow=uninstrumented +fun:cpowf=uninstrumented +fun:cpowl=uninstrumented +fun:cproj=uninstrumented +fun:cprojf=uninstrumented +fun:cprojl=uninstrumented +fun:creal=uninstrumented +fun:crealf=uninstrumented +fun:creall=uninstrumented +fun:creat=uninstrumented +fun:creat64=uninstrumented +fun:create_key=uninstrumented +fun:create_module=uninstrumented +fun:crypt=uninstrumented +fun:crypt_r=uninstrumented +fun:csin=uninstrumented +fun:csinf=uninstrumented +fun:csinh=uninstrumented +fun:csinhf=uninstrumented +fun:csinhl=uninstrumented +fun:csinl=uninstrumented +fun:csqrt=uninstrumented +fun:csqrtf=uninstrumented +fun:csqrtl=uninstrumented +fun:ctan=uninstrumented +fun:ctanf=uninstrumented +fun:ctanh=uninstrumented +fun:ctanhf=uninstrumented +fun:ctanhl=uninstrumented +fun:ctanl=uninstrumented +fun:ctermid=uninstrumented +fun:ctime=uninstrumented +fun:ctime_r=uninstrumented +fun:cuserid=uninstrumented +fun:daemon=uninstrumented +fun:dcgettext=uninstrumented +fun:dcngettext=uninstrumented +fun:delete_module=uninstrumented +fun:des_setparity=uninstrumented +fun:dgettext=uninstrumented +fun:difftime=uninstrumented +fun:dirfd=uninstrumented +fun:dirname=uninstrumented +fun:div=uninstrumented +fun:dl_iterate_phdr=uninstrumented +fun:dladdr=uninstrumented +fun:dladdr1=uninstrumented +fun:dlclose=uninstrumented +fun:dlerror=uninstrumented +fun:dlinfo=uninstrumented +fun:dlmopen=uninstrumented +fun:dlopen=uninstrumented +fun:dlsym=uninstrumented +fun:dlvsym=uninstrumented +fun:dngettext=uninstrumented +fun:do_clone.constprop.4=uninstrumented +fun:do_sigwait=uninstrumented +fun:dprintf=uninstrumented +fun:drand48=uninstrumented +fun:drand48_r=uninstrumented +fun:drem=uninstrumented +fun:dremf=uninstrumented +fun:dreml=uninstrumented +fun:dup=uninstrumented +fun:dup2=uninstrumented +fun:dup3=uninstrumented +fun:duplocale=uninstrumented +fun:dysize=uninstrumented +fun:eaccess=uninstrumented +fun:ecb_crypt=uninstrumented +fun:ecvt=uninstrumented +fun:ecvt_r=uninstrumented +fun:encrypt=uninstrumented +fun:encrypt_r=uninstrumented +fun:endaliasent=uninstrumented +fun:endfsent=uninstrumented +fun:endgrent=uninstrumented +fun:endhostent=uninstrumented +fun:endmntent=uninstrumented +fun:endnetent=uninstrumented +fun:endnetgrent=uninstrumented +fun:endprotoent=uninstrumented +fun:endpwent=uninstrumented +fun:endrpcent=uninstrumented +fun:endservent=uninstrumented +fun:endsgent=uninstrumented +fun:endspent=uninstrumented +fun:endttyent=uninstrumented +fun:endusershell=uninstrumented +fun:endutent=uninstrumented +fun:endutxent=uninstrumented +fun:envz_add=uninstrumented +fun:envz_entry=uninstrumented +fun:envz_get=uninstrumented +fun:envz_merge=uninstrumented +fun:envz_remove=uninstrumented +fun:envz_strip=uninstrumented +fun:epoll_create=uninstrumented +fun:epoll_create1=uninstrumented +fun:epoll_ctl=uninstrumented +fun:epoll_pwait=uninstrumented +fun:epoll_wait=uninstrumented +fun:erand48=uninstrumented +fun:erand48_r=uninstrumented +fun:erf=uninstrumented +fun:erfc=uninstrumented +fun:erfcf=uninstrumented +fun:erfcl=uninstrumented +fun:erff=uninstrumented +fun:erfl=uninstrumented +fun:err=uninstrumented +fun:error=uninstrumented +fun:error_at_line=uninstrumented +fun:errx=uninstrumented +fun:ether_aton=uninstrumented +fun:ether_aton_r=uninstrumented +fun:ether_hostton=uninstrumented +fun:ether_line=uninstrumented +fun:ether_ntoa=uninstrumented +fun:ether_ntoa_r=uninstrumented +fun:ether_ntohost=uninstrumented +fun:euidaccess=uninstrumented +fun:eventfd=uninstrumented +fun:eventfd_read=uninstrumented +fun:eventfd_write=uninstrumented +fun:execl=uninstrumented +fun:execle=uninstrumented +fun:execlp=uninstrumented +fun:execv=uninstrumented +fun:execve=uninstrumented +fun:execvp=uninstrumented +fun:execvpe=uninstrumented +fun:exit=uninstrumented +fun:exp=uninstrumented +fun:exp10=uninstrumented +fun:exp10f=uninstrumented +fun:exp10l=uninstrumented +fun:exp2=uninstrumented +fun:exp2f=uninstrumented +fun:exp2l=uninstrumented +fun:expf=uninstrumented +fun:expl=uninstrumented +fun:expm1=uninstrumented +fun:expm1f=uninstrumented +fun:expm1l=uninstrumented +fun:fabs=uninstrumented +fun:fabsf=uninstrumented +fun:fabsl=uninstrumented +fun:faccessat=uninstrumented +fun:fallocate=uninstrumented +fun:fallocate64=uninstrumented +fun:fanotify_init=uninstrumented +fun:fanotify_mark=uninstrumented +fun:fattach=uninstrumented +fun:fchdir=uninstrumented +fun:fchflags=uninstrumented +fun:fchmod=uninstrumented +fun:fchmodat=uninstrumented +fun:fchown=uninstrumented +fun:fchownat=uninstrumented +fun:fclose=uninstrumented +fun:fcloseall=uninstrumented +fun:fcntl=uninstrumented +fun:fcrypt=uninstrumented +fun:fcvt=uninstrumented +fun:fcvt_r=uninstrumented +fun:fdatasync=uninstrumented +fun:fdetach=uninstrumented +fun:fdim=uninstrumented +fun:fdimf=uninstrumented +fun:fdiml=uninstrumented +fun:fdopen=uninstrumented +fun:fdopendir=uninstrumented +fun:feclearexcept=uninstrumented +fun:fedisableexcept=uninstrumented +fun:feenableexcept=uninstrumented +fun:fegetenv=uninstrumented +fun:fegetexcept=uninstrumented +fun:fegetexceptflag=uninstrumented +fun:fegetround=uninstrumented +fun:feholdexcept=uninstrumented +fun:feof=uninstrumented +fun:feof_unlocked=uninstrumented +fun:feraiseexcept=uninstrumented +fun:ferror=uninstrumented +fun:ferror_unlocked=uninstrumented +fun:fesetenv=uninstrumented +fun:fesetexceptflag=uninstrumented +fun:fesetround=uninstrumented +fun:fetestexcept=uninstrumented +fun:feupdateenv=uninstrumented +fun:fexecve=uninstrumented +fun:fflush=uninstrumented +fun:fflush_unlocked=uninstrumented +fun:ffs=uninstrumented +fun:ffsl=uninstrumented +fun:ffsll=uninstrumented +fun:fgetc=uninstrumented +fun:fgetc_unlocked=uninstrumented +fun:fgetgrent=uninstrumented +fun:fgetgrent_r=uninstrumented +fun:fgetpos=uninstrumented +fun:fgetpos64=uninstrumented +fun:fgetpwent=uninstrumented +fun:fgetpwent_r=uninstrumented +fun:fgets=uninstrumented +fun:fgets_unlocked=uninstrumented +fun:fgetsgent=uninstrumented +fun:fgetsgent_r=uninstrumented +fun:fgetspent=uninstrumented +fun:fgetspent_r=uninstrumented +fun:fgetwc=uninstrumented +fun:fgetwc_unlocked=uninstrumented +fun:fgetws=uninstrumented +fun:fgetws_unlocked=uninstrumented +fun:fgetxattr=uninstrumented +fun:fileno=uninstrumented +fun:fileno_unlocked=uninstrumented +fun:finite=uninstrumented +fun:finitef=uninstrumented +fun:finitel=uninstrumented +fun:flistxattr=uninstrumented +fun:flock=uninstrumented +fun:flockfile=uninstrumented +fun:floor=uninstrumented +fun:floorf=uninstrumented +fun:floorl=uninstrumented +fun:fma=uninstrumented +fun:fmaf=uninstrumented +fun:fmal=uninstrumented +fun:fmax=uninstrumented +fun:fmaxf=uninstrumented +fun:fmaxl=uninstrumented +fun:fmemopen=uninstrumented +fun:fmin=uninstrumented +fun:fminf=uninstrumented +fun:fminl=uninstrumented +fun:fmod=uninstrumented +fun:fmodf=uninstrumented +fun:fmodl=uninstrumented +fun:fmtmsg=uninstrumented +fun:fnmatch=uninstrumented +fun:fopen=uninstrumented +fun:fopen64=uninstrumented +fun:fopencookie=uninstrumented +fun:fork=uninstrumented +fun:forkpty=uninstrumented +fun:fpathconf=uninstrumented +fun:fprintf=uninstrumented +fun:fputc=uninstrumented +fun:fputc_unlocked=uninstrumented +fun:fputs=uninstrumented +fun:fputs_unlocked=uninstrumented +fun:fputwc=uninstrumented +fun:fputwc_unlocked=uninstrumented +fun:fputws=uninstrumented +fun:fputws_unlocked=uninstrumented +fun:frame_dummy=uninstrumented +fun:fread=uninstrumented +fun:fread_unlocked=uninstrumented +fun:free=uninstrumented +fun:free_dynamic_blocks=uninstrumented +fun:free_segments=uninstrumented +fun:freeaddrinfo=uninstrumented +fun:freeifaddrs=uninstrumented +fun:freelocale=uninstrumented +fun:fremovexattr=uninstrumented +fun:freopen=uninstrumented +fun:freopen64=uninstrumented +fun:frexp=uninstrumented +fun:frexpf=uninstrumented +fun:frexpl=uninstrumented +fun:fscanf=uninstrumented +fun:fseek=uninstrumented +fun:fseeko=uninstrumented +fun:fseeko64=uninstrumented +fun:fsetpos=uninstrumented +fun:fsetpos64=uninstrumented +fun:fsetxattr=uninstrumented +fun:fstat=uninstrumented +fun:fstat64=uninstrumented +fun:fstatat=uninstrumented +fun:fstatat64=uninstrumented +fun:fstatfs=uninstrumented +fun:fstatfs64=uninstrumented +fun:fstatvfs=uninstrumented +fun:fstatvfs64=uninstrumented +fun:fsync=uninstrumented +fun:ftell=uninstrumented +fun:ftello=uninstrumented +fun:ftello64=uninstrumented +fun:ftime=uninstrumented +fun:ftok=uninstrumented +fun:ftruncate=uninstrumented +fun:ftruncate64=uninstrumented +fun:ftrylockfile=uninstrumented +fun:fts_children=uninstrumented +fun:fts_close=uninstrumented +fun:fts_open=uninstrumented +fun:fts_read=uninstrumented +fun:fts_set=uninstrumented +fun:ftw=uninstrumented +fun:ftw64=uninstrumented +fun:funlockfile=uninstrumented +fun:futimens=uninstrumented +fun:futimes=uninstrumented +fun:futimesat=uninstrumented +fun:fwide=uninstrumented +fun:fwprintf=uninstrumented +fun:fwrite=uninstrumented +fun:fwrite_unlocked=uninstrumented +fun:fwscanf=uninstrumented +fun:gai_cancel=uninstrumented +fun:gai_error=uninstrumented +fun:gai_strerror=uninstrumented +fun:gai_suspend=uninstrumented +fun:gamma=uninstrumented +fun:gammaf=uninstrumented +fun:gammal=uninstrumented +fun:gcvt=uninstrumented +fun:get_BID128.constprop.5=uninstrumented +fun:get_avphys_pages=uninstrumented +fun:get_current_dir_name=uninstrumented +fun:get_kernel_syms=uninstrumented +fun:get_myaddress=uninstrumented +fun:get_nprocs=uninstrumented +fun:get_nprocs_conf=uninstrumented +fun:get_phys_pages=uninstrumented +fun:getaddrinfo=uninstrumented +fun:getaddrinfo_a=uninstrumented +fun:getaliasbyname=uninstrumented +fun:getaliasbyname_r=uninstrumented +fun:getaliasent=uninstrumented +fun:getaliasent_r=uninstrumented +fun:getc=uninstrumented +fun:getc_unlocked=uninstrumented +fun:getchar=uninstrumented +fun:getchar_unlocked=uninstrumented +fun:getcontext=uninstrumented +fun:getcwd=uninstrumented +fun:getdate=uninstrumented +fun:getdate_r=uninstrumented +fun:getdelim=uninstrumented +fun:getdirentries=uninstrumented +fun:getdirentries64=uninstrumented +fun:getdomainname=uninstrumented +fun:getdtablesize=uninstrumented +fun:getegid=uninstrumented +fun:getenv=uninstrumented +fun:geteuid=uninstrumented +fun:getfsent=uninstrumented +fun:getfsfile=uninstrumented +fun:getfsspec=uninstrumented +fun:getgid=uninstrumented +fun:getgrent=uninstrumented +fun:getgrent_r=uninstrumented +fun:getgrgid=uninstrumented +fun:getgrgid_r=uninstrumented +fun:getgrnam=uninstrumented +fun:getgrnam_r=uninstrumented +fun:getgrouplist=uninstrumented +fun:getgroups=uninstrumented +fun:gethostbyaddr=uninstrumented +fun:gethostbyaddr_r=uninstrumented +fun:gethostbyname=uninstrumented +fun:gethostbyname2=uninstrumented +fun:gethostbyname2_r=uninstrumented +fun:gethostbyname_r=uninstrumented +fun:gethostent=uninstrumented +fun:gethostent_r=uninstrumented +fun:gethostid=uninstrumented +fun:gethostname=uninstrumented +fun:getifaddrs=uninstrumented +fun:getipv4sourcefilter=uninstrumented +fun:getitimer=uninstrumented +fun:getline=uninstrumented +fun:getloadavg=uninstrumented +fun:getlogin=uninstrumented +fun:getlogin_r=uninstrumented +fun:getmntent=uninstrumented +fun:getmntent_r=uninstrumented +fun:getmsg=uninstrumented +fun:getnameinfo=uninstrumented +fun:getnetbyaddr=uninstrumented +fun:getnetbyaddr_r=uninstrumented +fun:getnetbyname=uninstrumented +fun:getnetbyname_r=uninstrumented +fun:getnetent=uninstrumented +fun:getnetent_r=uninstrumented +fun:getnetgrent=uninstrumented +fun:getnetgrent_r=uninstrumented +fun:getnetname=uninstrumented +fun:getopt=uninstrumented +fun:getopt_long=uninstrumented +fun:getopt_long_only=uninstrumented +fun:getpagesize=uninstrumented +fun:getpass=uninstrumented +fun:getpeername=uninstrumented +fun:getpgid=uninstrumented +fun:getpgrp=uninstrumented +fun:getpid=uninstrumented +fun:getpmsg=uninstrumented +fun:getppid=uninstrumented +fun:getpriority=uninstrumented +fun:getprotobyname=uninstrumented +fun:getprotobyname_r=uninstrumented +fun:getprotobynumber=uninstrumented +fun:getprotobynumber_r=uninstrumented +fun:getprotoent=uninstrumented +fun:getprotoent_r=uninstrumented +fun:getpt=uninstrumented +fun:getpublickey=uninstrumented +fun:getpw=uninstrumented +fun:getpwent=uninstrumented +fun:getpwent_r=uninstrumented +fun:getpwnam=uninstrumented +fun:getpwnam_r=uninstrumented +fun:getpwuid=uninstrumented +fun:getpwuid_r=uninstrumented +fun:getresgid=uninstrumented +fun:getresuid=uninstrumented +fun:getrlimit=uninstrumented +fun:getrlimit64=uninstrumented +fun:getrpcbyname=uninstrumented +fun:getrpcbyname_r=uninstrumented +fun:getrpcbynumber=uninstrumented +fun:getrpcbynumber_r=uninstrumented +fun:getrpcent=uninstrumented +fun:getrpcent_r=uninstrumented +fun:getrpcport=uninstrumented +fun:getrusage=uninstrumented +fun:gets=uninstrumented +fun:getsecretkey=uninstrumented +fun:getservbyname=uninstrumented +fun:getservbyname_r=uninstrumented +fun:getservbyport=uninstrumented +fun:getservbyport_r=uninstrumented +fun:getservent=uninstrumented +fun:getservent_r=uninstrumented +fun:getsgent=uninstrumented +fun:getsgent_r=uninstrumented +fun:getsgnam=uninstrumented +fun:getsgnam_r=uninstrumented +fun:getsid=uninstrumented +fun:getsockname=uninstrumented +fun:getsockopt=uninstrumented +fun:getsourcefilter=uninstrumented +fun:getspent=uninstrumented +fun:getspent_r=uninstrumented +fun:getspnam=uninstrumented +fun:getspnam_r=uninstrumented +fun:getsubopt=uninstrumented +fun:gettext=uninstrumented +fun:gettimeofday=uninstrumented +fun:getttyent=uninstrumented +fun:getttynam=uninstrumented +fun:getuid=uninstrumented +fun:getusershell=uninstrumented +fun:getutent=uninstrumented +fun:getutent_r=uninstrumented +fun:getutid=uninstrumented +fun:getutid_r=uninstrumented +fun:getutline=uninstrumented +fun:getutline_r=uninstrumented +fun:getutmp=uninstrumented +fun:getutmpx=uninstrumented +fun:getutxent=uninstrumented +fun:getutxid=uninstrumented +fun:getutxline=uninstrumented +fun:getw=uninstrumented +fun:getwc=uninstrumented +fun:getwc_unlocked=uninstrumented +fun:getwchar=uninstrumented +fun:getwchar_unlocked=uninstrumented +fun:getwd=uninstrumented +fun:getxattr=uninstrumented +fun:glob=uninstrumented +fun:glob64=uninstrumented +fun:glob_pattern_p=uninstrumented +fun:globfree=uninstrumented +fun:globfree64=uninstrumented +fun:gmtime=uninstrumented +fun:gmtime_r=uninstrumented +fun:gnu_dev_major=uninstrumented +fun:gnu_dev_makedev=uninstrumented +fun:gnu_dev_minor=uninstrumented +fun:gnu_get_libc_release=uninstrumented +fun:gnu_get_libc_version=uninstrumented +fun:grantpt=uninstrumented +fun:group_member=uninstrumented +fun:gsignal=uninstrumented +fun:gtty=uninstrumented +fun:hasmntopt=uninstrumented +fun:hcreate=uninstrumented +fun:hcreate_r=uninstrumented +fun:hdestroy=uninstrumented +fun:hdestroy_r=uninstrumented +fun:herror=uninstrumented +fun:host2netname=uninstrumented +fun:hsearch=uninstrumented +fun:hsearch_r=uninstrumented +fun:hstrerror=uninstrumented +fun:htonl=uninstrumented +fun:htons=uninstrumented +fun:hypot=uninstrumented +fun:hypotf=uninstrumented +fun:hypotl=uninstrumented +fun:iconv=uninstrumented +fun:iconv_close=uninstrumented +fun:iconv_open=uninstrumented +fun:idna_to_ascii_lz=uninstrumented +fun:idna_to_unicode_lzlz=uninstrumented +fun:if_freenameindex=uninstrumented +fun:if_indextoname=uninstrumented +fun:if_nameindex=uninstrumented +fun:if_nametoindex=uninstrumented +fun:ilogb=uninstrumented +fun:ilogbf=uninstrumented +fun:ilogbl=uninstrumented +fun:imaxabs=uninstrumented +fun:imaxdiv=uninstrumented +fun:index=uninstrumented +fun:inet6_opt_append=uninstrumented +fun:inet6_opt_find=uninstrumented +fun:inet6_opt_finish=uninstrumented +fun:inet6_opt_get_val=uninstrumented +fun:inet6_opt_init=uninstrumented +fun:inet6_opt_next=uninstrumented +fun:inet6_opt_set_val=uninstrumented +fun:inet6_option_alloc=uninstrumented +fun:inet6_option_append=uninstrumented +fun:inet6_option_find=uninstrumented +fun:inet6_option_init=uninstrumented +fun:inet6_option_next=uninstrumented +fun:inet6_option_space=uninstrumented +fun:inet6_rth_add=uninstrumented +fun:inet6_rth_getaddr=uninstrumented +fun:inet6_rth_init=uninstrumented +fun:inet6_rth_reverse=uninstrumented +fun:inet6_rth_segments=uninstrumented +fun:inet6_rth_space=uninstrumented +fun:inet_addr=uninstrumented +fun:inet_aton=uninstrumented +fun:inet_lnaof=uninstrumented +fun:inet_makeaddr=uninstrumented +fun:inet_net_ntop=uninstrumented +fun:inet_net_pton=uninstrumented +fun:inet_neta=uninstrumented +fun:inet_netof=uninstrumented +fun:inet_network=uninstrumented +fun:inet_nsap_addr=uninstrumented +fun:inet_nsap_ntoa=uninstrumented +fun:inet_ntoa=uninstrumented +fun:inet_ntop=uninstrumented +fun:inet_pton=uninstrumented +fun:init_module=uninstrumented +fun:initgroups=uninstrumented +fun:initstate=uninstrumented +fun:initstate_r=uninstrumented +fun:innetgr=uninstrumented +fun:inotify_add_watch=uninstrumented +fun:inotify_init=uninstrumented +fun:inotify_init1=uninstrumented +fun:inotify_rm_watch=uninstrumented +fun:insque=uninstrumented +fun:ioctl=uninstrumented +fun:ioperm=uninstrumented +fun:iopl=uninstrumented +fun:iruserok=uninstrumented +fun:iruserok_af=uninstrumented +fun:isalnum=uninstrumented +fun:isalnum_l=uninstrumented +fun:isalpha=uninstrumented +fun:isalpha_l=uninstrumented +fun:isascii=uninstrumented +fun:isastream=uninstrumented +fun:isatty=uninstrumented +fun:isblank=uninstrumented +fun:isblank_l=uninstrumented +fun:iscntrl=uninstrumented +fun:iscntrl_l=uninstrumented +fun:isctype=uninstrumented +fun:isdigit=uninstrumented +fun:isdigit_l=uninstrumented +fun:isfdtype=uninstrumented +fun:isgraph=uninstrumented +fun:isgraph_l=uninstrumented +fun:isinf=uninstrumented +fun:isinfd128=uninstrumented +fun:isinfd32=uninstrumented +fun:isinfd64=uninstrumented +fun:isinff=uninstrumented +fun:isinfl=uninstrumented +fun:islower=uninstrumented +fun:islower_l=uninstrumented +fun:isnan=uninstrumented +fun:isnanf=uninstrumented +fun:isnanl=uninstrumented +fun:isprint=uninstrumented +fun:isprint_l=uninstrumented +fun:ispunct=uninstrumented +fun:ispunct_l=uninstrumented +fun:isspace=uninstrumented +fun:isspace_l=uninstrumented +fun:isupper=uninstrumented +fun:isupper_l=uninstrumented +fun:iswalnum=uninstrumented +fun:iswalnum_l=uninstrumented +fun:iswalpha=uninstrumented +fun:iswalpha_l=uninstrumented +fun:iswblank=uninstrumented +fun:iswblank_l=uninstrumented +fun:iswcntrl=uninstrumented +fun:iswcntrl_l=uninstrumented +fun:iswctype=uninstrumented +fun:iswctype_l=uninstrumented +fun:iswdigit=uninstrumented +fun:iswdigit_l=uninstrumented +fun:iswgraph=uninstrumented +fun:iswgraph_l=uninstrumented +fun:iswlower=uninstrumented +fun:iswlower_l=uninstrumented +fun:iswprint=uninstrumented +fun:iswprint_l=uninstrumented +fun:iswpunct=uninstrumented +fun:iswpunct_l=uninstrumented +fun:iswspace=uninstrumented +fun:iswspace_l=uninstrumented +fun:iswupper=uninstrumented +fun:iswupper_l=uninstrumented +fun:iswxdigit=uninstrumented +fun:iswxdigit_l=uninstrumented +fun:isxdigit=uninstrumented +fun:isxdigit_l=uninstrumented +fun:j0=uninstrumented +fun:j0f=uninstrumented +fun:j0l=uninstrumented +fun:j1=uninstrumented +fun:j1f=uninstrumented +fun:j1l=uninstrumented +fun:jn=uninstrumented +fun:jnf=uninstrumented +fun:jnl=uninstrumented +fun:jrand48=uninstrumented +fun:jrand48_r=uninstrumented +fun:key_decryptsession=uninstrumented +fun:key_decryptsession_pk=uninstrumented +fun:key_encryptsession=uninstrumented +fun:key_encryptsession_pk=uninstrumented +fun:key_gendes=uninstrumented +fun:key_get_conv=uninstrumented +fun:key_secretkey_is_set=uninstrumented +fun:key_setnet=uninstrumented +fun:key_setsecret=uninstrumented +fun:kill=uninstrumented +fun:killpg=uninstrumented +fun:klogctl=uninstrumented +fun:l64a=uninstrumented +fun:labs=uninstrumented +fun:lchmod=uninstrumented +fun:lchown=uninstrumented +fun:lckpwdf=uninstrumented +fun:lcong48=uninstrumented +fun:lcong48_r=uninstrumented +fun:ldexp=uninstrumented +fun:ldexpf=uninstrumented +fun:ldexpl=uninstrumented +fun:ldiv=uninstrumented +fun:lfind=uninstrumented +fun:lgamma=uninstrumented +fun:lgamma_r=uninstrumented +fun:lgammaf=uninstrumented +fun:lgammaf_r=uninstrumented +fun:lgammal=uninstrumented +fun:lgammal_r=uninstrumented +fun:lgetxattr=uninstrumented +fun:link=uninstrumented +fun:linkat=uninstrumented +fun:lio_listio=uninstrumented +fun:lio_listio64=uninstrumented +fun:listen=uninstrumented +fun:listxattr=uninstrumented +fun:llabs=uninstrumented +fun:lldiv=uninstrumented +fun:llistxattr=uninstrumented +fun:llrint=uninstrumented +fun:llrintf=uninstrumented +fun:llrintl=uninstrumented +fun:llround=uninstrumented +fun:llroundf=uninstrumented +fun:llroundl=uninstrumented +fun:llseek=uninstrumented +fun:localeconv=uninstrumented +fun:localtime=uninstrumented +fun:localtime_r=uninstrumented +fun:lockf=uninstrumented +fun:lockf64=uninstrumented +fun:log=uninstrumented +fun:log10=uninstrumented +fun:log10f=uninstrumented +fun:log10l=uninstrumented +fun:log1p=uninstrumented +fun:log1pf=uninstrumented +fun:log1pl=uninstrumented +fun:log2=uninstrumented +fun:log2f=uninstrumented +fun:log2l=uninstrumented +fun:logb=uninstrumented +fun:logbf=uninstrumented +fun:logbl=uninstrumented +fun:logf=uninstrumented +fun:login=uninstrumented +fun:login_tty=uninstrumented +fun:logl=uninstrumented +fun:logout=uninstrumented +fun:logwtmp=uninstrumented +fun:longjmp=uninstrumented +fun:lrand48=uninstrumented +fun:lrand48_r=uninstrumented +fun:lremovexattr=uninstrumented +fun:lrint=uninstrumented +fun:lrintf=uninstrumented +fun:lrintl=uninstrumented +fun:lround=uninstrumented +fun:lroundf=uninstrumented +fun:lroundl=uninstrumented +fun:lsearch=uninstrumented +fun:lseek=uninstrumented +fun:lseek64=uninstrumented +fun:lsetxattr=uninstrumented +fun:lstat=uninstrumented +fun:lstat64=uninstrumented +fun:lutimes=uninstrumented +fun:madvise=uninstrumented +fun:makecontext=uninstrumented +fun:mallinfo=uninstrumented +fun:malloc=uninstrumented +fun:malloc_get_state=uninstrumented +fun:malloc_info=uninstrumented +fun:malloc_set_state=uninstrumented +fun:malloc_stats=uninstrumented +fun:malloc_trim=uninstrumented +fun:malloc_usable_size=uninstrumented +fun:mallopt=uninstrumented +fun:matherr=uninstrumented +fun:mblen=uninstrumented +fun:mbrlen=uninstrumented +fun:mbrtowc=uninstrumented +fun:mbsinit=uninstrumented +fun:mbsnrtowcs=uninstrumented +fun:mbsrtowcs=uninstrumented +fun:mbstowcs=uninstrumented +fun:mbtowc=uninstrumented +fun:mcheck=uninstrumented +fun:mcheck_check_all=uninstrumented +fun:mcheck_pedantic=uninstrumented +fun:mcount=uninstrumented +fun:memalign=uninstrumented +fun:memccpy=uninstrumented +fun:memchr=uninstrumented +fun:memcmp=uninstrumented +fun:memcpy=uninstrumented +fun:memfrob=uninstrumented +fun:memmem=uninstrumented +fun:memmove=uninstrumented +fun:mempcpy=uninstrumented +fun:memrchr=uninstrumented +fun:memset=uninstrumented +fun:mincore=uninstrumented +fun:mkdir=uninstrumented +fun:mkdirat=uninstrumented +fun:mkdtemp=uninstrumented +fun:mkfifo=uninstrumented +fun:mkfifoat=uninstrumented +fun:mknod=uninstrumented +fun:mknodat=uninstrumented +fun:mkostemp=uninstrumented +fun:mkostemp64=uninstrumented +fun:mkostemps=uninstrumented +fun:mkostemps64=uninstrumented +fun:mkstemp=uninstrumented +fun:mkstemp64=uninstrumented +fun:mkstemps=uninstrumented +fun:mkstemps64=uninstrumented +fun:mktemp=uninstrumented +fun:mktime=uninstrumented +fun:mlock=uninstrumented +fun:mlockall=uninstrumented +fun:mmap=uninstrumented +fun:mmap64=uninstrumented +fun:modf=uninstrumented +fun:modff=uninstrumented +fun:modfl=uninstrumented +fun:modify_ldt=uninstrumented +fun:moncontrol=uninstrumented +fun:monstartup=uninstrumented +fun:mount=uninstrumented +fun:mprobe=uninstrumented +fun:mprotect=uninstrumented +fun:mq_close=uninstrumented +fun:mq_getattr=uninstrumented +fun:mq_notify=uninstrumented +fun:mq_open=uninstrumented +fun:mq_receive=uninstrumented +fun:mq_send=uninstrumented +fun:mq_setattr=uninstrumented +fun:mq_timedreceive=uninstrumented +fun:mq_timedsend=uninstrumented +fun:mq_unlink=uninstrumented +fun:mrand48=uninstrumented +fun:mrand48_r=uninstrumented +fun:mremap=uninstrumented +fun:msgctl=uninstrumented +fun:msgget=uninstrumented +fun:msgrcv=uninstrumented +fun:msgsnd=uninstrumented +fun:msync=uninstrumented +fun:mtrace=uninstrumented +fun:munlock=uninstrumented +fun:munlockall=uninstrumented +fun:munmap=uninstrumented +fun:muntrace=uninstrumented +fun:name_to_handle_at=uninstrumented +fun:nan=uninstrumented +fun:nanf=uninstrumented +fun:nanl=uninstrumented +fun:nanosleep=uninstrumented +fun:nearbyint=uninstrumented +fun:nearbyintf=uninstrumented +fun:nearbyintl=uninstrumented +fun:netname2host=uninstrumented +fun:netname2user=uninstrumented +fun:newlocale=uninstrumented +fun:nextafter=uninstrumented +fun:nextafterf=uninstrumented +fun:nextafterl=uninstrumented +fun:nexttoward=uninstrumented +fun:nexttowardf=uninstrumented +fun:nexttowardl=uninstrumented +fun:nfsservctl=uninstrumented +fun:nftw=uninstrumented +fun:nftw64=uninstrumented +fun:ngettext=uninstrumented +fun:nice=uninstrumented +fun:nis_add=uninstrumented +fun:nis_add_entry=uninstrumented +fun:nis_addmember=uninstrumented +fun:nis_checkpoint=uninstrumented +fun:nis_clone_directory=uninstrumented +fun:nis_clone_object=uninstrumented +fun:nis_clone_result=uninstrumented +fun:nis_creategroup=uninstrumented +fun:nis_destroy_object=uninstrumented +fun:nis_destroygroup=uninstrumented +fun:nis_dir_cmp=uninstrumented +fun:nis_domain_of=uninstrumented +fun:nis_domain_of_r=uninstrumented +fun:nis_first_entry=uninstrumented +fun:nis_free_directory=uninstrumented +fun:nis_free_object=uninstrumented +fun:nis_free_request=uninstrumented +fun:nis_freenames=uninstrumented +fun:nis_freeresult=uninstrumented +fun:nis_freeservlist=uninstrumented +fun:nis_freetags=uninstrumented +fun:nis_getnames=uninstrumented +fun:nis_getservlist=uninstrumented +fun:nis_ismember=uninstrumented +fun:nis_leaf_of=uninstrumented +fun:nis_leaf_of_r=uninstrumented +fun:nis_lerror=uninstrumented +fun:nis_list=uninstrumented +fun:nis_local_directory=uninstrumented +fun:nis_local_group=uninstrumented +fun:nis_local_host=uninstrumented +fun:nis_local_principal=uninstrumented +fun:nis_lookup=uninstrumented +fun:nis_mkdir=uninstrumented +fun:nis_modify=uninstrumented +fun:nis_modify_entry=uninstrumented +fun:nis_name_of=uninstrumented +fun:nis_name_of_r=uninstrumented +fun:nis_next_entry=uninstrumented +fun:nis_perror=uninstrumented +fun:nis_ping=uninstrumented +fun:nis_print_directory=uninstrumented +fun:nis_print_entry=uninstrumented +fun:nis_print_group=uninstrumented +fun:nis_print_group_entry=uninstrumented +fun:nis_print_link=uninstrumented +fun:nis_print_object=uninstrumented +fun:nis_print_result=uninstrumented +fun:nis_print_rights=uninstrumented +fun:nis_print_table=uninstrumented +fun:nis_read_obj=uninstrumented +fun:nis_remove=uninstrumented +fun:nis_remove_entry=uninstrumented +fun:nis_removemember=uninstrumented +fun:nis_rmdir=uninstrumented +fun:nis_servstate=uninstrumented +fun:nis_sperrno=uninstrumented +fun:nis_sperror=uninstrumented +fun:nis_sperror_r=uninstrumented +fun:nis_stats=uninstrumented +fun:nis_verifygroup=uninstrumented +fun:nis_write_obj=uninstrumented +fun:nl_langinfo=uninstrumented +fun:nl_langinfo_l=uninstrumented +fun:nop=uninstrumented +fun:nptl_freeres=uninstrumented +fun:nr_digits256=uninstrumented +fun:nrand48=uninstrumented +fun:nrand48_r=uninstrumented +fun:ns_datetosecs=uninstrumented +fun:ns_format_ttl=uninstrumented +fun:ns_get16=uninstrumented +fun:ns_get32=uninstrumented +fun:ns_initparse=uninstrumented +fun:ns_makecanon=uninstrumented +fun:ns_msg_getflag=uninstrumented +fun:ns_name_compress=uninstrumented +fun:ns_name_ntol=uninstrumented +fun:ns_name_ntop=uninstrumented +fun:ns_name_pack=uninstrumented +fun:ns_name_pton=uninstrumented +fun:ns_name_rollback=uninstrumented +fun:ns_name_skip=uninstrumented +fun:ns_name_uncompress=uninstrumented +fun:ns_name_unpack=uninstrumented +fun:ns_parse_ttl=uninstrumented +fun:ns_parserr=uninstrumented +fun:ns_put16=uninstrumented +fun:ns_put32=uninstrumented +fun:ns_samedomain=uninstrumented +fun:ns_samename=uninstrumented +fun:ns_skiprr=uninstrumented +fun:ns_sprintrr=uninstrumented +fun:ns_sprintrrf=uninstrumented +fun:ns_subdomain=uninstrumented +fun:ntohl=uninstrumented +fun:ntohs=uninstrumented +fun:ntp_adjtime=uninstrumented +fun:ntp_gettime=uninstrumented +fun:ntp_gettimex=uninstrumented +fun:obstack_free=uninstrumented +fun:obstack_printf=uninstrumented +fun:obstack_vprintf=uninstrumented +fun:on_exit=uninstrumented +fun:open=uninstrumented +fun:open64=uninstrumented +fun:open_by_handle_at=uninstrumented +fun:open_memstream=uninstrumented +fun:open_wmemstream=uninstrumented +fun:openat=uninstrumented +fun:openat64=uninstrumented +fun:opendir=uninstrumented +fun:openlog=uninstrumented +fun:openpty=uninstrumented +fun:parse_printf_format=uninstrumented +fun:passwd2des=uninstrumented +fun:pathconf=uninstrumented +fun:pause=uninstrumented +fun:pclose=uninstrumented +fun:perror=uninstrumented +fun:personality=uninstrumented +fun:pipe=uninstrumented +fun:pipe2=uninstrumented +fun:pivot_root=uninstrumented +fun:pmap_getmaps=uninstrumented +fun:pmap_getport=uninstrumented +fun:pmap_rmtcall=uninstrumented +fun:pmap_set=uninstrumented +fun:pmap_unset=uninstrumented +fun:poll=uninstrumented +fun:popen=uninstrumented +fun:posix_fadvise=uninstrumented +fun:posix_fadvise64=uninstrumented +fun:posix_fallocate=uninstrumented +fun:posix_fallocate64=uninstrumented +fun:posix_madvise=uninstrumented +fun:posix_memalign=uninstrumented +fun:posix_openpt=uninstrumented +fun:posix_spawn=uninstrumented +fun:posix_spawn_file_actions_addclose=uninstrumented +fun:posix_spawn_file_actions_adddup2=uninstrumented +fun:posix_spawn_file_actions_addopen=uninstrumented +fun:posix_spawn_file_actions_destroy=uninstrumented +fun:posix_spawn_file_actions_init=uninstrumented +fun:posix_spawnattr_destroy=uninstrumented +fun:posix_spawnattr_getflags=uninstrumented +fun:posix_spawnattr_getpgroup=uninstrumented +fun:posix_spawnattr_getschedparam=uninstrumented +fun:posix_spawnattr_getschedpolicy=uninstrumented +fun:posix_spawnattr_getsigdefault=uninstrumented +fun:posix_spawnattr_getsigmask=uninstrumented +fun:posix_spawnattr_init=uninstrumented +fun:posix_spawnattr_setflags=uninstrumented +fun:posix_spawnattr_setpgroup=uninstrumented +fun:posix_spawnattr_setschedparam=uninstrumented +fun:posix_spawnattr_setschedpolicy=uninstrumented +fun:posix_spawnattr_setsigdefault=uninstrumented +fun:posix_spawnattr_setsigmask=uninstrumented +fun:posix_spawnp=uninstrumented +fun:pow=uninstrumented +fun:pow10=uninstrumented +fun:pow10f=uninstrumented +fun:pow10l=uninstrumented +fun:powf=uninstrumented +fun:powl=uninstrumented +fun:ppoll=uninstrumented +fun:prctl=uninstrumented +fun:pread=uninstrumented +fun:pread64=uninstrumented +fun:preadv=uninstrumented +fun:preadv64=uninstrumented +fun:printf=uninstrumented +fun:printf_size=uninstrumented +fun:printf_size_info=uninstrumented +fun:prlimit=uninstrumented +fun:prlimit64=uninstrumented +fun:process_vm_readv=uninstrumented +fun:process_vm_writev=uninstrumented +fun:profil=uninstrumented +fun:pselect=uninstrumented +fun:psiginfo=uninstrumented +fun:psignal=uninstrumented +fun:pthread_atfork=uninstrumented +fun:pthread_attr_destroy=uninstrumented +fun:pthread_attr_getaffinity_np=uninstrumented +fun:pthread_attr_getdetachstate=uninstrumented +fun:pthread_attr_getguardsize=uninstrumented +fun:pthread_attr_getinheritsched=uninstrumented +fun:pthread_attr_getschedparam=uninstrumented +fun:pthread_attr_getschedpolicy=uninstrumented +fun:pthread_attr_getscope=uninstrumented +fun:pthread_attr_getstack=uninstrumented +fun:pthread_attr_getstackaddr=uninstrumented +fun:pthread_attr_getstacksize=uninstrumented +fun:pthread_attr_init=uninstrumented +fun:pthread_attr_setaffinity_np=uninstrumented +fun:pthread_attr_setdetachstate=uninstrumented +fun:pthread_attr_setguardsize=uninstrumented +fun:pthread_attr_setinheritsched=uninstrumented +fun:pthread_attr_setschedparam=uninstrumented +fun:pthread_attr_setschedpolicy=uninstrumented +fun:pthread_attr_setscope=uninstrumented +fun:pthread_attr_setstack=uninstrumented +fun:pthread_attr_setstackaddr=uninstrumented +fun:pthread_attr_setstacksize=uninstrumented +fun:pthread_barrier_destroy=uninstrumented +fun:pthread_barrier_init=uninstrumented +fun:pthread_barrier_wait=uninstrumented +fun:pthread_barrierattr_destroy=uninstrumented +fun:pthread_barrierattr_getpshared=uninstrumented +fun:pthread_barrierattr_init=uninstrumented +fun:pthread_barrierattr_setpshared=uninstrumented +fun:pthread_cancel=uninstrumented +fun:pthread_cancel_init=uninstrumented +fun:pthread_cond_broadcast=uninstrumented +fun:pthread_cond_destroy=uninstrumented +fun:pthread_cond_init=uninstrumented +fun:pthread_cond_signal=uninstrumented +fun:pthread_cond_timedwait=uninstrumented +fun:pthread_cond_wait=uninstrumented +fun:pthread_condattr_destroy=uninstrumented +fun:pthread_condattr_getclock=uninstrumented +fun:pthread_condattr_getpshared=uninstrumented +fun:pthread_condattr_init=uninstrumented +fun:pthread_condattr_setclock=uninstrumented +fun:pthread_condattr_setpshared=uninstrumented +fun:pthread_create=uninstrumented +fun:pthread_detach=uninstrumented +fun:pthread_equal=uninstrumented +fun:pthread_exit=uninstrumented +fun:pthread_getaffinity_np=uninstrumented +fun:pthread_getattr_np=uninstrumented +fun:pthread_getconcurrency=uninstrumented +fun:pthread_getcpuclockid=uninstrumented +fun:pthread_getname_np=uninstrumented +fun:pthread_getschedparam=uninstrumented +fun:pthread_getspecific=uninstrumented +fun:pthread_join=uninstrumented +fun:pthread_key_create=uninstrumented +fun:pthread_key_delete=uninstrumented +fun:pthread_kill=uninstrumented +fun:pthread_kill_other_threads_np=uninstrumented +fun:pthread_mutex_consistent=uninstrumented +fun:pthread_mutex_consistent_np=uninstrumented +fun:pthread_mutex_destroy=uninstrumented +fun:pthread_mutex_getprioceiling=uninstrumented +fun:pthread_mutex_init=uninstrumented +fun:pthread_mutex_lock=uninstrumented +fun:pthread_mutex_setprioceiling=uninstrumented +fun:pthread_mutex_timedlock=uninstrumented +fun:pthread_mutex_trylock=uninstrumented +fun:pthread_mutex_unlock=uninstrumented +fun:pthread_mutexattr_destroy=uninstrumented +fun:pthread_mutexattr_getkind_np=uninstrumented +fun:pthread_mutexattr_getprioceiling=uninstrumented +fun:pthread_mutexattr_getprotocol=uninstrumented +fun:pthread_mutexattr_getpshared=uninstrumented +fun:pthread_mutexattr_getrobust=uninstrumented +fun:pthread_mutexattr_getrobust_np=uninstrumented +fun:pthread_mutexattr_gettype=uninstrumented +fun:pthread_mutexattr_init=uninstrumented +fun:pthread_mutexattr_setkind_np=uninstrumented +fun:pthread_mutexattr_setprioceiling=uninstrumented +fun:pthread_mutexattr_setprotocol=uninstrumented +fun:pthread_mutexattr_setpshared=uninstrumented +fun:pthread_mutexattr_setrobust=uninstrumented +fun:pthread_mutexattr_setrobust_np=uninstrumented +fun:pthread_mutexattr_settype=uninstrumented +fun:pthread_once=uninstrumented +fun:pthread_rwlock_destroy=uninstrumented +fun:pthread_rwlock_init=uninstrumented +fun:pthread_rwlock_rdlock=uninstrumented +fun:pthread_rwlock_timedrdlock=uninstrumented +fun:pthread_rwlock_timedwrlock=uninstrumented +fun:pthread_rwlock_tryrdlock=uninstrumented +fun:pthread_rwlock_trywrlock=uninstrumented +fun:pthread_rwlock_unlock=uninstrumented +fun:pthread_rwlock_wrlock=uninstrumented +fun:pthread_rwlockattr_destroy=uninstrumented +fun:pthread_rwlockattr_getkind_np=uninstrumented +fun:pthread_rwlockattr_getpshared=uninstrumented +fun:pthread_rwlockattr_init=uninstrumented +fun:pthread_rwlockattr_setkind_np=uninstrumented +fun:pthread_rwlockattr_setpshared=uninstrumented +fun:pthread_self=uninstrumented +fun:pthread_setaffinity_np=uninstrumented +fun:pthread_setcancelstate=uninstrumented +fun:pthread_setcanceltype=uninstrumented +fun:pthread_setconcurrency=uninstrumented +fun:pthread_setname_np=uninstrumented +fun:pthread_setschedparam=uninstrumented +fun:pthread_setschedprio=uninstrumented +fun:pthread_setspecific=uninstrumented +fun:pthread_sigmask=uninstrumented +fun:pthread_sigqueue=uninstrumented +fun:pthread_spin_destroy=uninstrumented +fun:pthread_spin_init=uninstrumented +fun:pthread_spin_lock=uninstrumented +fun:pthread_spin_trylock=uninstrumented +fun:pthread_spin_unlock=uninstrumented +fun:pthread_testcancel=uninstrumented +fun:pthread_timedjoin_np=uninstrumented +fun:pthread_tryjoin_np=uninstrumented +fun:pthread_yield=uninstrumented +fun:ptrace=uninstrumented +fun:ptsname=uninstrumented +fun:ptsname_r=uninstrumented +fun:putc=uninstrumented +fun:putc_unlocked=uninstrumented +fun:putchar=uninstrumented +fun:putchar_unlocked=uninstrumented +fun:putenv=uninstrumented +fun:putgrent=uninstrumented +fun:putmsg=uninstrumented +fun:putpmsg=uninstrumented +fun:putpwent=uninstrumented +fun:puts=uninstrumented +fun:putsgent=uninstrumented +fun:putspent=uninstrumented +fun:pututline=uninstrumented +fun:pututxline=uninstrumented +fun:putw=uninstrumented +fun:putwc=uninstrumented +fun:putwc_unlocked=uninstrumented +fun:putwchar=uninstrumented +fun:putwchar_unlocked=uninstrumented +fun:pvalloc=uninstrumented +fun:pwrite=uninstrumented +fun:pwrite64=uninstrumented +fun:pwritev=uninstrumented +fun:pwritev64=uninstrumented +fun:qecvt=uninstrumented +fun:qecvt_r=uninstrumented +fun:qfcvt=uninstrumented +fun:qfcvt_r=uninstrumented +fun:qgcvt=uninstrumented +fun:qsort=uninstrumented +fun:qsort_r=uninstrumented +fun:query_module=uninstrumented +fun:quick_exit=uninstrumented +fun:quotactl=uninstrumented +fun:raise=uninstrumented +fun:rand=uninstrumented +fun:rand_r=uninstrumented +fun:random=uninstrumented +fun:random_r=uninstrumented +fun:rawmemchr=uninstrumented +fun:rcmd=uninstrumented +fun:rcmd_af=uninstrumented +fun:re_comp=uninstrumented +fun:re_compile_fastmap=uninstrumented +fun:re_compile_pattern=uninstrumented +fun:re_exec=uninstrumented +fun:re_match=uninstrumented +fun:re_match_2=uninstrumented +fun:re_search=uninstrumented +fun:re_search_2=uninstrumented +fun:re_set_registers=uninstrumented +fun:re_set_syntax=uninstrumented +fun:read=uninstrumented +fun:readColdStartFile=uninstrumented +fun:readahead=uninstrumented +fun:readdir=uninstrumented +fun:readdir64=uninstrumented +fun:readdir64_r=uninstrumented +fun:readdir_r=uninstrumented +fun:readlink=uninstrumented +fun:readlinkat=uninstrumented +fun:readv=uninstrumented +fun:realloc=uninstrumented +fun:realpath=uninstrumented +fun:reboot=uninstrumented +fun:recv=uninstrumented +fun:recvfrom=uninstrumented +fun:recvmmsg=uninstrumented +fun:recvmsg=uninstrumented +fun:regcomp=uninstrumented +fun:regerror=uninstrumented +fun:regexec=uninstrumented +fun:regfree=uninstrumented +fun:register_printf_function=uninstrumented +fun:register_printf_modifier=uninstrumented +fun:register_printf_specifier=uninstrumented +fun:register_printf_type=uninstrumented +fun:registerrpc=uninstrumented +fun:remainder=uninstrumented +fun:remainderf=uninstrumented +fun:remainderl=uninstrumented +fun:remap_file_pages=uninstrumented +fun:remove=uninstrumented +fun:removexattr=uninstrumented +fun:remque=uninstrumented +fun:remquo=uninstrumented +fun:remquof=uninstrumented +fun:remquol=uninstrumented +fun:rename=uninstrumented +fun:renameat=uninstrumented +fun:res_gethostbyaddr=uninstrumented +fun:res_gethostbyname=uninstrumented +fun:res_gethostbyname2=uninstrumented +fun:res_send_setqhook=uninstrumented +fun:res_send_setrhook=uninstrumented +fun:revoke=uninstrumented +fun:rewind=uninstrumented +fun:rewinddir=uninstrumented +fun:rexec=uninstrumented +fun:rexec_af=uninstrumented +fun:rindex=uninstrumented +fun:rint=uninstrumented +fun:rintf=uninstrumented +fun:rintl=uninstrumented +fun:rmdir=uninstrumented +fun:round=uninstrumented +fun:roundf=uninstrumented +fun:rounding_correction.constprop.1=uninstrumented +fun:roundl=uninstrumented +fun:rpmatch=uninstrumented +fun:rresvport=uninstrumented +fun:rresvport_af=uninstrumented +fun:rtime=uninstrumented +fun:ruserok=uninstrumented +fun:ruserok_af=uninstrumented +fun:ruserpass=uninstrumented +fun:sbrk=uninstrumented +fun:scalb=uninstrumented +fun:scalbf=uninstrumented +fun:scalbl=uninstrumented +fun:scalbln=uninstrumented +fun:scalblnf=uninstrumented +fun:scalblnl=uninstrumented +fun:scalbn=uninstrumented +fun:scalbnf=uninstrumented +fun:scalbnl=uninstrumented +fun:scandir=uninstrumented +fun:scandir64=uninstrumented +fun:scandirat=uninstrumented +fun:scandirat64=uninstrumented +fun:scanf=uninstrumented +fun:sched_get_priority_max=uninstrumented +fun:sched_get_priority_min=uninstrumented +fun:sched_getaffinity=uninstrumented +fun:sched_getcpu=uninstrumented +fun:sched_getparam=uninstrumented +fun:sched_getscheduler=uninstrumented +fun:sched_rr_get_interval=uninstrumented +fun:sched_setaffinity=uninstrumented +fun:sched_setparam=uninstrumented +fun:sched_setscheduler=uninstrumented +fun:sched_yield=uninstrumented +fun:seed48=uninstrumented +fun:seed48_r=uninstrumented +fun:seekdir=uninstrumented +fun:select=uninstrumented +fun:sem_close=uninstrumented +fun:sem_destroy=uninstrumented +fun:sem_getvalue=uninstrumented +fun:sem_init=uninstrumented +fun:sem_open=uninstrumented +fun:sem_post=uninstrumented +fun:sem_timedwait=uninstrumented +fun:sem_timedwait_cleanup=uninstrumented +fun:sem_timedwait_cleanup2=uninstrumented +fun:sem_trywait=uninstrumented +fun:sem_unlink=uninstrumented +fun:sem_wait=uninstrumented +fun:sem_wait_cleanup=uninstrumented +fun:semctl=uninstrumented +fun:semget=uninstrumented +fun:semop=uninstrumented +fun:semtimedop=uninstrumented +fun:send=uninstrumented +fun:sendfile=uninstrumented +fun:sendfile64=uninstrumented +fun:sendmmsg=uninstrumented +fun:sendmsg=uninstrumented +fun:sendto=uninstrumented +fun:setaliasent=uninstrumented +fun:setbuf=uninstrumented +fun:setbuffer=uninstrumented +fun:setcontext=uninstrumented +fun:setdomainname=uninstrumented +fun:setegid=uninstrumented +fun:setenv=uninstrumented +fun:seteuid=uninstrumented +fun:setfsent=uninstrumented +fun:setfsgid=uninstrumented +fun:setfsuid=uninstrumented +fun:setgid=uninstrumented +fun:setgrent=uninstrumented +fun:setgroups=uninstrumented +fun:sethostent=uninstrumented +fun:sethostid=uninstrumented +fun:sethostname=uninstrumented +fun:setipv4sourcefilter=uninstrumented +fun:setitimer=uninstrumented +fun:setjmp=uninstrumented +fun:setkey=uninstrumented +fun:setkey_r=uninstrumented +fun:setlinebuf=uninstrumented +fun:setlocale=uninstrumented +fun:setlogin=uninstrumented +fun:setlogmask=uninstrumented +fun:setmntent=uninstrumented +fun:setnetent=uninstrumented +fun:setnetgrent=uninstrumented +fun:setns=uninstrumented +fun:setpgid=uninstrumented +fun:setpgrp=uninstrumented +fun:setpriority=uninstrumented +fun:setprotoent=uninstrumented +fun:setpwent=uninstrumented +fun:setregid=uninstrumented +fun:setresgid=uninstrumented +fun:setresuid=uninstrumented +fun:setreuid=uninstrumented +fun:setrlimit=uninstrumented +fun:setrlimit64=uninstrumented +fun:setrpcent=uninstrumented +fun:setservent=uninstrumented +fun:setsgent=uninstrumented +fun:setsid=uninstrumented +fun:setsockopt=uninstrumented +fun:setsourcefilter=uninstrumented +fun:setspent=uninstrumented +fun:setstate=uninstrumented +fun:setstate_r=uninstrumented +fun:settimeofday=uninstrumented +fun:setttyent=uninstrumented +fun:setuid=uninstrumented +fun:setusershell=uninstrumented +fun:setutent=uninstrumented +fun:setutxent=uninstrumented +fun:setvbuf=uninstrumented +fun:setxattr=uninstrumented +fun:setxid_mark_thread.isra.1=uninstrumented +fun:sgetsgent=uninstrumented +fun:sgetsgent_r=uninstrumented +fun:sgetspent=uninstrumented +fun:sgetspent_r=uninstrumented +fun:shm_open=uninstrumented +fun:shm_unlink=uninstrumented +fun:shmat=uninstrumented +fun:shmctl=uninstrumented +fun:shmdt=uninstrumented +fun:shmget=uninstrumented +fun:shutdown=uninstrumented +fun:sigaction=uninstrumented +fun:sigaddset=uninstrumented +fun:sigaltstack=uninstrumented +fun:sigandset=uninstrumented +fun:sigblock=uninstrumented +fun:sigcancel_handler=uninstrumented +fun:sigdelset=uninstrumented +fun:sigemptyset=uninstrumented +fun:sigfillset=uninstrumented +fun:siggetmask=uninstrumented +fun:sighandler_setxid=uninstrumented +fun:sighold=uninstrumented +fun:sigignore=uninstrumented +fun:siginterrupt=uninstrumented +fun:sigisemptyset=uninstrumented +fun:sigismember=uninstrumented +fun:siglongjmp=uninstrumented +fun:signal=uninstrumented +fun:signalfd=uninstrumented +fun:significand=uninstrumented +fun:significandf=uninstrumented +fun:significandl=uninstrumented +fun:sigorset=uninstrumented +fun:sigpause=uninstrumented +fun:sigpending=uninstrumented +fun:sigprocmask=uninstrumented +fun:sigqueue=uninstrumented +fun:sigrelse=uninstrumented +fun:sigreturn=uninstrumented +fun:sigset=uninstrumented +fun:sigsetmask=uninstrumented +fun:sigstack=uninstrumented +fun:sigsuspend=uninstrumented +fun:sigtimedwait=uninstrumented +fun:sigvec=uninstrumented +fun:sigwait=uninstrumented +fun:sigwaitinfo=uninstrumented +fun:sin=uninstrumented +fun:sincos=uninstrumented +fun:sincosf=uninstrumented +fun:sincosl=uninstrumented +fun:sinf=uninstrumented +fun:sinh=uninstrumented +fun:sinhf=uninstrumented +fun:sinhl=uninstrumented +fun:sinl=uninstrumented +fun:sleep=uninstrumented +fun:snprintf=uninstrumented +fun:sockatmark=uninstrumented +fun:socket=uninstrumented +fun:socketpair=uninstrumented +fun:splice=uninstrumented +fun:sprintf=uninstrumented +fun:sprofil=uninstrumented +fun:sqrt=uninstrumented +fun:sqrtf=uninstrumented +fun:sqrtl=uninstrumented +fun:srand=uninstrumented +fun:srand48=uninstrumented +fun:srand48_r=uninstrumented +fun:srandom=uninstrumented +fun:srandom_r=uninstrumented +fun:sscanf=uninstrumented +fun:ssignal=uninstrumented +fun:sstk=uninstrumented +fun:stack_split_initialize_thread=uninstrumented +fun:start_thread=uninstrumented +fun:stat=uninstrumented +fun:stat64=uninstrumented +fun:statfs=uninstrumented +fun:statfs64=uninstrumented +fun:statvfs=uninstrumented +fun:statvfs64=uninstrumented +fun:step=uninstrumented +fun:stime=uninstrumented +fun:stpcpy=uninstrumented +fun:stpncpy=uninstrumented +fun:strcasecmp=uninstrumented +fun:strcasecmp_l=uninstrumented +fun:strcasestr=uninstrumented +fun:strcat=uninstrumented +fun:strchr=uninstrumented +fun:strchrnul=uninstrumented +fun:strcmp=uninstrumented +fun:strcoll=uninstrumented +fun:strcoll_l=uninstrumented +fun:strcpy=uninstrumented +fun:strcspn=uninstrumented +fun:strdup=uninstrumented +fun:strerror=uninstrumented +fun:strerror_l=uninstrumented +fun:strerror_r=uninstrumented +fun:strfmon=uninstrumented +fun:strfmon_l=uninstrumented +fun:strfry=uninstrumented +fun:strftime=uninstrumented +fun:strftime_l=uninstrumented +fun:strlen=uninstrumented +fun:strncasecmp=uninstrumented +fun:strncasecmp_l=uninstrumented +fun:strncat=uninstrumented +fun:strncmp=uninstrumented +fun:strncpy=uninstrumented +fun:strndup=uninstrumented +fun:strnlen=uninstrumented +fun:strpbrk=uninstrumented +fun:strptime=uninstrumented +fun:strptime_l=uninstrumented +fun:strrchr=uninstrumented +fun:strsep=uninstrumented +fun:strsignal=uninstrumented +fun:strspn=uninstrumented +fun:strstr=uninstrumented +fun:strtod=uninstrumented +fun:strtod_l=uninstrumented +fun:strtof=uninstrumented +fun:strtof_l=uninstrumented +fun:strtoimax=uninstrumented +fun:strtok=uninstrumented +fun:strtok_r=uninstrumented +fun:strtol=uninstrumented +fun:strtol_l=uninstrumented +fun:strtold=uninstrumented +fun:strtold_l=uninstrumented +fun:strtoll=uninstrumented +fun:strtoll_l=uninstrumented +fun:strtoq=uninstrumented +fun:strtoul=uninstrumented +fun:strtoul_l=uninstrumented +fun:strtoull=uninstrumented +fun:strtoull_l=uninstrumented +fun:strtoumax=uninstrumented +fun:strtouq=uninstrumented +fun:strverscmp=uninstrumented +fun:strxfrm=uninstrumented +fun:strxfrm_l=uninstrumented +fun:stty=uninstrumented +fun:sub256=uninstrumented +fun:svc_exit=uninstrumented +fun:svc_getreq=uninstrumented +fun:svc_getreq_common=uninstrumented +fun:svc_getreq_poll=uninstrumented +fun:svc_getreqset=uninstrumented +fun:svc_register=uninstrumented +fun:svc_run=uninstrumented +fun:svc_sendreply=uninstrumented +fun:svc_unregister=uninstrumented +fun:svcerr_auth=uninstrumented +fun:svcerr_decode=uninstrumented +fun:svcerr_noproc=uninstrumented +fun:svcerr_noprog=uninstrumented +fun:svcerr_progvers=uninstrumented +fun:svcerr_systemerr=uninstrumented +fun:svcerr_weakauth=uninstrumented +fun:svcfd_create=uninstrumented +fun:svcraw_create=uninstrumented +fun:svctcp_create=uninstrumented +fun:svcudp_bufcreate=uninstrumented +fun:svcudp_create=uninstrumented +fun:svcudp_enablecache=uninstrumented +fun:svcunix_create=uninstrumented +fun:svcunixfd_create=uninstrumented +fun:swab=uninstrumented +fun:swapcontext=uninstrumented +fun:swapoff=uninstrumented +fun:swapon=uninstrumented +fun:swprintf=uninstrumented +fun:swscanf=uninstrumented +fun:symlink=uninstrumented +fun:symlinkat=uninstrumented +fun:sync=uninstrumented +fun:sync_file_range=uninstrumented +fun:syncfs=uninstrumented +fun:syscall=uninstrumented +fun:sysconf=uninstrumented +fun:sysctl=uninstrumented +fun:sysinfo=uninstrumented +fun:syslog=uninstrumented +fun:system=uninstrumented +fun:sysv_signal=uninstrumented +fun:tan=uninstrumented +fun:tanf=uninstrumented +fun:tanh=uninstrumented +fun:tanhf=uninstrumented +fun:tanhl=uninstrumented +fun:tanl=uninstrumented +fun:tcdrain=uninstrumented +fun:tcflow=uninstrumented +fun:tcflush=uninstrumented +fun:tcgetattr=uninstrumented +fun:tcgetpgrp=uninstrumented +fun:tcgetsid=uninstrumented +fun:tcsendbreak=uninstrumented +fun:tcsetattr=uninstrumented +fun:tcsetpgrp=uninstrumented +fun:td_init=uninstrumented +fun:td_log=uninstrumented +fun:td_symbol_list=uninstrumented +fun:td_ta_clear_event=uninstrumented +fun:td_ta_delete=uninstrumented +fun:td_ta_enable_stats=uninstrumented +fun:td_ta_event_addr=uninstrumented +fun:td_ta_event_getmsg=uninstrumented +fun:td_ta_get_nthreads=uninstrumented +fun:td_ta_get_ph=uninstrumented +fun:td_ta_get_stats=uninstrumented +fun:td_ta_map_id2thr=uninstrumented +fun:td_ta_map_lwp2thr=uninstrumented +fun:td_ta_new=uninstrumented +fun:td_ta_reset_stats=uninstrumented +fun:td_ta_set_event=uninstrumented +fun:td_ta_setconcurrency=uninstrumented +fun:td_ta_thr_iter=uninstrumented +fun:td_ta_tsd_iter=uninstrumented +fun:td_thr_clear_event=uninstrumented +fun:td_thr_dbresume=uninstrumented +fun:td_thr_dbsuspend=uninstrumented +fun:td_thr_event_enable=uninstrumented +fun:td_thr_event_getmsg=uninstrumented +fun:td_thr_get_info=uninstrumented +fun:td_thr_getfpregs=uninstrumented +fun:td_thr_getgregs=uninstrumented +fun:td_thr_getxregs=uninstrumented +fun:td_thr_getxregsize=uninstrumented +fun:td_thr_set_event=uninstrumented +fun:td_thr_setfpregs=uninstrumented +fun:td_thr_setgregs=uninstrumented +fun:td_thr_setprio=uninstrumented +fun:td_thr_setsigpending=uninstrumented +fun:td_thr_setxregs=uninstrumented +fun:td_thr_sigsetmask=uninstrumented +fun:td_thr_tls_get_addr=uninstrumented +fun:td_thr_tlsbase=uninstrumented +fun:td_thr_tsd=uninstrumented +fun:td_thr_validate=uninstrumented +fun:tdelete=uninstrumented +fun:tdestroy=uninstrumented +fun:tee=uninstrumented +fun:telldir=uninstrumented +fun:tempnam=uninstrumented +fun:textdomain=uninstrumented +fun:tfind=uninstrumented +fun:tgamma=uninstrumented +fun:tgammaf=uninstrumented +fun:tgammal=uninstrumented +fun:time=uninstrumented +fun:timegm=uninstrumented +fun:timelocal=uninstrumented +fun:timer_create=uninstrumented +fun:timer_delete=uninstrumented +fun:timer_getoverrun=uninstrumented +fun:timer_gettime=uninstrumented +fun:timer_settime=uninstrumented +fun:timerfd_create=uninstrumented +fun:timerfd_gettime=uninstrumented +fun:timerfd_settime=uninstrumented +fun:times=uninstrumented +fun:tmpfile=uninstrumented +fun:tmpfile64=uninstrumented +fun:tmpnam=uninstrumented +fun:tmpnam_r=uninstrumented +fun:toascii=uninstrumented +fun:tolower=uninstrumented +fun:tolower_l=uninstrumented +fun:toupper=uninstrumented +fun:toupper_l=uninstrumented +fun:towctrans=uninstrumented +fun:towctrans_l=uninstrumented +fun:towlower=uninstrumented +fun:towlower_l=uninstrumented +fun:towupper=uninstrumented +fun:towupper_l=uninstrumented +fun:tr_break=uninstrumented +fun:trunc=uninstrumented +fun:truncate=uninstrumented +fun:truncate64=uninstrumented +fun:truncf=uninstrumented +fun:truncl=uninstrumented +fun:tsearch=uninstrumented +fun:ttyname=uninstrumented +fun:ttyname_r=uninstrumented +fun:ttyslot=uninstrumented +fun:twalk=uninstrumented +fun:tzset=uninstrumented +fun:ualarm=uninstrumented +fun:ulckpwdf=uninstrumented +fun:ulimit=uninstrumented +fun:umask=uninstrumented +fun:umount=uninstrumented +fun:umount2=uninstrumented +fun:uname=uninstrumented +fun:ungetc=uninstrumented +fun:ungetwc=uninstrumented +fun:unlink=uninstrumented +fun:unlinkat=uninstrumented +fun:unlockpt=uninstrumented +fun:unsetenv=uninstrumented +fun:unshare=uninstrumented +fun:unwind_cleanup=uninstrumented +fun:unwind_stop=uninstrumented +fun:updwtmp=uninstrumented +fun:updwtmpx=uninstrumented +fun:uselib=uninstrumented +fun:uselocale=uninstrumented +fun:user2netname=uninstrumented +fun:usleep=uninstrumented +fun:ustat=uninstrumented +fun:utime=uninstrumented +fun:utimensat=uninstrumented +fun:utimes=uninstrumented +fun:utmpname=uninstrumented +fun:utmpxname=uninstrumented +fun:valloc=uninstrumented +fun:vasprintf=uninstrumented +fun:vdprintf=uninstrumented +fun:verr=uninstrumented +fun:verrx=uninstrumented +fun:versionsort=uninstrumented +fun:versionsort64=uninstrumented +fun:vfork=uninstrumented +fun:vfprintf=uninstrumented +fun:vfscanf=uninstrumented +fun:vfwprintf=uninstrumented +fun:vfwscanf=uninstrumented +fun:vhangup=uninstrumented +fun:vlimit=uninstrumented +fun:vmsplice=uninstrumented +fun:vprintf=uninstrumented +fun:vscanf=uninstrumented +fun:vsnprintf=uninstrumented +fun:vsprintf=uninstrumented +fun:vsscanf=uninstrumented +fun:vswprintf=uninstrumented +fun:vswscanf=uninstrumented +fun:vsyslog=uninstrumented +fun:vtimes=uninstrumented +fun:vwarn=uninstrumented +fun:vwarnx=uninstrumented +fun:vwprintf=uninstrumented +fun:vwscanf=uninstrumented +fun:wait=uninstrumented +fun:wait3=uninstrumented +fun:wait4=uninstrumented +fun:waitid=uninstrumented +fun:waitpid=uninstrumented +fun:walker=uninstrumented +fun:warn=uninstrumented +fun:warnx=uninstrumented +fun:wcpcpy=uninstrumented +fun:wcpncpy=uninstrumented +fun:wcrtomb=uninstrumented +fun:wcscasecmp=uninstrumented +fun:wcscasecmp_l=uninstrumented +fun:wcscat=uninstrumented +fun:wcschr=uninstrumented +fun:wcschrnul=uninstrumented +fun:wcscmp=uninstrumented +fun:wcscoll=uninstrumented +fun:wcscoll_l=uninstrumented +fun:wcscpy=uninstrumented +fun:wcscspn=uninstrumented +fun:wcsdup=uninstrumented +fun:wcsftime=uninstrumented +fun:wcsftime_l=uninstrumented +fun:wcslen=uninstrumented +fun:wcsncasecmp=uninstrumented +fun:wcsncasecmp_l=uninstrumented +fun:wcsncat=uninstrumented +fun:wcsncmp=uninstrumented +fun:wcsncpy=uninstrumented +fun:wcsnlen=uninstrumented +fun:wcsnrtombs=uninstrumented +fun:wcspbrk=uninstrumented +fun:wcsrchr=uninstrumented +fun:wcsrtombs=uninstrumented +fun:wcsspn=uninstrumented +fun:wcsstr=uninstrumented +fun:wcstod=uninstrumented +fun:wcstod_l=uninstrumented +fun:wcstof=uninstrumented +fun:wcstof_l=uninstrumented +fun:wcstoimax=uninstrumented +fun:wcstok=uninstrumented +fun:wcstol=uninstrumented +fun:wcstol_l=uninstrumented +fun:wcstold=uninstrumented +fun:wcstold_l=uninstrumented +fun:wcstoll=uninstrumented +fun:wcstoll_l=uninstrumented +fun:wcstombs=uninstrumented +fun:wcstoq=uninstrumented +fun:wcstoul=uninstrumented +fun:wcstoul_l=uninstrumented +fun:wcstoull=uninstrumented +fun:wcstoull_l=uninstrumented +fun:wcstoumax=uninstrumented +fun:wcstouq=uninstrumented +fun:wcswcs=uninstrumented +fun:wcswidth=uninstrumented +fun:wcsxfrm=uninstrumented +fun:wcsxfrm_l=uninstrumented +fun:wctob=uninstrumented +fun:wctomb=uninstrumented +fun:wctrans=uninstrumented +fun:wctrans_l=uninstrumented +fun:wctype=uninstrumented +fun:wctype_l=uninstrumented +fun:wcwidth=uninstrumented +fun:wmemchr=uninstrumented +fun:wmemcmp=uninstrumented +fun:wmemcpy=uninstrumented +fun:wmemmove=uninstrumented +fun:wmempcpy=uninstrumented +fun:wmemset=uninstrumented +fun:wordexp=uninstrumented +fun:wordfree=uninstrumented +fun:wprintf=uninstrumented +fun:write=uninstrumented +fun:writeColdStartFile=uninstrumented +fun:writev=uninstrumented +fun:wscanf=uninstrumented +fun:xdecrypt=uninstrumented +fun:xdr_accepted_reply=uninstrumented +fun:xdr_array=uninstrumented +fun:xdr_authdes_cred=uninstrumented +fun:xdr_authdes_verf=uninstrumented +fun:xdr_authunix_parms=uninstrumented +fun:xdr_bool=uninstrumented +fun:xdr_bytes=uninstrumented +fun:xdr_callhdr=uninstrumented +fun:xdr_callmsg=uninstrumented +fun:xdr_cback_data=uninstrumented +fun:xdr_char=uninstrumented +fun:xdr_cryptkeyarg=uninstrumented +fun:xdr_cryptkeyarg2=uninstrumented +fun:xdr_cryptkeyres=uninstrumented +fun:xdr_des_block=uninstrumented +fun:xdr_domainname=uninstrumented +fun:xdr_double=uninstrumented +fun:xdr_enum=uninstrumented +fun:xdr_float=uninstrumented +fun:xdr_free=uninstrumented +fun:xdr_getcredres=uninstrumented +fun:xdr_hyper=uninstrumented +fun:xdr_int=uninstrumented +fun:xdr_int16_t=uninstrumented +fun:xdr_int32_t=uninstrumented +fun:xdr_int64_t=uninstrumented +fun:xdr_int8_t=uninstrumented +fun:xdr_key_netstarg=uninstrumented +fun:xdr_key_netstres=uninstrumented +fun:xdr_keybuf=uninstrumented +fun:xdr_keydat=uninstrumented +fun:xdr_keystatus=uninstrumented +fun:xdr_long=uninstrumented +fun:xdr_longlong_t=uninstrumented +fun:xdr_mapname=uninstrumented +fun:xdr_netnamestr=uninstrumented +fun:xdr_netobj=uninstrumented +fun:xdr_obj_p=uninstrumented +fun:xdr_opaque=uninstrumented +fun:xdr_opaque_auth=uninstrumented +fun:xdr_peername=uninstrumented +fun:xdr_pmap=uninstrumented +fun:xdr_pmaplist=uninstrumented +fun:xdr_pointer=uninstrumented +fun:xdr_quad_t=uninstrumented +fun:xdr_reference=uninstrumented +fun:xdr_rejected_reply=uninstrumented +fun:xdr_replymsg=uninstrumented +fun:xdr_rmtcall_args=uninstrumented +fun:xdr_rmtcallres=uninstrumented +fun:xdr_short=uninstrumented +fun:xdr_sizeof=uninstrumented +fun:xdr_string=uninstrumented +fun:xdr_u_char=uninstrumented +fun:xdr_u_hyper=uninstrumented +fun:xdr_u_int=uninstrumented +fun:xdr_u_long=uninstrumented +fun:xdr_u_longlong_t=uninstrumented +fun:xdr_u_quad_t=uninstrumented +fun:xdr_u_short=uninstrumented +fun:xdr_uint16_t=uninstrumented +fun:xdr_uint32_t=uninstrumented +fun:xdr_uint64_t=uninstrumented +fun:xdr_uint8_t=uninstrumented +fun:xdr_union=uninstrumented +fun:xdr_unixcred=uninstrumented +fun:xdr_valdat=uninstrumented +fun:xdr_vector=uninstrumented +fun:xdr_void=uninstrumented +fun:xdr_wrapstring=uninstrumented +fun:xdr_yp_buf=uninstrumented +fun:xdr_ypall=uninstrumented +fun:xdr_ypbind_binding=uninstrumented +fun:xdr_ypbind_resp=uninstrumented +fun:xdr_ypbind_resptype=uninstrumented +fun:xdr_ypbind_setdom=uninstrumented +fun:xdr_ypdelete_args=uninstrumented +fun:xdr_ypmap_parms=uninstrumented +fun:xdr_ypmaplist=uninstrumented +fun:xdr_yppush_status=uninstrumented +fun:xdr_yppushresp_xfr=uninstrumented +fun:xdr_ypreq_key=uninstrumented +fun:xdr_ypreq_nokey=uninstrumented +fun:xdr_ypreq_xfr=uninstrumented +fun:xdr_ypresp_all=uninstrumented +fun:xdr_ypresp_key_val=uninstrumented +fun:xdr_ypresp_maplist=uninstrumented +fun:xdr_ypresp_master=uninstrumented +fun:xdr_ypresp_order=uninstrumented +fun:xdr_ypresp_val=uninstrumented +fun:xdr_ypresp_xfr=uninstrumented +fun:xdr_ypstat=uninstrumented +fun:xdr_ypupdate_args=uninstrumented +fun:xdr_ypxfrstat=uninstrumented +fun:xdrmem_create=uninstrumented +fun:xdrrec_create=uninstrumented +fun:xdrrec_endofrecord=uninstrumented +fun:xdrrec_eof=uninstrumented +fun:xdrrec_skiprecord=uninstrumented +fun:xdrstdio_create=uninstrumented +fun:xencrypt=uninstrumented +fun:xprt_register=uninstrumented +fun:xprt_unregister=uninstrumented +fun:y0=uninstrumented +fun:y0f=uninstrumented +fun:y0l=uninstrumented +fun:y1=uninstrumented +fun:y1f=uninstrumented +fun:y1l=uninstrumented +fun:yn=uninstrumented +fun:ynf=uninstrumented +fun:ynl=uninstrumented +fun:yp_all=uninstrumented +fun:yp_bind=uninstrumented +fun:yp_first=uninstrumented +fun:yp_get_default_domain=uninstrumented +fun:yp_maplist=uninstrumented +fun:yp_master=uninstrumented +fun:yp_match=uninstrumented +fun:yp_next=uninstrumented +fun:yp_order=uninstrumented +fun:yp_unbind=uninstrumented +fun:yp_update=uninstrumented +fun:ypbinderr_string=uninstrumented +fun:yperr_string=uninstrumented +fun:ypprot_err=uninstrumented diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/CMakeLists.txt b/projects/compiler-rt/lib/dfsan/lit_tests/CMakeLists.txt new file mode 100644 index 00000000..d7c5c82a --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/CMakeLists.txt @@ -0,0 +1,21 @@ +set(DFSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) +set(DFSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + # Run DFSan tests only if we're sure we may produce working binaries. + set(DFSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + ${DFSAN_RUNTIME_LIBRARIES} + dfsan_abilist) + set(DFSAN_TEST_PARAMS + dfsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + add_lit_testsuite(check-dfsan "Running the DataFlowSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS ${DFSAN_TEST_PARAMS} + DEPENDS ${DFSAN_TEST_DEPS}) + set_target_properties(check-dfsan PROPERTIES FOLDER "DFSan tests") +endif() diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/Inputs/flags_abilist.txt b/projects/compiler-rt/lib/dfsan/lit_tests/Inputs/flags_abilist.txt new file mode 100644 index 00000000..94b1fa29 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/Inputs/flags_abilist.txt @@ -0,0 +1,10 @@ +fun:f=uninstrumented + +fun:main=uninstrumented +fun:main=discard + +fun:dfsan_create_label=uninstrumented +fun:dfsan_create_label=discard + +fun:dfsan_set_label=uninstrumented +fun:dfsan_set_label=discard diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/basic.c b/projects/compiler-rt/lib/dfsan/lit_tests/basic.c new file mode 100644 index 00000000..b566c927 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/basic.c @@ -0,0 +1,21 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests that labels are propagated through loads and stores. + +#include +#include + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + dfsan_label new_label = dfsan_get_label(i); + assert(i_label == new_label); + + dfsan_label read_label = dfsan_read_label(&i, sizeof(i)); + assert(i_label == read_label); + + return 0; +} diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/custom.c b/projects/compiler-rt/lib/dfsan/lit_tests/custom.c new file mode 100644 index 00000000..c9fa9356 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/custom.c @@ -0,0 +1,154 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests custom implementations of various libc functions. + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *ptcb(void *p) { + assert(p == (void *)1); + assert(dfsan_get_label((uintptr_t)p) == 0); + return (void *)2; +} + +int dlcb(struct dl_phdr_info *info, size_t size, void *data) { + assert(data == (void *)3); + assert(dfsan_get_label((uintptr_t)info) == 0); + assert(dfsan_get_label(size) == 0); + assert(dfsan_get_label((uintptr_t)data) == 0); + return 0; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + + struct stat s; + s.st_dev = i; + int rv = stat("/", &s); + assert(rv == 0); + assert(dfsan_get_label(s.st_dev) == 0); + + s.st_dev = i; + rv = stat("/nonexistent", &s); + assert(rv == -1); + assert(dfsan_get_label(s.st_dev) == i_label); + + int fd = open("/dev/zero", O_RDONLY); + s.st_dev = i; + rv = fstat(fd, &s); + assert(rv == 0); + assert(dfsan_get_label(s.st_dev) == 0); + + char str1[] = "str1", str2[] = "str2"; + dfsan_set_label(i_label, &str1[3], 1); + dfsan_set_label(j_label, &str2[3], 1); + + rv = memcmp(str1, str2, sizeof(str1)); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + char strc[sizeof(str1)]; + memcpy(strc, str1, sizeof(str1)); + assert(dfsan_get_label(strc[0]) == 0); + assert(dfsan_get_label(strc[3]) == i_label); + + memset(strc, j, sizeof(strc)); + assert(dfsan_get_label(strc[0]) == j_label); + assert(dfsan_get_label(strc[1]) == j_label); + assert(dfsan_get_label(strc[2]) == j_label); + assert(dfsan_get_label(strc[3]) == j_label); + assert(dfsan_get_label(strc[4]) == j_label); + + rv = strcmp(str1, str2); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + char *strd = strdup(str1); + assert(dfsan_get_label(strd[0]) == 0); + assert(dfsan_get_label(strd[3]) == i_label); + free(strd); + + rv = strncmp(str1, str2, sizeof(str1)); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + rv = strncmp(str1, str2, 3); + assert(rv == 0); + assert(dfsan_get_label(rv) == 0); + + str1[0] = 'S'; + + rv = strncasecmp(str1, str2, sizeof(str1)); + assert(rv < 0); + assert(dfsan_get_label(rv) == dfsan_union(i_label, j_label)); + + rv = strncasecmp(str1, str2, 3); + assert(rv == 0); + assert(dfsan_get_label(rv) == 0); + + char *crv = strchr(str1, 'r'); + assert(crv == &str1[2]); + assert(dfsan_get_label((uintptr_t)crv) == 0); + + crv = strchr(str1, '1'); + assert(crv == &str1[3]); + assert(dfsan_get_label((uintptr_t)crv) == i_label); + + crv = strchr(str1, 'x'); + assert(crv == 0); + assert(dfsan_get_label((uintptr_t)crv) == i_label); + + // With any luck this sequence of calls will cause calloc to return the same + // pointer both times. This is probably the best we can do to test this + // function. + crv = calloc(4096, 1); + assert(dfsan_get_label(crv[0]) == 0); + free(crv); + + crv = calloc(4096, 1); + assert(dfsan_get_label(crv[0]) == 0); + free(crv); + + char buf[16]; + buf[0] = i; + buf[15] = j; + rv = read(fd, buf, sizeof(buf)); + assert(rv == sizeof(buf)); + assert(dfsan_get_label(buf[0]) == 0); + assert(dfsan_get_label(buf[15]) == 0); + + close(fd); + fd = open("/bin/sh", O_RDONLY); + buf[0] = i; + buf[15] = j; + rv = pread(fd, buf, sizeof(buf), 0); + assert(rv == sizeof(buf)); + assert(dfsan_get_label(buf[0]) == 0); + assert(dfsan_get_label(buf[15]) == 0); + + pthread_t pt; + pthread_create(&pt, 0, ptcb, (void *)1); + void *cbrv; + pthread_join(pt, &cbrv); + assert(cbrv == (void *)2); + + dl_iterate_phdr(dlcb, (void *)3); + + return 0; +} diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/flags.c b/projects/compiler-rt/lib/dfsan/lit_tests/flags.c new file mode 100644 index 00000000..5cf970dc --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/flags.c @@ -0,0 +1,24 @@ +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_unimplemented=0 %t 2>&1 | count 0 +// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_nonzero_labels=1 %t 2>&1 | FileCheck --check-prefix=CHECK-NONZERO %s + +// Tests that flags work correctly. + +#include + +int f(int i) { + return i; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + // CHECK: WARNING: DataFlowSanitizer: call to uninstrumented function f + // CHECK-NOT: WARNING: DataFlowSanitizer: saw nonzero label + // CHECK-NONZERO: WARNING: DataFlowSanitizer: saw nonzero label + f(i); + + return 0; +} diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/fncall.c b/projects/compiler-rt/lib/dfsan/lit_tests/fncall.c new file mode 100644 index 00000000..15b77bd6 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/fncall.c @@ -0,0 +1,26 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests that labels are propagated through function calls. + +#include +#include + +int f(int x) { + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + return x + j; +} + +int main(void) { + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + dfsan_label ij_label = dfsan_get_label(f(i)); + assert(dfsan_has_label(ij_label, i_label)); + assert(dfsan_has_label_with_desc(ij_label, "j")); + + return 0; +} diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/lit.cfg b/projects/compiler-rt/lib/dfsan/lit_tests/lit.cfg new file mode 100644 index 00000000..19bc9769 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/lit.cfg @@ -0,0 +1,69 @@ +# -*- Python -*- + +import os + +import lit.util + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'DataFlowSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +def DisplayNoConfigMessage(): + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-dfsan") + +# Figure out LLVM source root. +llvm_src_root = getattr(config, 'llvm_src_root', None) +if llvm_src_root is None: + # We probably haven't loaded the site-specific configuration: the user + # is likely trying to run a test file directly, and the site configuration + # wasn't created by the build system. + dfsan_site_cfg = lit_config.params.get('dfsan_site_config', None) + if (dfsan_site_cfg) and (os.path.exists(dfsan_site_cfg)): + lit_config.load_config(config, dfsan_site_cfg) + raise SystemExit + + # Try to guess the location of site-specific configuration using llvm-config + # util that can point where the build tree is. + llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) + if not llvm_config: + DisplayNoConfigMessage() + + # Find out the presumed location of generated site config. + llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() + dfsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", + "lib", "dfsan", "lit_tests", "lit.site.cfg") + if (not dfsan_site_cfg) or (not os.path.exists(dfsan_site_cfg)): + DisplayNoConfigMessage() + + lit_config.load_config(config, dfsan_site_cfg) + raise SystemExit + +# Setup default compiler flags used with -fsanitize=dataflow option. +clang_dfsan_cflags = ["-fsanitize=dataflow"] +clang_dfsan_cxxflags = ["--driver-mode=g++ "] + clang_dfsan_cflags +config.substitutions.append( ("%clang_dfsan ", + " ".join([config.clang] + clang_dfsan_cflags) + + " ") ) +config.substitutions.append( ("%clangxx_dfsan ", + " ".join([config.clang] + clang_dfsan_cxxflags) + + " ") ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# DataFlowSanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/lit.site.cfg.in b/projects/compiler-rt/lib/dfsan/lit_tests/lit.site.cfg.in new file mode 100644 index 00000000..0cf6d6b5 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/lit.site.cfg.in @@ -0,0 +1,5 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@DFSAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/projects/compiler-rt/lib/dfsan/lit_tests/propagate.c b/projects/compiler-rt/lib/dfsan/lit_tests/propagate.c new file mode 100644 index 00000000..86d182b8 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/lit_tests/propagate.c @@ -0,0 +1,47 @@ +// RUN: %clang_dfsan -m64 %s -o %t && %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %t + +// Tests that labels are propagated through computation and that union labels +// are properly created. + +#include +#include + +int main(void) { + assert(dfsan_union(0, 0) == 0); + + int i = 1; + dfsan_label i_label = dfsan_create_label("i", 0); + dfsan_set_label(i_label, &i, sizeof(i)); + + int j = 2; + dfsan_label j_label = dfsan_create_label("j", 0); + dfsan_set_label(j_label, &j, sizeof(j)); + + int k = 3; + dfsan_label k_label = dfsan_create_label("k", 0); + dfsan_set_label(k_label, &k, sizeof(k)); + + int k2 = 4; + dfsan_set_label(k_label, &k2, sizeof(k2)); + + dfsan_label ij_label = dfsan_get_label(i + j); + assert(dfsan_has_label(ij_label, i_label)); + assert(dfsan_has_label(ij_label, j_label)); + assert(!dfsan_has_label(ij_label, k_label)); + // Test uniquing. + assert(dfsan_union(i_label, j_label) == ij_label); + assert(dfsan_union(j_label, i_label) == ij_label); + + dfsan_label ijk_label = dfsan_get_label(i + j + k); + assert(dfsan_has_label(ijk_label, i_label)); + assert(dfsan_has_label(ijk_label, j_label)); + assert(dfsan_has_label(ijk_label, k_label)); + + assert(dfsan_get_label(k + k2) == k_label); + + struct { int i, j; } s = { i, j }; + assert(dfsan_read_label(&s, sizeof(s)) == ij_label); + + return 0; +} diff --git a/projects/compiler-rt/lib/dfsan/scripts/build-libc-list.py b/projects/compiler-rt/lib/dfsan/scripts/build-libc-list.py new file mode 100755 index 00000000..8f6b8d51 --- /dev/null +++ b/projects/compiler-rt/lib/dfsan/scripts/build-libc-list.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +#===- lib/dfsan/scripts/build-libc-list.py ---------------------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# The purpose of this script is to identify every function symbol in a set of +# libraries (in this case, libc and libgcc) so that they can be marked as +# uninstrumented, thus allowing the instrumentation pass to treat calls to those +# functions correctly. + +import os +import subprocess +import sys +from optparse import OptionParser + +def defined_function_list(object): + functions = [] + readelf_proc = subprocess.Popen(['readelf', '-s', '-W', object], + stdout=subprocess.PIPE) + readelf = readelf_proc.communicate()[0].split('\n') + if readelf_proc.returncode != 0: + raise subprocess.CalledProcessError(readelf_proc.returncode, 'readelf') + for line in readelf: + if (line[31:35] == 'FUNC' or line[31:36] == 'IFUNC') and \ + line[55:58] != 'UND': + function_name = line[59:].split('@')[0] + functions.append(function_name) + return functions + +p = OptionParser() +p.add_option('--lib', metavar='PATH', + help='path to lib directory to use', + default='/lib/x86_64-linux-gnu') +p.add_option('--usrlib', metavar='PATH', + help='path to usr/lib directory to use', + default='/usr/lib/x86_64-linux-gnu') +p.add_option('--gcclib', metavar='PATH', + help='path to gcc lib directory to use', + default='/usr/lib/gcc/x86_64-linux-gnu/4.6') +p.add_option('--with-libstdcxx', action='store_true', + dest='with_libstdcxx', + help='include libstdc++ in the list (inadvisable)') +(options, args) = p.parse_args() + +libs = [os.path.join(options.lib, name) for name in + ['ld-linux-x86-64.so.2', + 'libanl.so.1', + 'libBrokenLocale.so.1', + 'libcidn.so.1', + 'libcrypt.so.1', + 'libc.so.6', + 'libdl.so.2', + 'libm.so.6', + 'libnsl.so.1', + 'libpthread.so.0', + 'libresolv.so.2', + 'librt.so.1', + 'libthread_db.so.1', + 'libutil.so.1']] +libs += [os.path.join(options.usrlib, name) for name in + ['libc_nonshared.a', + 'libpthread_nonshared.a']] +gcclibs = ['libgcc.a', + 'libgcc_s.so'] +if options.with_libstdcxx: + gcclibs += ['libstdc++.so'] +libs += [os.path.join(options.gcclib, name) for name in gcclibs] + +functions = [] +for l in libs: + if os.path.exists(l): + functions += defined_function_list(l) + else: + print >> sys.stderr, 'warning: library %s not found' % l + +functions = list(set(functions)) +functions.sort() + +for f in functions: + print 'fun:%s=uninstrumented' % f diff --git a/projects/compiler-rt/lib/divdc3.c b/projects/compiler-rt/lib/divdc3.c new file mode 100644 index 00000000..cfbc498e --- /dev/null +++ b/projects/compiler-rt/lib/divdc3.c @@ -0,0 +1,60 @@ +/* ===-- divdc3.c - Implement __divdc3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divdc3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" +#include "int_math.h" + +/* Returns: the quotient of (a + ib) / (c + id) */ + +double _Complex +__divdc3(double __a, double __b, double __c, double __d) +{ + int __ilogbw = 0; + double __logbw = crt_logb(crt_fmax(crt_fabs(__c), crt_fabs(__d))); + if (crt_isfinite(__logbw)) + { + __ilogbw = (int)__logbw; + __c = crt_scalbn(__c, -__ilogbw); + __d = crt_scalbn(__d, -__ilogbw); + } + double __denom = __c * __c + __d * __d; + double _Complex z; + __real__ z = crt_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw); + __imag__ z = crt_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw); + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + { + if ((__denom == 0.0) && (!crt_isnan(__a) || !crt_isnan(__b))) + { + __real__ z = crt_copysign(CRT_INFINITY, __c) * __a; + __imag__ z = crt_copysign(CRT_INFINITY, __c) * __b; + } + else if ((crt_isinf(__a) || crt_isinf(__b)) && + crt_isfinite(__c) && crt_isfinite(__d)) + { + __a = crt_copysign(crt_isinf(__a) ? 1.0 : 0.0, __a); + __b = crt_copysign(crt_isinf(__b) ? 1.0 : 0.0, __b); + __real__ z = CRT_INFINITY * (__a * __c + __b * __d); + __imag__ z = CRT_INFINITY * (__b * __c - __a * __d); + } + else if (crt_isinf(__logbw) && __logbw > 0.0 && + crt_isfinite(__a) && crt_isfinite(__b)) + { + __c = crt_copysign(crt_isinf(__c) ? 1.0 : 0.0, __c); + __d = crt_copysign(crt_isinf(__d) ? 1.0 : 0.0, __d); + __real__ z = 0.0 * (__a * __c + __b * __d); + __imag__ z = 0.0 * (__b * __c - __a * __d); + } + } + return z; +} diff --git a/projects/compiler-rt/lib/divdf3.c b/projects/compiler-rt/lib/divdf3.c new file mode 100644 index 00000000..efce6bb4 --- /dev/null +++ b/projects/compiler-rt/lib/divdf3.c @@ -0,0 +1,184 @@ +//===-- lib/divdf3.c - Double-precision division ------------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float division +// with the IEEE-754 default rounding (to nearest, ties to even). +// +// For simplicity, this implementation currently flushes denormals to zero. +// It should be a fairly straightforward exercise to implement gradual +// underflow with correct rounding. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(ddiv, divdf3) + +fp_t __divdf3(fp_t a, fp_t b) { + + const unsigned int aExponent = toRep(a) >> significandBits & maxExponent; + const unsigned int bExponent = toRep(b) >> significandBits & maxExponent; + const rep_t quotientSign = (toRep(a) ^ toRep(b)) & signBit; + + rep_t aSignificand = toRep(a) & significandMask; + rep_t bSignificand = toRep(b) & significandMask; + int scale = 0; + + // Detect if a or b is zero, denormal, infinity, or NaN. + if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) { + + const rep_t aAbs = toRep(a) & absMask; + const rep_t bAbs = toRep(b) & absMask; + + // NaN / anything = qNaN + if (aAbs > infRep) return fromRep(toRep(a) | quietBit); + // anything / NaN = qNaN + if (bAbs > infRep) return fromRep(toRep(b) | quietBit); + + if (aAbs == infRep) { + // infinity / infinity = NaN + if (bAbs == infRep) return fromRep(qnanRep); + // infinity / anything else = +/- infinity + else return fromRep(aAbs | quotientSign); + } + + // anything else / infinity = +/- 0 + if (bAbs == infRep) return fromRep(quotientSign); + + if (!aAbs) { + // zero / zero = NaN + if (!bAbs) return fromRep(qnanRep); + // zero / anything else = +/- zero + else return fromRep(quotientSign); + } + // anything else / zero = +/- infinity + if (!bAbs) return fromRep(infRep | quotientSign); + + // one or both of a or b is denormal, the other (if applicable) is a + // normal number. Renormalize one or both of a and b, and set scale to + // include the necessary exponent adjustment. + if (aAbs < implicitBit) scale += normalize(&aSignificand); + if (bAbs < implicitBit) scale -= normalize(&bSignificand); + } + + // Or in the implicit significand bit. (If we fell through from the + // denormal path it was already set by normalize( ), but setting it twice + // won't hurt anything.) + aSignificand |= implicitBit; + bSignificand |= implicitBit; + int quotientExponent = aExponent - bExponent + scale; + + // Align the significand of b as a Q31 fixed-point number in the range + // [1, 2.0) and get a Q32 approximate reciprocal using a small minimax + // polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This + // is accurate to about 3.5 binary digits. + const uint32_t q31b = bSignificand >> 21; + uint32_t recip32 = UINT32_C(0x7504f333) - q31b; + + // Now refine the reciprocal estimate using a Newton-Raphson iteration: + // + // x1 = x0 * (2 - x0 * b) + // + // This doubles the number of correct binary digits in the approximation + // with each iteration, so after three iterations, we have about 28 binary + // digits of accuracy. + uint32_t correction32; + correction32 = -((uint64_t)recip32 * q31b >> 32); + recip32 = (uint64_t)recip32 * correction32 >> 31; + correction32 = -((uint64_t)recip32 * q31b >> 32); + recip32 = (uint64_t)recip32 * correction32 >> 31; + correction32 = -((uint64_t)recip32 * q31b >> 32); + recip32 = (uint64_t)recip32 * correction32 >> 31; + + // recip32 might have overflowed to exactly zero in the preceeding + // computation if the high word of b is exactly 1.0. This would sabotage + // the full-width final stage of the computation that follows, so we adjust + // recip32 downward by one bit. + recip32--; + + // We need to perform one more iteration to get us to 56 binary digits; + // The last iteration needs to happen with extra precision. + const uint32_t q63blo = bSignificand << 11; + uint64_t correction, reciprocal; + correction = -((uint64_t)recip32*q31b + ((uint64_t)recip32*q63blo >> 32)); + uint32_t cHi = correction >> 32; + uint32_t cLo = correction; + reciprocal = (uint64_t)recip32*cHi + ((uint64_t)recip32*cLo >> 32); + + // We already adjusted the 32-bit estimate, now we need to adjust the final + // 64-bit reciprocal estimate downward to ensure that it is strictly smaller + // than the infinitely precise exact reciprocal. Because the computation + // of the Newton-Raphson step is truncating at every step, this adjustment + // is small; most of the work is already done. + reciprocal -= 2; + + // The numerical reciprocal is accurate to within 2^-56, lies in the + // interval [0.5, 1.0), and is strictly smaller than the true reciprocal + // of b. Multiplying a by this reciprocal thus gives a numerical q = a/b + // in Q53 with the following properties: + // + // 1. q < a/b + // 2. q is in the interval [0.5, 2.0) + // 3. the error in q is bounded away from 2^-53 (actually, we have a + // couple of bits to spare, but this is all we need). + + // We need a 64 x 64 multiply high to compute q, which isn't a basic + // operation in C, so we need to be a little bit fussy. + rep_t quotient, quotientLo; + wideMultiply(aSignificand << 2, reciprocal, "ient, "ientLo); + + // Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0). + // In either case, we are going to compute a residual of the form + // + // r = a - q*b + // + // We know from the construction of q that r satisfies: + // + // 0 <= r < ulp(q)*b + // + // if r is greater than 1/2 ulp(q)*b, then q rounds up. Otherwise, we + // already have the correct result. The exact halfway case cannot occur. + // We also take this time to right shift quotient if it falls in the [1,2) + // range and adjust the exponent accordingly. + rep_t residual; + if (quotient < (implicitBit << 1)) { + residual = (aSignificand << 53) - quotient * bSignificand; + quotientExponent--; + } else { + quotient >>= 1; + residual = (aSignificand << 52) - quotient * bSignificand; + } + + const int writtenExponent = quotientExponent + exponentBias; + + if (writtenExponent >= maxExponent) { + // If we have overflowed the exponent, return infinity. + return fromRep(infRep | quotientSign); + } + + else if (writtenExponent < 1) { + // Flush denormals to zero. In the future, it would be nice to add + // code to round them correctly. + return fromRep(quotientSign); + } + + else { + const bool round = (residual << 1) > bSignificand; + // Clear the implicit bit + rep_t absResult = quotient & significandMask; + // Insert the exponent + absResult |= (rep_t)writtenExponent << significandBits; + // Round + absResult += round; + // Insert the sign and return + const double result = fromRep(absResult | quotientSign); + return result; + } +} diff --git a/projects/compiler-rt/lib/divdi3.c b/projects/compiler-rt/lib/divdi3.c new file mode 100644 index 00000000..2c2bcc26 --- /dev/null +++ b/projects/compiler-rt/lib/divdi3.c @@ -0,0 +1,31 @@ +/* ===-- divdi3.c - Implement __divdi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +du_int COMPILER_RT_ABI __udivmoddi4(du_int a, du_int b, du_int* rem); + +/* Returns: a / b */ + +COMPILER_RT_ABI di_int +__divdi3(di_int a, di_int b) +{ + const int bits_in_dword_m1 = (int)(sizeof(di_int) * CHAR_BIT) - 1; + di_int s_a = a >> bits_in_dword_m1; /* s_a = a < 0 ? -1 : 0 */ + di_int s_b = b >> bits_in_dword_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + s_a ^= s_b; /*sign of quotient */ + return (__udivmoddi4(a, b, (du_int*)0) ^ s_a) - s_a; /* negate if s_a == -1 */ +} diff --git a/projects/compiler-rt/lib/divmoddi4.c b/projects/compiler-rt/lib/divmoddi4.c new file mode 100644 index 00000000..2fe2b481 --- /dev/null +++ b/projects/compiler-rt/lib/divmoddi4.c @@ -0,0 +1,27 @@ +/*===-- divmoddi4.c - Implement __divmoddi4 --------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divmoddi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +extern COMPILER_RT_ABI di_int __divdi3(di_int a, di_int b); + +/* Returns: a / b, *rem = a % b */ + +COMPILER_RT_ABI di_int +__divmoddi4(di_int a, di_int b, di_int* rem) +{ + di_int d = __divdi3(a,b); + *rem = a - (d*b); + return d; +} diff --git a/projects/compiler-rt/lib/divmodsi4.c b/projects/compiler-rt/lib/divmodsi4.c new file mode 100644 index 00000000..c7f7b1a7 --- /dev/null +++ b/projects/compiler-rt/lib/divmodsi4.c @@ -0,0 +1,30 @@ +/*===-- divmodsi4.c - Implement __divmodsi4 --------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divmodsi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +extern COMPILER_RT_ABI si_int __divsi3(si_int a, si_int b); + + +/* Returns: a / b, *rem = a % b */ + +COMPILER_RT_ABI si_int +__divmodsi4(si_int a, si_int b, si_int* rem) +{ + si_int d = __divsi3(a,b); + *rem = a - (d*b); + return d; +} + + diff --git a/projects/compiler-rt/lib/divsc3.c b/projects/compiler-rt/lib/divsc3.c new file mode 100644 index 00000000..caa0c407 --- /dev/null +++ b/projects/compiler-rt/lib/divsc3.c @@ -0,0 +1,60 @@ +/*===-- divsc3.c - Implement __divsc3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divsc3 for the compiler_rt library. + * + *===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" +#include "int_math.h" + +/* Returns: the quotient of (a + ib) / (c + id) */ + +float _Complex +__divsc3(float __a, float __b, float __c, float __d) +{ + int __ilogbw = 0; + float __logbw = crt_logbf(crt_fmaxf(crt_fabsf(__c), crt_fabsf(__d))); + if (crt_isfinite(__logbw)) + { + __ilogbw = (int)__logbw; + __c = crt_scalbnf(__c, -__ilogbw); + __d = crt_scalbnf(__d, -__ilogbw); + } + float __denom = __c * __c + __d * __d; + float _Complex z; + __real__ z = crt_scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw); + __imag__ z = crt_scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw); + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + { + if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b))) + { + __real__ z = crt_copysignf(CRT_INFINITY, __c) * __a; + __imag__ z = crt_copysignf(CRT_INFINITY, __c) * __b; + } + else if ((crt_isinf(__a) || crt_isinf(__b)) && + crt_isfinite(__c) && crt_isfinite(__d)) + { + __a = crt_copysignf(crt_isinf(__a) ? 1 : 0, __a); + __b = crt_copysignf(crt_isinf(__b) ? 1 : 0, __b); + __real__ z = CRT_INFINITY * (__a * __c + __b * __d); + __imag__ z = CRT_INFINITY * (__b * __c - __a * __d); + } + else if (crt_isinf(__logbw) && __logbw > 0 && + crt_isfinite(__a) && crt_isfinite(__b)) + { + __c = crt_copysignf(crt_isinf(__c) ? 1 : 0, __c); + __d = crt_copysignf(crt_isinf(__d) ? 1 : 0, __d); + __real__ z = 0 * (__a * __c + __b * __d); + __imag__ z = 0 * (__b * __c - __a * __d); + } + } + return z; +} diff --git a/projects/compiler-rt/lib/divsf3.c b/projects/compiler-rt/lib/divsf3.c new file mode 100644 index 00000000..c91c648f --- /dev/null +++ b/projects/compiler-rt/lib/divsf3.c @@ -0,0 +1,168 @@ +//===-- lib/divsf3.c - Single-precision division ------------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float division +// with the IEEE-754 default rounding (to nearest, ties to even). +// +// For simplicity, this implementation currently flushes denormals to zero. +// It should be a fairly straightforward exercise to implement gradual +// underflow with correct rounding. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(fdiv, divsf3) + +fp_t __divsf3(fp_t a, fp_t b) { + + const unsigned int aExponent = toRep(a) >> significandBits & maxExponent; + const unsigned int bExponent = toRep(b) >> significandBits & maxExponent; + const rep_t quotientSign = (toRep(a) ^ toRep(b)) & signBit; + + rep_t aSignificand = toRep(a) & significandMask; + rep_t bSignificand = toRep(b) & significandMask; + int scale = 0; + + // Detect if a or b is zero, denormal, infinity, or NaN. + if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) { + + const rep_t aAbs = toRep(a) & absMask; + const rep_t bAbs = toRep(b) & absMask; + + // NaN / anything = qNaN + if (aAbs > infRep) return fromRep(toRep(a) | quietBit); + // anything / NaN = qNaN + if (bAbs > infRep) return fromRep(toRep(b) | quietBit); + + if (aAbs == infRep) { + // infinity / infinity = NaN + if (bAbs == infRep) return fromRep(qnanRep); + // infinity / anything else = +/- infinity + else return fromRep(aAbs | quotientSign); + } + + // anything else / infinity = +/- 0 + if (bAbs == infRep) return fromRep(quotientSign); + + if (!aAbs) { + // zero / zero = NaN + if (!bAbs) return fromRep(qnanRep); + // zero / anything else = +/- zero + else return fromRep(quotientSign); + } + // anything else / zero = +/- infinity + if (!bAbs) return fromRep(infRep | quotientSign); + + // one or both of a or b is denormal, the other (if applicable) is a + // normal number. Renormalize one or both of a and b, and set scale to + // include the necessary exponent adjustment. + if (aAbs < implicitBit) scale += normalize(&aSignificand); + if (bAbs < implicitBit) scale -= normalize(&bSignificand); + } + + // Or in the implicit significand bit. (If we fell through from the + // denormal path it was already set by normalize( ), but setting it twice + // won't hurt anything.) + aSignificand |= implicitBit; + bSignificand |= implicitBit; + int quotientExponent = aExponent - bExponent + scale; + + // Align the significand of b as a Q31 fixed-point number in the range + // [1, 2.0) and get a Q32 approximate reciprocal using a small minimax + // polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This + // is accurate to about 3.5 binary digits. + uint32_t q31b = bSignificand << 8; + uint32_t reciprocal = UINT32_C(0x7504f333) - q31b; + + // Now refine the reciprocal estimate using a Newton-Raphson iteration: + // + // x1 = x0 * (2 - x0 * b) + // + // This doubles the number of correct binary digits in the approximation + // with each iteration, so after three iterations, we have about 28 binary + // digits of accuracy. + uint32_t correction; + correction = -((uint64_t)reciprocal * q31b >> 32); + reciprocal = (uint64_t)reciprocal * correction >> 31; + correction = -((uint64_t)reciprocal * q31b >> 32); + reciprocal = (uint64_t)reciprocal * correction >> 31; + correction = -((uint64_t)reciprocal * q31b >> 32); + reciprocal = (uint64_t)reciprocal * correction >> 31; + + // Exhaustive testing shows that the error in reciprocal after three steps + // is in the interval [-0x1.f58108p-31, 0x1.d0e48cp-29], in line with our + // expectations. We bump the reciprocal by a tiny value to force the error + // to be strictly positive (in the range [0x1.4fdfp-37,0x1.287246p-29], to + // be specific). This also causes 1/1 to give a sensible approximation + // instead of zero (due to overflow). + reciprocal -= 2; + + // The numerical reciprocal is accurate to within 2^-28, lies in the + // interval [0x1.000000eep-1, 0x1.fffffffcp-1], and is strictly smaller + // than the true reciprocal of b. Multiplying a by this reciprocal thus + // gives a numerical q = a/b in Q24 with the following properties: + // + // 1. q < a/b + // 2. q is in the interval [0x1.000000eep-1, 0x1.fffffffcp0) + // 3. the error in q is at most 2^-24 + 2^-27 -- the 2^24 term comes + // from the fact that we truncate the product, and the 2^27 term + // is the error in the reciprocal of b scaled by the maximum + // possible value of a. As a consequence of this error bound, + // either q or nextafter(q) is the correctly rounded + rep_t quotient = (uint64_t)reciprocal*(aSignificand << 1) >> 32; + + // Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0). + // In either case, we are going to compute a residual of the form + // + // r = a - q*b + // + // We know from the construction of q that r satisfies: + // + // 0 <= r < ulp(q)*b + // + // if r is greater than 1/2 ulp(q)*b, then q rounds up. Otherwise, we + // already have the correct result. The exact halfway case cannot occur. + // We also take this time to right shift quotient if it falls in the [1,2) + // range and adjust the exponent accordingly. + rep_t residual; + if (quotient < (implicitBit << 1)) { + residual = (aSignificand << 24) - quotient * bSignificand; + quotientExponent--; + } else { + quotient >>= 1; + residual = (aSignificand << 23) - quotient * bSignificand; + } + + const int writtenExponent = quotientExponent + exponentBias; + + if (writtenExponent >= maxExponent) { + // If we have overflowed the exponent, return infinity. + return fromRep(infRep | quotientSign); + } + + else if (writtenExponent < 1) { + // Flush denormals to zero. In the future, it would be nice to add + // code to round them correctly. + return fromRep(quotientSign); + } + + else { + const bool round = (residual << 1) > bSignificand; + // Clear the implicit bit + rep_t absResult = quotient & significandMask; + // Insert the exponent + absResult |= (rep_t)writtenExponent << significandBits; + // Round + absResult += round; + // Insert the sign and return + return fromRep(absResult | quotientSign); + } +} diff --git a/projects/compiler-rt/lib/divsi3.c b/projects/compiler-rt/lib/divsi3.c new file mode 100644 index 00000000..cd19de95 --- /dev/null +++ b/projects/compiler-rt/lib/divsi3.c @@ -0,0 +1,39 @@ +/* ===-- divsi3.c - Implement __divsi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divsi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +su_int COMPILER_RT_ABI __udivsi3(su_int n, su_int d); + +/* Returns: a / b */ + +ARM_EABI_FNALIAS(idiv, divsi3) + +COMPILER_RT_ABI si_int +__divsi3(si_int a, si_int b) +{ + const int bits_in_word_m1 = (int)(sizeof(si_int) * CHAR_BIT) - 1; + si_int s_a = a >> bits_in_word_m1; /* s_a = a < 0 ? -1 : 0 */ + si_int s_b = b >> bits_in_word_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + s_a ^= s_b; /* sign of quotient */ + /* + * On CPUs without unsigned hardware division support, + * this calls __udivsi3 (notice the cast to su_int). + * On CPUs with unsigned hardware division support, + * this uses the unsigned division instruction. + */ + return ((su_int)a/(su_int)b ^ s_a) - s_a; /* negate if s_a == -1 */ +} diff --git a/projects/compiler-rt/lib/divti3.c b/projects/compiler-rt/lib/divti3.c new file mode 100644 index 00000000..0242c136 --- /dev/null +++ b/projects/compiler-rt/lib/divti3.c @@ -0,0 +1,35 @@ +/* ===-- divti3.c - Implement __divti3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); + +/* Returns: a / b */ + +ti_int +__divti3(ti_int a, ti_int b) +{ + const int bits_in_tword_m1 = (int)(sizeof(ti_int) * CHAR_BIT) - 1; + ti_int s_a = a >> bits_in_tword_m1; /* s_a = a < 0 ? -1 : 0 */ + ti_int s_b = b >> bits_in_tword_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + s_a ^= s_b; /* sign of quotient */ + return (__udivmodti4(a, b, (tu_int*)0) ^ s_a) - s_a; /* negate if s_a == -1 */ +} + +#endif diff --git a/projects/compiler-rt/lib/divxc3.c b/projects/compiler-rt/lib/divxc3.c new file mode 100644 index 00000000..5f240e95 --- /dev/null +++ b/projects/compiler-rt/lib/divxc3.c @@ -0,0 +1,63 @@ +/* ===-- divxc3.c - Implement __divxc3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divxc3 for the compiler_rt library. + * + */ + +#if !_ARCH_PPC + +#include "int_lib.h" +#include "int_math.h" + +/* Returns: the quotient of (a + ib) / (c + id) */ + +long double _Complex +__divxc3(long double __a, long double __b, long double __c, long double __d) +{ + int __ilogbw = 0; + long double __logbw = crt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d))); + if (crt_isfinite(__logbw)) + { + __ilogbw = (int)__logbw; + __c = crt_scalbnl(__c, -__ilogbw); + __d = crt_scalbnl(__d, -__ilogbw); + } + long double __denom = __c * __c + __d * __d; + long double _Complex z; + __real__ z = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw); + __imag__ z = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw); + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + { + if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b))) + { + __real__ z = crt_copysignl(CRT_INFINITY, __c) * __a; + __imag__ z = crt_copysignl(CRT_INFINITY, __c) * __b; + } + else if ((crt_isinf(__a) || crt_isinf(__b)) && + crt_isfinite(__c) && crt_isfinite(__d)) + { + __a = crt_copysignl(crt_isinf(__a) ? 1 : 0, __a); + __b = crt_copysignl(crt_isinf(__b) ? 1 : 0, __b); + __real__ z = CRT_INFINITY * (__a * __c + __b * __d); + __imag__ z = CRT_INFINITY * (__b * __c - __a * __d); + } + else if (crt_isinf(__logbw) && __logbw > 0 && + crt_isfinite(__a) && crt_isfinite(__b)) + { + __c = crt_copysignl(crt_isinf(__c) ? 1 : 0, __c); + __d = crt_copysignl(crt_isinf(__d) ? 1 : 0, __d); + __real__ z = 0 * (__a * __c + __b * __d); + __imag__ z = 0 * (__b * __c - __a * __d); + } + } + return z; +} + +#endif diff --git a/projects/compiler-rt/lib/enable_execute_stack.c b/projects/compiler-rt/lib/enable_execute_stack.c new file mode 100644 index 00000000..278ca246 --- /dev/null +++ b/projects/compiler-rt/lib/enable_execute_stack.c @@ -0,0 +1,59 @@ +/* ===-- enable_execute_stack.c - Implement __enable_execute_stack ---------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#include + +/* #include "config.h" + * FIXME: CMake - include when cmake system is ready. + * Remove #define HAVE_SYSCONF 1 line. + */ +#define HAVE_SYSCONF 1 + +#ifndef __APPLE__ +#include +#endif /* __APPLE__ */ + +#if __LP64__ + #define TRAMPOLINE_SIZE 48 +#else + #define TRAMPOLINE_SIZE 40 +#endif + +/* + * The compiler generates calls to __enable_execute_stack() when creating + * trampoline functions on the stack for use with nested functions. + * It is expected to mark the page(s) containing the address + * and the next 48 bytes as executable. Since the stack is normally rw- + * that means changing the protection on those page(s) to rwx. + */ + +void __enable_execute_stack(void* addr) +{ + +#if __APPLE__ + /* On Darwin, pagesize is always 4096 bytes */ + const uintptr_t pageSize = 4096; +#elif !defined(HAVE_SYSCONF) +#error "HAVE_SYSCONF not defined! See enable_execute_stack.c" +#else + const uintptr_t pageSize = sysconf(_SC_PAGESIZE); +#endif /* __APPLE__ */ + + const uintptr_t pageAlignMask = ~(pageSize-1); + uintptr_t p = (uintptr_t)addr; + unsigned char* startPage = (unsigned char*)(p & pageAlignMask); + unsigned char* endPage = (unsigned char*)((p+TRAMPOLINE_SIZE+pageSize) & pageAlignMask); + size_t length = endPage - startPage; + (void) mprotect((void *)startPage, length, PROT_READ | PROT_WRITE | PROT_EXEC); +} + + diff --git a/projects/compiler-rt/lib/eprintf.c b/projects/compiler-rt/lib/eprintf.c new file mode 100644 index 00000000..3626dbf8 --- /dev/null +++ b/projects/compiler-rt/lib/eprintf.c @@ -0,0 +1,34 @@ +/* ===---------- eprintf.c - Implements __eprintf --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + */ + + + +#include "int_lib.h" +#include + + +/* + * __eprintf() was used in an old version of . + * It can eventually go away, but it is needed when linking + * .o files built with the old . + * + * It should never be exported from a dylib, so it is marked + * visibility hidden. + */ +#ifndef _WIN32 +__attribute__((visibility("hidden"))) +#endif +void __eprintf(const char* format, const char* assertion_expression, + const char* line, const char* file) +{ + fprintf(stderr, format, assertion_expression, line, file); + fflush(stderr); + compilerrt_abort(); +} diff --git a/projects/compiler-rt/lib/extendsfdf2.c b/projects/compiler-rt/lib/extendsfdf2.c new file mode 100644 index 00000000..91fd2b43 --- /dev/null +++ b/projects/compiler-rt/lib/extendsfdf2.c @@ -0,0 +1,137 @@ +//===-- lib/extendsfdf2.c - single -> double conversion -----------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a fairly generic conversion from a narrower to a wider +// IEEE-754 floating-point type. The constants and types defined following the +// includes below parameterize the conversion. +// +// This routine can be trivially adapted to support conversions from +// half-precision or to quad-precision. It does not support types that don't +// use the usual IEEE-754 interchange formats; specifically, some work would be +// needed to adapt it to (for example) the Intel 80-bit format or PowerPC +// double-double format. +// +// Note please, however, that this implementation is only intended to support +// *widening* operations; if you need to convert to a *narrower* floating-point +// type (e.g. double -> float), then this routine will not do what you want it +// to. +// +// It also requires that integer types at least as large as both formats +// are available on the target platform; this may pose a problem when trying +// to add support for quad on some 32-bit systems, for example. You also may +// run into trouble finding an appropriate CLZ function for wide source types; +// you will likely need to roll your own on some platforms. +// +// Finally, the following assumptions are made: +// +// 1. floating-point types and integer types have the same endianness on the +// target platform +// +// 2. quiet NaNs, if supported, are indicated by the leading bit of the +// significand field being set +// +//===----------------------------------------------------------------------===// + +#include "int_lib.h" + +typedef float src_t; +typedef uint32_t src_rep_t; +#define SRC_REP_C UINT32_C +static const int srcSigBits = 23; +#define src_rep_t_clz __builtin_clz + +typedef double dst_t; +typedef uint64_t dst_rep_t; +#define DST_REP_C UINT64_C +static const int dstSigBits = 52; + +// End of specialization parameters. Two helper routines for conversion to and +// from the representation of floating-point data as integer values follow. + +static inline src_rep_t srcToRep(src_t x) { + const union { src_t f; src_rep_t i; } rep = {.f = x}; + return rep.i; +} + +static inline dst_t dstFromRep(dst_rep_t x) { + const union { dst_t f; dst_rep_t i; } rep = {.i = x}; + return rep.f; +} + +// End helper routines. Conversion implementation follows. + +ARM_EABI_FNALIAS(f2d, extendsfdf2) + +dst_t __extendsfdf2(src_t a) { + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const int srcBits = sizeof(src_t)*CHAR_BIT; + const int srcExpBits = srcBits - srcSigBits - 1; + const int srcInfExp = (1 << srcExpBits) - 1; + const int srcExpBias = srcInfExp >> 1; + + const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigBits; + const src_rep_t srcInfinity = (src_rep_t)srcInfExp << srcSigBits; + const src_rep_t srcSignMask = SRC_REP_C(1) << (srcSigBits + srcExpBits); + const src_rep_t srcAbsMask = srcSignMask - 1; + const src_rep_t srcQNaN = SRC_REP_C(1) << (srcSigBits - 1); + const src_rep_t srcNaNCode = srcQNaN - 1; + + const int dstBits = sizeof(dst_t)*CHAR_BIT; + const int dstExpBits = dstBits - dstSigBits - 1; + const int dstInfExp = (1 << dstExpBits) - 1; + const int dstExpBias = dstInfExp >> 1; + + const dst_rep_t dstMinNormal = DST_REP_C(1) << dstSigBits; + + // Break a into a sign and representation of the absolute value + const src_rep_t aRep = srcToRep(a); + const src_rep_t aAbs = aRep & srcAbsMask; + const src_rep_t sign = aRep & srcSignMask; + dst_rep_t absResult; + + if (aAbs - srcMinNormal < srcInfinity - srcMinNormal) { + // a is a normal number. + // Extend to the destination type by shifting the significand and + // exponent into the proper position and rebiasing the exponent. + absResult = (dst_rep_t)aAbs << (dstSigBits - srcSigBits); + absResult += (dst_rep_t)(dstExpBias - srcExpBias) << dstSigBits; + } + + else if (aAbs >= srcInfinity) { + // a is NaN or infinity. + // Conjure the result by beginning with infinity, then setting the qNaN + // bit (if needed) and right-aligning the rest of the trailing NaN + // payload field. + absResult = (dst_rep_t)dstInfExp << dstSigBits; + absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits); + absResult |= aAbs & srcNaNCode; + } + + else if (aAbs) { + // a is denormal. + // renormalize the significand and clear the leading bit, then insert + // the correct adjusted exponent in the destination type. + const int scale = src_rep_t_clz(aAbs) - src_rep_t_clz(srcMinNormal); + absResult = (dst_rep_t)aAbs << (dstSigBits - srcSigBits + scale); + absResult ^= dstMinNormal; + const int resultExponent = dstExpBias - srcExpBias - scale + 1; + absResult |= (dst_rep_t)resultExponent << dstSigBits; + } + + else { + // a is zero. + absResult = 0; + } + + // Apply the signbit to (dst_t)abs(a). + const dst_rep_t result = absResult | (dst_rep_t)sign << (dstBits - srcBits); + return dstFromRep(result); +} diff --git a/projects/compiler-rt/lib/ffsdi2.c b/projects/compiler-rt/lib/ffsdi2.c new file mode 100644 index 00000000..a5ac9900 --- /dev/null +++ b/projects/compiler-rt/lib/ffsdi2.c @@ -0,0 +1,33 @@ +/* ===-- ffsdi2.c - Implement __ffsdi2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ffsdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: the index of the least significant 1-bit in a, or + * the value zero if a is zero. The least significant bit is index one. + */ + +COMPILER_RT_ABI si_int +__ffsdi2(di_int a) +{ + dwords x; + x.all = a; + if (x.s.low == 0) + { + if (x.s.high == 0) + return 0; + return __builtin_ctz(x.s.high) + (1 + sizeof(si_int) * CHAR_BIT); + } + return __builtin_ctz(x.s.low) + 1; +} diff --git a/projects/compiler-rt/lib/ffsti2.c b/projects/compiler-rt/lib/ffsti2.c new file mode 100644 index 00000000..27e15d58 --- /dev/null +++ b/projects/compiler-rt/lib/ffsti2.c @@ -0,0 +1,37 @@ +/* ===-- ffsti2.c - Implement __ffsti2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ffsti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: the index of the least significant 1-bit in a, or + * the value zero if a is zero. The least significant bit is index one. + */ + +si_int +__ffsti2(ti_int a) +{ + twords x; + x.all = a; + if (x.s.low == 0) + { + if (x.s.high == 0) + return 0; + return __builtin_ctzll(x.s.high) + (1 + sizeof(di_int) * CHAR_BIT); + } + return __builtin_ctzll(x.s.low) + 1; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/fixdfdi.c b/projects/compiler-rt/lib/fixdfdi.c new file mode 100644 index 00000000..7665ea5a --- /dev/null +++ b/projects/compiler-rt/lib/fixdfdi.c @@ -0,0 +1,45 @@ +/* ===-- fixdfdi.c - Implement __fixdfdi -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixdfdi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: convert a to a signed long long, rounding toward zero. */ + +/* Assumption: double is a IEEE 64 bit floating point type + * su_int is a 32 bit integral type + * value in double is representable in di_int (no range checking performed) + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +ARM_EABI_FNALIAS(d2lz, fixdfdi) + +di_int +__fixdfdi(double a) +{ + double_bits fb; + fb.f = a; + int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; + if (e < 0) + return 0; + di_int s = (si_int)(fb.u.s.high & 0x80000000) >> 31; + dwords r; + r.s.high = (fb.u.s.high & 0x000FFFFF) | 0x00100000; + r.s.low = fb.u.s.low; + if (e > 52) + r.all <<= (e - 52); + else + r.all >>= (52 - e); + return (r.all ^ s) - s; +} diff --git a/projects/compiler-rt/lib/fixdfsi.c b/projects/compiler-rt/lib/fixdfsi.c new file mode 100644 index 00000000..614d032a --- /dev/null +++ b/projects/compiler-rt/lib/fixdfsi.c @@ -0,0 +1,49 @@ +//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision to integer conversion for the +// compiler-rt library. No range checking is performed; the behavior of this +// conversion is undefined for out of range values in the C standard. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +#include "int_lib.h" + +ARM_EABI_FNALIAS(d2iz, fixdfsi) + +int __fixdfsi(fp_t a) { + + // Break a into sign, exponent, significand + const rep_t aRep = toRep(a); + const rep_t aAbs = aRep & absMask; + const int sign = aRep & signBit ? -1 : 1; + const int exponent = (aAbs >> significandBits) - exponentBias; + const rep_t significand = (aAbs & significandMask) | implicitBit; + + // If 0 < exponent < significandBits, right shift to get the result. + if ((unsigned int)exponent < significandBits) { + return sign * (significand >> (significandBits - exponent)); + } + + // If exponent is negative, the result is zero. + else if (exponent < 0) { + return 0; + } + + // If significandBits < exponent, left shift to get the result. This shift + // may end up being larger than the type width, which incurs undefined + // behavior, but the conversion itself is undefined in that case, so + // whatever the compiler decides to do is fine. + else { + return sign * (significand << (exponent - significandBits)); + } +} diff --git a/projects/compiler-rt/lib/fixdfti.c b/projects/compiler-rt/lib/fixdfti.c new file mode 100644 index 00000000..b110a94b --- /dev/null +++ b/projects/compiler-rt/lib/fixdfti.c @@ -0,0 +1,45 @@ +/* ===-- fixdfti.c - Implement __fixdfti -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixdfti for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a signed long long, rounding toward zero. */ + +/* Assumption: double is a IEEE 64 bit floating point type + * su_int is a 32 bit integral type + * value in double is representable in ti_int (no range checking performed) + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +ti_int +__fixdfti(double a) +{ + double_bits fb; + fb.f = a; + int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; + if (e < 0) + return 0; + ti_int s = (si_int)(fb.u.s.high & 0x80000000) >> 31; + ti_int r = 0x0010000000000000uLL | (0x000FFFFFFFFFFFFFuLL & fb.u.all); + if (e > 52) + r <<= (e - 52); + else + r >>= (52 - e); + return (r ^ s) - s; +} + +#endif diff --git a/projects/compiler-rt/lib/fixsfdi.c b/projects/compiler-rt/lib/fixsfdi.c new file mode 100644 index 00000000..4f6cfdd7 --- /dev/null +++ b/projects/compiler-rt/lib/fixsfdi.c @@ -0,0 +1,43 @@ +/* ===-- fixsfdi.c - Implement __fixsfdi -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixsfdi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: convert a to a signed long long, rounding toward zero. */ + +/* Assumption: float is a IEEE 32 bit floating point type + * su_int is a 32 bit integral type + * value in float is representable in di_int (no range checking performed) + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +ARM_EABI_FNALIAS(f2lz, fixsfdi) + +COMPILER_RT_ABI di_int +__fixsfdi(float a) +{ + float_bits fb; + fb.f = a; + int e = ((fb.u & 0x7F800000) >> 23) - 127; + if (e < 0) + return 0; + di_int s = (si_int)(fb.u & 0x80000000) >> 31; + di_int r = (fb.u & 0x007FFFFF) | 0x00800000; + if (e > 23) + r <<= (e - 23); + else + r >>= (23 - e); + return (r ^ s) - s; +} diff --git a/projects/compiler-rt/lib/fixsfsi.c b/projects/compiler-rt/lib/fixsfsi.c new file mode 100644 index 00000000..e3cc42d5 --- /dev/null +++ b/projects/compiler-rt/lib/fixsfsi.c @@ -0,0 +1,47 @@ +//===-- lib/fixsfsi.c - Single-precision -> integer conversion ----*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision to integer conversion for the +// compiler-rt library. No range checking is performed; the behavior of this +// conversion is undefined for out of range values in the C standard. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(f2iz, fixsfsi) + +COMPILER_RT_ABI int +__fixsfsi(fp_t a) { + // Break a into sign, exponent, significand + const rep_t aRep = toRep(a); + const rep_t aAbs = aRep & absMask; + const int sign = aRep & signBit ? -1 : 1; + const int exponent = (aAbs >> significandBits) - exponentBias; + const rep_t significand = (aAbs & significandMask) | implicitBit; + + // If 0 < exponent < significandBits, right shift to get the result. + if ((unsigned int)exponent < significandBits) { + return sign * (significand >> (significandBits - exponent)); + } + + // If exponent is negative, the result is zero. + else if (exponent < 0) { + return 0; + } + + // If significandBits < exponent, left shift to get the result. This shift + // may end up being larger than the type width, which incurs undefined + // behavior, but the conversion itself is undefined in that case, so + // whatever the compiler decides to do is fine. + else { + return sign * (significand << (exponent - significandBits)); + } +} diff --git a/projects/compiler-rt/lib/fixsfti.c b/projects/compiler-rt/lib/fixsfti.c new file mode 100644 index 00000000..c730ae04 --- /dev/null +++ b/projects/compiler-rt/lib/fixsfti.c @@ -0,0 +1,45 @@ +/* ===-- fixsfti.c - Implement __fixsfti -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixsfti for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a signed long long, rounding toward zero. */ + +/* Assumption: float is a IEEE 32 bit floating point type + * su_int is a 32 bit integral type + * value in float is representable in ti_int (no range checking performed) + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +ti_int +__fixsfti(float a) +{ + float_bits fb; + fb.f = a; + int e = ((fb.u & 0x7F800000) >> 23) - 127; + if (e < 0) + return 0; + ti_int s = (si_int)(fb.u & 0x80000000) >> 31; + ti_int r = (fb.u & 0x007FFFFF) | 0x00800000; + if (e > 23) + r <<= (e - 23); + else + r >>= (23 - e); + return (r ^ s) - s; +} + +#endif diff --git a/projects/compiler-rt/lib/fixunsdfdi.c b/projects/compiler-rt/lib/fixunsdfdi.c new file mode 100644 index 00000000..9e637139 --- /dev/null +++ b/projects/compiler-rt/lib/fixunsdfdi.c @@ -0,0 +1,47 @@ +/* ===-- fixunsdfdi.c - Implement __fixunsdfdi -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunsdfdi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: convert a to a unsigned long long, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: double is a IEEE 64 bit floating point type + * du_int is a 64 bit integral type + * value in double is representable in du_int or is negative + * (no range checking performed) + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +ARM_EABI_FNALIAS(d2ulz, fixunsdfdi) + +COMPILER_RT_ABI du_int +__fixunsdfdi(double a) +{ + double_bits fb; + fb.f = a; + int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; + if (e < 0 || (fb.u.s.high & 0x80000000)) + return 0; + udwords r; + r.s.high = (fb.u.s.high & 0x000FFFFF) | 0x00100000; + r.s.low = fb.u.s.low; + if (e > 52) + r.all <<= (e - 52); + else + r.all >>= (52 - e); + return r.all; +} diff --git a/projects/compiler-rt/lib/fixunsdfsi.c b/projects/compiler-rt/lib/fixunsdfsi.c new file mode 100644 index 00000000..c6a3c755 --- /dev/null +++ b/projects/compiler-rt/lib/fixunsdfsi.c @@ -0,0 +1,44 @@ +/* ===-- fixunsdfsi.c - Implement __fixunsdfsi -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunsdfsi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: convert a to a unsigned int, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: double is a IEEE 64 bit floating point type + * su_int is a 32 bit integral type + * value in double is representable in su_int or is negative + * (no range checking performed) + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +ARM_EABI_FNALIAS(d2uiz, fixunsdfsi) + +COMPILER_RT_ABI su_int +__fixunsdfsi(double a) +{ + double_bits fb; + fb.f = a; + int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; + if (e < 0 || (fb.u.s.high & 0x80000000)) + return 0; + return ( + 0x80000000u | + ((fb.u.s.high & 0x000FFFFF) << 11) | + (fb.u.s.low >> 21) + ) >> (31 - e); +} diff --git a/projects/compiler-rt/lib/fixunsdfti.c b/projects/compiler-rt/lib/fixunsdfti.c new file mode 100644 index 00000000..fb0336f6 --- /dev/null +++ b/projects/compiler-rt/lib/fixunsdfti.c @@ -0,0 +1,47 @@ +/* ===-- fixunsdfti.c - Implement __fixunsdfti -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunsdfti for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a unsigned long long, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: double is a IEEE 64 bit floating point type + * tu_int is a 64 bit integral type + * value in double is representable in tu_int or is negative + * (no range checking performed) + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +tu_int +__fixunsdfti(double a) +{ + double_bits fb; + fb.f = a; + int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; + if (e < 0 || (fb.u.s.high & 0x80000000)) + return 0; + tu_int r = 0x0010000000000000uLL | (fb.u.all & 0x000FFFFFFFFFFFFFuLL); + if (e > 52) + r <<= (e - 52); + else + r >>= (52 - e); + return r; +} + +#endif diff --git a/projects/compiler-rt/lib/fixunssfdi.c b/projects/compiler-rt/lib/fixunssfdi.c new file mode 100644 index 00000000..69d5952e --- /dev/null +++ b/projects/compiler-rt/lib/fixunssfdi.c @@ -0,0 +1,44 @@ +/* ===-- fixunssfdi.c - Implement __fixunssfdi -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunssfdi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" +/* Returns: convert a to a unsigned long long, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: float is a IEEE 32 bit floating point type + * du_int is a 64 bit integral type + * value in float is representable in du_int or is negative + * (no range checking performed) + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +ARM_EABI_FNALIAS(f2ulz, fixunssfdi) + +COMPILER_RT_ABI du_int +__fixunssfdi(float a) +{ + float_bits fb; + fb.f = a; + int e = ((fb.u & 0x7F800000) >> 23) - 127; + if (e < 0 || (fb.u & 0x80000000)) + return 0; + du_int r = (fb.u & 0x007FFFFF) | 0x00800000; + if (e > 23) + r <<= (e - 23); + else + r >>= (23 - e); + return r; +} diff --git a/projects/compiler-rt/lib/fixunssfsi.c b/projects/compiler-rt/lib/fixunssfsi.c new file mode 100644 index 00000000..e034139e --- /dev/null +++ b/projects/compiler-rt/lib/fixunssfsi.c @@ -0,0 +1,45 @@ +/* ===-- fixunssfsi.c - Implement __fixunssfsi -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunssfsi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: convert a to a unsigned int, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: float is a IEEE 32 bit floating point type + * su_int is a 32 bit integral type + * value in float is representable in su_int or is negative + * (no range checking performed) + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +ARM_EABI_FNALIAS(f2uiz, fixunssfsi) + +COMPILER_RT_ABI su_int +__fixunssfsi(float a) +{ + float_bits fb; + fb.f = a; + int e = ((fb.u & 0x7F800000) >> 23) - 127; + if (e < 0 || (fb.u & 0x80000000)) + return 0; + su_int r = (fb.u & 0x007FFFFF) | 0x00800000; + if (e > 23) + r <<= (e - 23); + else + r >>= (23 - e); + return r; +} diff --git a/projects/compiler-rt/lib/fixunssfti.c b/projects/compiler-rt/lib/fixunssfti.c new file mode 100644 index 00000000..8f4c6262 --- /dev/null +++ b/projects/compiler-rt/lib/fixunssfti.c @@ -0,0 +1,47 @@ +/* ===-- fixunssfti.c - Implement __fixunssfti -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunssfti for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a unsigned long long, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: float is a IEEE 32 bit floating point type + * tu_int is a 64 bit integral type + * value in float is representable in tu_int or is negative + * (no range checking performed) + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +tu_int +__fixunssfti(float a) +{ + float_bits fb; + fb.f = a; + int e = ((fb.u & 0x7F800000) >> 23) - 127; + if (e < 0 || (fb.u & 0x80000000)) + return 0; + tu_int r = (fb.u & 0x007FFFFF) | 0x00800000; + if (e > 23) + r <<= (e - 23); + else + r >>= (23 - e); + return r; +} + +#endif diff --git a/projects/compiler-rt/lib/fixunsxfdi.c b/projects/compiler-rt/lib/fixunsxfdi.c new file mode 100644 index 00000000..6c817d8e --- /dev/null +++ b/projects/compiler-rt/lib/fixunsxfdi.c @@ -0,0 +1,44 @@ +/* ===-- fixunsxfdi.c - Implement __fixunsxfdi -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunsxfdi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#if !_ARCH_PPC + +#include "int_lib.h" + +/* Returns: convert a to a unsigned long long, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes + * du_int is a 64 bit integral type + * value in long double is representable in du_int or is negative + * (no range checking performed) + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +du_int +__fixunsxfdi(long double a) +{ + long_double_bits fb; + fb.f = a; + int e = (fb.u.high.s.low & 0x00007FFF) - 16383; + if (e < 0 || (fb.u.high.s.low & 0x00008000)) + return 0; + return fb.u.low.all >> (63 - e); +} + +#endif diff --git a/projects/compiler-rt/lib/fixunsxfsi.c b/projects/compiler-rt/lib/fixunsxfsi.c new file mode 100644 index 00000000..b9da86c4 --- /dev/null +++ b/projects/compiler-rt/lib/fixunsxfsi.c @@ -0,0 +1,44 @@ +/* ===-- fixunsxfsi.c - Implement __fixunsxfsi -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunsxfsi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#if !_ARCH_PPC + +#include "int_lib.h" + +/* Returns: convert a to a unsigned int, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes + * su_int is a 32 bit integral type + * value in long double is representable in su_int or is negative + * (no range checking performed) + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +su_int +__fixunsxfsi(long double a) +{ + long_double_bits fb; + fb.f = a; + int e = (fb.u.high.s.low & 0x00007FFF) - 16383; + if (e < 0 || (fb.u.high.s.low & 0x00008000)) + return 0; + return fb.u.low.s.high >> (31 - e); +} + +#endif /* !_ARCH_PPC */ diff --git a/projects/compiler-rt/lib/fixunsxfti.c b/projects/compiler-rt/lib/fixunsxfti.c new file mode 100644 index 00000000..260bfc01 --- /dev/null +++ b/projects/compiler-rt/lib/fixunsxfti.c @@ -0,0 +1,49 @@ +/* ===-- fixunsxfti.c - Implement __fixunsxfti -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixunsxfti for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a unsigned long long, rounding toward zero. + * Negative values all become zero. + */ + +/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes + * tu_int is a 64 bit integral type + * value in long double is representable in tu_int or is negative + * (no range checking performed) + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +tu_int +__fixunsxfti(long double a) +{ + long_double_bits fb; + fb.f = a; + int e = (fb.u.high.s.low & 0x00007FFF) - 16383; + if (e < 0 || (fb.u.high.s.low & 0x00008000)) + return 0; + tu_int r = fb.u.low.all; + if (e > 63) + r <<= (e - 63); + else + r >>= (63 - e); + return r; +} + +#endif diff --git a/projects/compiler-rt/lib/fixxfdi.c b/projects/compiler-rt/lib/fixxfdi.c new file mode 100644 index 00000000..9592ce43 --- /dev/null +++ b/projects/compiler-rt/lib/fixxfdi.c @@ -0,0 +1,44 @@ +/* ===-- fixxfdi.c - Implement __fixxfdi -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixxfdi for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#if !_ARCH_PPC + +#include "int_lib.h" + +/* Returns: convert a to a signed long long, rounding toward zero. */ + +/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes + * su_int is a 32 bit integral type + * value in long double is representable in di_int (no range checking performed) + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +di_int +__fixxfdi(long double a) +{ + long_double_bits fb; + fb.f = a; + int e = (fb.u.high.s.low & 0x00007FFF) - 16383; + if (e < 0) + return 0; + di_int s = -(si_int)((fb.u.high.s.low & 0x00008000) >> 15); + di_int r = fb.u.low.all; + r = (du_int)r >> (63 - e); + return (r ^ s) - s; +} + +#endif /* !_ARCH_PPC */ diff --git a/projects/compiler-rt/lib/fixxfti.c b/projects/compiler-rt/lib/fixxfti.c new file mode 100644 index 00000000..973dc31b --- /dev/null +++ b/projects/compiler-rt/lib/fixxfti.c @@ -0,0 +1,47 @@ +/* ===-- fixxfti.c - Implement __fixxfti -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __fixxfti for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a signed long long, rounding toward zero. */ + +/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes + * su_int is a 32 bit integral type + * value in long double is representable in ti_int (no range checking performed) + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +ti_int +__fixxfti(long double a) +{ + long_double_bits fb; + fb.f = a; + int e = (fb.u.high.s.low & 0x00007FFF) - 16383; + if (e < 0) + return 0; + ti_int s = -(si_int)((fb.u.high.s.low & 0x00008000) >> 15); + ti_int r = fb.u.low.all; + if (e > 63) + r <<= (e - 63); + else + r >>= (63 - e); + return (r ^ s) - s; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/floatdidf.c b/projects/compiler-rt/lib/floatdidf.c new file mode 100644 index 00000000..e53fa258 --- /dev/null +++ b/projects/compiler-rt/lib/floatdidf.c @@ -0,0 +1,107 @@ +/*===-- floatdidf.c - Implement __floatdidf -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------=== + * + * This file implements __floatdidf for the compiler_rt library. + * + *===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: convert a to a double, rounding toward even. */ + +/* Assumption: double is a IEEE 64 bit floating point type + * di_int is a 64 bit integral type + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +ARM_EABI_FNALIAS(l2d, floatdidf) + +#ifndef __SOFT_FP__ +/* Support for systems that have hardware floating-point; we'll set the inexact flag + * as a side-effect of this computation. + */ + +COMPILER_RT_ABI double +__floatdidf(di_int a) +{ + static const double twop52 = 0x1.0p52; + static const double twop32 = 0x1.0p32; + + union { int64_t x; double d; } low = { .d = twop52 }; + + const double high = (int32_t)(a >> 32) * twop32; + low.x |= a & INT64_C(0x00000000ffffffff); + + const double result = (high - twop52) + low.d; + return result; +} + +#else +/* Support for systems that don't have hardware floating-point; there are no flags to + * set, and we don't want to code-gen to an unknown soft-float implementation. + */ + +COMPILER_RT_ABI double +__floatdidf(di_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(di_int) * CHAR_BIT; + const di_int s = a >> (N-1); + a = (a ^ s) - s; + int sd = N - __builtin_clzll(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > DBL_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit DBL_MANT_DIG-1 bits to the right of 1 + * Q = bit DBL_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case DBL_MANT_DIG + 1: + a <<= 1; + break; + case DBL_MANT_DIG + 2: + break; + default: + a = ((du_int)a >> (sd - (DBL_MANT_DIG+2))) | + ((a & ((du_int)(-1) >> ((N + DBL_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits */ + if (a & ((du_int)1 << DBL_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to DBL_MANT_DIG bits */ + } + else + { + a <<= (DBL_MANT_DIG - sd); + /* a is now rounded to DBL_MANT_DIG bits */ + } + double_bits fb; + fb.u.high = ((su_int)s & 0x80000000) | /* sign */ + ((e + 1023) << 20) | /* exponent */ + ((su_int)(a >> 32) & 0x000FFFFF); /* mantissa-high */ + fb.u.low = (su_int)a; /* mantissa-low */ + return fb.f; +} +#endif diff --git a/projects/compiler-rt/lib/floatdisf.c b/projects/compiler-rt/lib/floatdisf.c new file mode 100644 index 00000000..3e47580e --- /dev/null +++ b/projects/compiler-rt/lib/floatdisf.c @@ -0,0 +1,80 @@ +/*===-- floatdisf.c - Implement __floatdisf -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------=== + * + * This file implements __floatdisf for the compiler_rt library. + * + *===----------------------------------------------------------------------=== + */ + +/* Returns: convert a to a float, rounding toward even.*/ + +/* Assumption: float is a IEEE 32 bit floating point type + * di_int is a 64 bit integral type + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +#include "int_lib.h" + +ARM_EABI_FNALIAS(l2f, floatdisf) + +COMPILER_RT_ABI float +__floatdisf(di_int a) +{ + if (a == 0) + return 0.0F; + const unsigned N = sizeof(di_int) * CHAR_BIT; + const di_int s = a >> (N-1); + a = (a ^ s) - s; + int sd = N - __builtin_clzll(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > FLT_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit FLT_MANT_DIG-1 bits to the right of 1 + * Q = bit FLT_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case FLT_MANT_DIG + 1: + a <<= 1; + break; + case FLT_MANT_DIG + 2: + break; + default: + a = ((du_int)a >> (sd - (FLT_MANT_DIG+2))) | + ((a & ((du_int)(-1) >> ((N + FLT_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits */ + if (a & ((du_int)1 << FLT_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to FLT_MANT_DIG bits */ + } + else + { + a <<= (FLT_MANT_DIG - sd); + /* a is now rounded to FLT_MANT_DIG bits */ + } + float_bits fb; + fb.u = ((su_int)s & 0x80000000) | /* sign */ + ((e + 127) << 23) | /* exponent */ + ((su_int)a & 0x007FFFFF); /* mantissa */ + return fb.f; +} diff --git a/projects/compiler-rt/lib/floatdixf.c b/projects/compiler-rt/lib/floatdixf.c new file mode 100644 index 00000000..ebf62dba --- /dev/null +++ b/projects/compiler-rt/lib/floatdixf.c @@ -0,0 +1,46 @@ +/* ===-- floatdixf.c - Implement __floatdixf -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floatdixf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#if !_ARCH_PPC + +#include "int_lib.h" + +/* Returns: convert a to a long double, rounding toward even. */ + +/* Assumption: long double is a IEEE 80 bit floating point type padded to 128 bits + * di_int is a 64 bit integral type + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +long double +__floatdixf(di_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(di_int) * CHAR_BIT; + const di_int s = a >> (N-1); + a = (a ^ s) - s; + int clz = __builtin_clzll(a); + int e = (N - 1) - clz ; /* exponent */ + long_double_bits fb; + fb.u.high.s.low = ((su_int)s & 0x00008000) | /* sign */ + (e + 16383); /* exponent */ + fb.u.low.all = a << clz; /* mantissa */ + return fb.f; +} + +#endif /* !_ARCH_PPC */ diff --git a/projects/compiler-rt/lib/floatsidf.c b/projects/compiler-rt/lib/floatsidf.c new file mode 100644 index 00000000..18f378f2 --- /dev/null +++ b/projects/compiler-rt/lib/floatsidf.c @@ -0,0 +1,52 @@ +//===-- lib/floatsidf.c - integer -> double-precision conversion --*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements integer to double-precision conversion for the +// compiler-rt library in the IEEE-754 default round-to-nearest, ties-to-even +// mode. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +#include "int_lib.h" + +ARM_EABI_FNALIAS(i2d, floatsidf) + +fp_t __floatsidf(int a) { + + const int aWidth = sizeof a * CHAR_BIT; + + // Handle zero as a special case to protect clz + if (a == 0) + return fromRep(0); + + // All other cases begin by extracting the sign and absolute value of a + rep_t sign = 0; + if (a < 0) { + sign = signBit; + a = -a; + } + + // Exponent of (fp_t)a is the width of abs(a). + const int exponent = (aWidth - 1) - __builtin_clz(a); + rep_t result; + + // Shift a into the significand field and clear the implicit bit. Extra + // cast to unsigned int is necessary to get the correct behavior for + // the input INT_MIN. + const int shift = significandBits - exponent; + result = (rep_t)(unsigned int)a << shift ^ implicitBit; + + // Insert the exponent + result += (rep_t)(exponent + exponentBias) << significandBits; + // Insert the sign bit and return + return fromRep(result | sign); +} diff --git a/projects/compiler-rt/lib/floatsisf.c b/projects/compiler-rt/lib/floatsisf.c new file mode 100644 index 00000000..83983934 --- /dev/null +++ b/projects/compiler-rt/lib/floatsisf.c @@ -0,0 +1,58 @@ +//===-- lib/floatsisf.c - integer -> single-precision conversion --*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements integer to single-precision conversion for the +// compiler-rt library in the IEEE-754 default round-to-nearest, ties-to-even +// mode. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +#include "int_lib.h" + +ARM_EABI_FNALIAS(i2f, floatsisf) + +fp_t __floatsisf(int a) { + + const int aWidth = sizeof a * CHAR_BIT; + + // Handle zero as a special case to protect clz + if (a == 0) + return fromRep(0); + + // All other cases begin by extracting the sign and absolute value of a + rep_t sign = 0; + if (a < 0) { + sign = signBit; + a = -a; + } + + // Exponent of (fp_t)a is the width of abs(a). + const int exponent = (aWidth - 1) - __builtin_clz(a); + rep_t result; + + // Shift a into the significand field, rounding if it is a right-shift + if (exponent <= significandBits) { + const int shift = significandBits - exponent; + result = (rep_t)a << shift ^ implicitBit; + } else { + const int shift = exponent - significandBits; + result = (rep_t)a >> shift ^ implicitBit; + rep_t round = (rep_t)a << (typeWidth - shift); + if (round > signBit) result++; + if (round == signBit) result += result & 1; + } + + // Insert the exponent + result += (rep_t)(exponent + exponentBias) << significandBits; + // Insert the sign bit and return + return fromRep(result | sign); +} diff --git a/projects/compiler-rt/lib/floattidf.c b/projects/compiler-rt/lib/floattidf.c new file mode 100644 index 00000000..77749f8d --- /dev/null +++ b/projects/compiler-rt/lib/floattidf.c @@ -0,0 +1,85 @@ +/* ===-- floattidf.c - Implement __floattidf -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floattidf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a double, rounding toward even.*/ + +/* Assumption: double is a IEEE 64 bit floating point type + * ti_int is a 128 bit integral type + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +si_int __clzti2(ti_int a); + +double +__floattidf(ti_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(ti_int) * CHAR_BIT; + const ti_int s = a >> (N-1); + a = (a ^ s) - s; + int sd = N - __clzti2(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > DBL_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit DBL_MANT_DIG-1 bits to the right of 1 + * Q = bit DBL_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case DBL_MANT_DIG + 1: + a <<= 1; + break; + case DBL_MANT_DIG + 2: + break; + default: + a = ((tu_int)a >> (sd - (DBL_MANT_DIG+2))) | + ((a & ((tu_int)(-1) >> ((N + DBL_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits */ + if (a & ((tu_int)1 << DBL_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to DBL_MANT_DIG bits */ + } + else + { + a <<= (DBL_MANT_DIG - sd); + /* a is now rounded to DBL_MANT_DIG bits */ + } + double_bits fb; + fb.u.s.high = ((su_int)s & 0x80000000) | /* sign */ + ((e + 1023) << 20) | /* exponent */ + ((su_int)(a >> 32) & 0x000FFFFF); /* mantissa-high */ + fb.u.s.low = (su_int)a; /* mantissa-low */ + return fb.f; +} + +#endif diff --git a/projects/compiler-rt/lib/floattisf.c b/projects/compiler-rt/lib/floattisf.c new file mode 100644 index 00000000..4776125d --- /dev/null +++ b/projects/compiler-rt/lib/floattisf.c @@ -0,0 +1,84 @@ +/* ===-- floattisf.c - Implement __floattisf -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floattisf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a float, rounding toward even. */ + +/* Assumption: float is a IEEE 32 bit floating point type + * ti_int is a 128 bit integral type + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +si_int __clzti2(ti_int a); + +float +__floattisf(ti_int a) +{ + if (a == 0) + return 0.0F; + const unsigned N = sizeof(ti_int) * CHAR_BIT; + const ti_int s = a >> (N-1); + a = (a ^ s) - s; + int sd = N - __clzti2(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > FLT_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit FLT_MANT_DIG-1 bits to the right of 1 + * Q = bit FLT_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case FLT_MANT_DIG + 1: + a <<= 1; + break; + case FLT_MANT_DIG + 2: + break; + default: + a = ((tu_int)a >> (sd - (FLT_MANT_DIG+2))) | + ((a & ((tu_int)(-1) >> ((N + FLT_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits */ + if (a & ((tu_int)1 << FLT_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to FLT_MANT_DIG bits */ + } + else + { + a <<= (FLT_MANT_DIG - sd); + /* a is now rounded to FLT_MANT_DIG bits */ + } + float_bits fb; + fb.u = ((su_int)s & 0x80000000) | /* sign */ + ((e + 127) << 23) | /* exponent */ + ((su_int)a & 0x007FFFFF); /* mantissa */ + return fb.f; +} + +#endif diff --git a/projects/compiler-rt/lib/floattixf.c b/projects/compiler-rt/lib/floattixf.c new file mode 100644 index 00000000..3813dc6b --- /dev/null +++ b/projects/compiler-rt/lib/floattixf.c @@ -0,0 +1,86 @@ +/* ===-- floattixf.c - Implement __floattixf -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floattixf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a long double, rounding toward even. */ + +/* Assumption: long double is a IEEE 80 bit floating point type padded to 128 bits + * ti_int is a 128 bit integral type + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +si_int __clzti2(ti_int a); + +long double +__floattixf(ti_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(ti_int) * CHAR_BIT; + const ti_int s = a >> (N-1); + a = (a ^ s) - s; + int sd = N - __clzti2(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > LDBL_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit LDBL_MANT_DIG-1 bits to the right of 1 + * Q = bit LDBL_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case LDBL_MANT_DIG + 1: + a <<= 1; + break; + case LDBL_MANT_DIG + 2: + break; + default: + a = ((tu_int)a >> (sd - (LDBL_MANT_DIG+2))) | + ((a & ((tu_int)(-1) >> ((N + LDBL_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits */ + if (a & ((tu_int)1 << LDBL_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to LDBL_MANT_DIG bits */ + } + else + { + a <<= (LDBL_MANT_DIG - sd); + /* a is now rounded to LDBL_MANT_DIG bits */ + } + long_double_bits fb; + fb.u.high.s.low = ((su_int)s & 0x8000) | /* sign */ + (e + 16383); /* exponent */ + fb.u.low.all = (du_int)a; /* mantissa */ + return fb.f; +} + +#endif diff --git a/projects/compiler-rt/lib/floatundidf.c b/projects/compiler-rt/lib/floatundidf.c new file mode 100644 index 00000000..e52fa0a3 --- /dev/null +++ b/projects/compiler-rt/lib/floatundidf.c @@ -0,0 +1,107 @@ +/* ===-- floatundidf.c - Implement __floatundidf ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floatundidf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +/* Returns: convert a to a double, rounding toward even. */ + +/* Assumption: double is a IEEE 64 bit floating point type + * du_int is a 64 bit integral type + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +#include "int_lib.h" + +ARM_EABI_FNALIAS(ul2d, floatundidf) + +#ifndef __SOFT_FP__ +/* Support for systems that have hardware floating-point; we'll set the inexact flag + * as a side-effect of this computation. + */ + + +COMPILER_RT_ABI double +__floatundidf(du_int a) +{ + static const double twop52 = 0x1.0p52; + static const double twop84 = 0x1.0p84; + static const double twop84_plus_twop52 = 0x1.00000001p84; + + union { uint64_t x; double d; } high = { .d = twop84 }; + union { uint64_t x; double d; } low = { .d = twop52 }; + + high.x |= a >> 32; + low.x |= a & UINT64_C(0x00000000ffffffff); + + const double result = (high.d - twop84_plus_twop52) + low.d; + return result; +} + +#else +/* Support for systems that don't have hardware floating-point; there are no flags to + * set, and we don't want to code-gen to an unknown soft-float implementation. + */ + +COMPILER_RT_ABI double +__floatundidf(du_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(du_int) * CHAR_BIT; + int sd = N - __builtin_clzll(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > DBL_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit DBL_MANT_DIG-1 bits to the right of 1 + * Q = bit DBL_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case DBL_MANT_DIG + 1: + a <<= 1; + break; + case DBL_MANT_DIG + 2: + break; + default: + a = (a >> (sd - (DBL_MANT_DIG+2))) | + ((a & ((du_int)(-1) >> ((N + DBL_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits */ + if (a & ((du_int)1 << DBL_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to DBL_MANT_DIG bits */ + } + else + { + a <<= (DBL_MANT_DIG - sd); + /* a is now rounded to DBL_MANT_DIG bits */ + } + double_bits fb; + fb.u.high = ((e + 1023) << 20) | /* exponent */ + ((su_int)(a >> 32) & 0x000FFFFF); /* mantissa-high */ + fb.u.low = (su_int)a; /* mantissa-low */ + return fb.f; +} +#endif diff --git a/projects/compiler-rt/lib/floatundisf.c b/projects/compiler-rt/lib/floatundisf.c new file mode 100644 index 00000000..713a44ab --- /dev/null +++ b/projects/compiler-rt/lib/floatundisf.c @@ -0,0 +1,77 @@ +/*===-- floatundisf.c - Implement __floatundisf ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floatundisf for the compiler_rt library. + * + *===----------------------------------------------------------------------=== + */ + +/* Returns: convert a to a float, rounding toward even. */ + +/* Assumption: float is a IEEE 32 bit floating point type + * du_int is a 64 bit integral type + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +#include "int_lib.h" + +ARM_EABI_FNALIAS(ul2f, floatundisf) + +COMPILER_RT_ABI float +__floatundisf(du_int a) +{ + if (a == 0) + return 0.0F; + const unsigned N = sizeof(du_int) * CHAR_BIT; + int sd = N - __builtin_clzll(a); /* number of significant digits */ + int e = sd - 1; /* 8 exponent */ + if (sd > FLT_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit FLT_MANT_DIG-1 bits to the right of 1 + * Q = bit FLT_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case FLT_MANT_DIG + 1: + a <<= 1; + break; + case FLT_MANT_DIG + 2: + break; + default: + a = (a >> (sd - (FLT_MANT_DIG+2))) | + ((a & ((du_int)(-1) >> ((N + FLT_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits */ + if (a & ((du_int)1 << FLT_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to FLT_MANT_DIG bits */ + } + else + { + a <<= (FLT_MANT_DIG - sd); + /* a is now rounded to FLT_MANT_DIG bits */ + } + float_bits fb; + fb.u = ((e + 127) << 23) | /* exponent */ + ((su_int)a & 0x007FFFFF); /* mantissa */ + return fb.f; +} diff --git a/projects/compiler-rt/lib/floatundixf.c b/projects/compiler-rt/lib/floatundixf.c new file mode 100644 index 00000000..64f7662d --- /dev/null +++ b/projects/compiler-rt/lib/floatundixf.c @@ -0,0 +1,42 @@ +/* ===-- floatundixf.c - Implement __floatundixf ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floatundixf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#if !_ARCH_PPC + +#include "int_lib.h" + +/* Returns: convert a to a long double, rounding toward even. */ + +/* Assumption: long double is a IEEE 80 bit floating point type padded to 128 bits + * du_int is a 64 bit integral type + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ +long double +__floatundixf(du_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(du_int) * CHAR_BIT; + int clz = __builtin_clzll(a); + int e = (N - 1) - clz ; /* exponent */ + long_double_bits fb; + fb.u.high.s.low = (e + 16383); /* exponent */ + fb.u.low.all = a << clz; /* mantissa */ + return fb.f; +} + +#endif /* _ARCH_PPC */ diff --git a/projects/compiler-rt/lib/floatunsidf.c b/projects/compiler-rt/lib/floatunsidf.c new file mode 100644 index 00000000..ba6c2cfd --- /dev/null +++ b/projects/compiler-rt/lib/floatunsidf.c @@ -0,0 +1,41 @@ +//===-- lib/floatunsidf.c - uint -> double-precision conversion ---*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements unsigned integer to double-precision conversion for the +// compiler-rt library in the IEEE-754 default round-to-nearest, ties-to-even +// mode. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +#include "int_lib.h" + +ARM_EABI_FNALIAS(ui2d, floatunsidf) + +fp_t __floatunsidf(unsigned int a) { + + const int aWidth = sizeof a * CHAR_BIT; + + // Handle zero as a special case to protect clz + if (a == 0) return fromRep(0); + + // Exponent of (fp_t)a is the width of abs(a). + const int exponent = (aWidth - 1) - __builtin_clz(a); + rep_t result; + + // Shift a into the significand field and clear the implicit bit. + const int shift = significandBits - exponent; + result = (rep_t)a << shift ^ implicitBit; + + // Insert the exponent + result += (rep_t)(exponent + exponentBias) << significandBits; + return fromRep(result); +} diff --git a/projects/compiler-rt/lib/floatunsisf.c b/projects/compiler-rt/lib/floatunsisf.c new file mode 100644 index 00000000..e392c0ec --- /dev/null +++ b/projects/compiler-rt/lib/floatunsisf.c @@ -0,0 +1,49 @@ +//===-- lib/floatunsisf.c - uint -> single-precision conversion ---*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements unsigned integer to single-precision conversion for the +// compiler-rt library in the IEEE-754 default round-to-nearest, ties-to-even +// mode. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +#include "int_lib.h" + +ARM_EABI_FNALIAS(ui2f, floatunsisf) + +fp_t __floatunsisf(unsigned int a) { + + const int aWidth = sizeof a * CHAR_BIT; + + // Handle zero as a special case to protect clz + if (a == 0) return fromRep(0); + + // Exponent of (fp_t)a is the width of abs(a). + const int exponent = (aWidth - 1) - __builtin_clz(a); + rep_t result; + + // Shift a into the significand field, rounding if it is a right-shift + if (exponent <= significandBits) { + const int shift = significandBits - exponent; + result = (rep_t)a << shift ^ implicitBit; + } else { + const int shift = exponent - significandBits; + result = (rep_t)a >> shift ^ implicitBit; + rep_t round = (rep_t)a << (typeWidth - shift); + if (round > signBit) result++; + if (round == signBit) result += result & 1; + } + + // Insert the exponent + result += (rep_t)(exponent + exponentBias) << significandBits; + return fromRep(result); +} diff --git a/projects/compiler-rt/lib/floatuntidf.c b/projects/compiler-rt/lib/floatuntidf.c new file mode 100644 index 00000000..4c1d3289 --- /dev/null +++ b/projects/compiler-rt/lib/floatuntidf.c @@ -0,0 +1,82 @@ +/* ===-- floatuntidf.c - Implement __floatuntidf ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floatuntidf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a double, rounding toward even. */ + +/* Assumption: double is a IEEE 64 bit floating point type + * tu_int is a 128 bit integral type + */ + +/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ + +si_int __clzti2(ti_int a); + +double +__floatuntidf(tu_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(tu_int) * CHAR_BIT; + int sd = N - __clzti2(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > DBL_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit DBL_MANT_DIG-1 bits to the right of 1 + * Q = bit DBL_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case DBL_MANT_DIG + 1: + a <<= 1; + break; + case DBL_MANT_DIG + 2: + break; + default: + a = (a >> (sd - (DBL_MANT_DIG+2))) | + ((a & ((tu_int)(-1) >> ((N + DBL_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits */ + if (a & ((tu_int)1 << DBL_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to DBL_MANT_DIG bits */ + } + else + { + a <<= (DBL_MANT_DIG - sd); + /* a is now rounded to DBL_MANT_DIG bits */ + } + double_bits fb; + fb.u.s.high = ((e + 1023) << 20) | /* exponent */ + ((su_int)(a >> 32) & 0x000FFFFF); /* mantissa-high */ + fb.u.s.low = (su_int)a; /* mantissa-low */ + return fb.f; +} + +#endif diff --git a/projects/compiler-rt/lib/floatuntisf.c b/projects/compiler-rt/lib/floatuntisf.c new file mode 100644 index 00000000..c8da2601 --- /dev/null +++ b/projects/compiler-rt/lib/floatuntisf.c @@ -0,0 +1,81 @@ +/* ===-- floatuntisf.c - Implement __floatuntisf ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floatuntisf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a float, rounding toward even. */ + +/* Assumption: float is a IEEE 32 bit floating point type + * tu_int is a 128 bit integral type + */ + +/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ + +si_int __clzti2(ti_int a); + +float +__floatuntisf(tu_int a) +{ + if (a == 0) + return 0.0F; + const unsigned N = sizeof(tu_int) * CHAR_BIT; + int sd = N - __clzti2(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > FLT_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit FLT_MANT_DIG-1 bits to the right of 1 + * Q = bit FLT_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case FLT_MANT_DIG + 1: + a <<= 1; + break; + case FLT_MANT_DIG + 2: + break; + default: + a = (a >> (sd - (FLT_MANT_DIG+2))) | + ((a & ((tu_int)(-1) >> ((N + FLT_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits */ + if (a & ((tu_int)1 << FLT_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to FLT_MANT_DIG bits */ + } + else + { + a <<= (FLT_MANT_DIG - sd); + /* a is now rounded to FLT_MANT_DIG bits */ + } + float_bits fb; + fb.u = ((e + 127) << 23) | /* exponent */ + ((su_int)a & 0x007FFFFF); /* mantissa */ + return fb.f; +} + +#endif diff --git a/projects/compiler-rt/lib/floatuntixf.c b/projects/compiler-rt/lib/floatuntixf.c new file mode 100644 index 00000000..dbce80f1 --- /dev/null +++ b/projects/compiler-rt/lib/floatuntixf.c @@ -0,0 +1,83 @@ +/* ===-- floatuntixf.c - Implement __floatuntixf ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __floatuntixf for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: convert a to a long double, rounding toward even. */ + +/* Assumption: long double is a IEEE 80 bit floating point type padded to 128 bits + * tu_int is a 128 bit integral type + */ + +/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | + * 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm + */ + +si_int __clzti2(ti_int a); + +long double +__floatuntixf(tu_int a) +{ + if (a == 0) + return 0.0; + const unsigned N = sizeof(tu_int) * CHAR_BIT; + int sd = N - __clzti2(a); /* number of significant digits */ + int e = sd - 1; /* exponent */ + if (sd > LDBL_MANT_DIG) + { + /* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + * finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + * 12345678901234567890123456 + * 1 = msb 1 bit + * P = bit LDBL_MANT_DIG-1 bits to the right of 1 + * Q = bit LDBL_MANT_DIG bits to the right of 1 + * R = "or" of all bits to the right of Q + */ + switch (sd) + { + case LDBL_MANT_DIG + 1: + a <<= 1; + break; + case LDBL_MANT_DIG + 2: + break; + default: + a = (a >> (sd - (LDBL_MANT_DIG+2))) | + ((a & ((tu_int)(-1) >> ((N + LDBL_MANT_DIG+2) - sd))) != 0); + }; + /* finish: */ + a |= (a & 4) != 0; /* Or P into R */ + ++a; /* round - this step may add a significant bit */ + a >>= 2; /* dump Q and R */ + /* a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits */ + if (a & ((tu_int)1 << LDBL_MANT_DIG)) + { + a >>= 1; + ++e; + } + /* a is now rounded to LDBL_MANT_DIG bits */ + } + else + { + a <<= (LDBL_MANT_DIG - sd); + /* a is now rounded to LDBL_MANT_DIG bits */ + } + long_double_bits fb; + fb.u.high.s.low = (e + 16383); /* exponent */ + fb.u.low.all = (du_int)a; /* mantissa */ + return fb.f; +} + +#endif diff --git a/projects/compiler-rt/lib/fp_lib.h b/projects/compiler-rt/lib/fp_lib.h new file mode 100644 index 00000000..661119ae --- /dev/null +++ b/projects/compiler-rt/lib/fp_lib.h @@ -0,0 +1,144 @@ +//===-- lib/fp_lib.h - Floating-point utilities -------------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a configuration header for soft-float routines in compiler-rt. +// This file does not provide any part of the compiler-rt interface, but defines +// many useful constants and utility routines that are used in the +// implementation of the soft-float routines in compiler-rt. +// +// Assumes that float and double correspond to the IEEE-754 binary32 and +// binary64 types, respectively, and that integer endianness matches floating +// point endianness on the target platform. +// +//===----------------------------------------------------------------------===// + +#ifndef FP_LIB_HEADER +#define FP_LIB_HEADER + +#include +#include +#include +#include "int_lib.h" + +#if defined SINGLE_PRECISION + +typedef uint32_t rep_t; +typedef int32_t srep_t; +typedef float fp_t; +#define REP_C UINT32_C +#define significandBits 23 + +static inline int rep_clz(rep_t a) { + return __builtin_clz(a); +} + +// 32x32 --> 64 bit multiply +static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { + const uint64_t product = (uint64_t)a*b; + *hi = product >> 32; + *lo = product; +} + +#elif defined DOUBLE_PRECISION + +typedef uint64_t rep_t; +typedef int64_t srep_t; +typedef double fp_t; +#define REP_C UINT64_C +#define significandBits 52 + +static inline int rep_clz(rep_t a) { +#if defined __LP64__ + return __builtin_clzl(a); +#else + if (a & REP_C(0xffffffff00000000)) + return __builtin_clz(a >> 32); + else + return 32 + __builtin_clz(a & REP_C(0xffffffff)); +#endif +} + +#define loWord(a) (a & 0xffffffffU) +#define hiWord(a) (a >> 32) + +// 64x64 -> 128 wide multiply for platforms that don't have such an operation; +// many 64-bit platforms have this operation, but they tend to have hardware +// floating-point, so we don't bother with a special case for them here. +static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) { + // Each of the component 32x32 -> 64 products + const uint64_t plolo = loWord(a) * loWord(b); + const uint64_t plohi = loWord(a) * hiWord(b); + const uint64_t philo = hiWord(a) * loWord(b); + const uint64_t phihi = hiWord(a) * hiWord(b); + // Sum terms that contribute to lo in a way that allows us to get the carry + const uint64_t r0 = loWord(plolo); + const uint64_t r1 = hiWord(plolo) + loWord(plohi) + loWord(philo); + *lo = r0 + (r1 << 32); + // Sum terms contributing to hi with the carry from lo + *hi = hiWord(plohi) + hiWord(philo) + hiWord(r1) + phihi; +} + +#else +#error Either SINGLE_PRECISION or DOUBLE_PRECISION must be defined. +#endif + +#define typeWidth (sizeof(rep_t)*CHAR_BIT) +#define exponentBits (typeWidth - significandBits - 1) +#define maxExponent ((1 << exponentBits) - 1) +#define exponentBias (maxExponent >> 1) + +#define implicitBit (REP_C(1) << significandBits) +#define significandMask (implicitBit - 1U) +#define signBit (REP_C(1) << (significandBits + exponentBits)) +#define absMask (signBit - 1U) +#define exponentMask (absMask ^ significandMask) +#define oneRep ((rep_t)exponentBias << significandBits) +#define infRep exponentMask +#define quietBit (implicitBit >> 1) +#define qnanRep (exponentMask | quietBit) + +static inline rep_t toRep(fp_t x) { + const union { fp_t f; rep_t i; } rep = {.f = x}; + return rep.i; +} + +static inline fp_t fromRep(rep_t x) { + const union { fp_t f; rep_t i; } rep = {.i = x}; + return rep.f; +} + +static inline int normalize(rep_t *significand) { + const int shift = rep_clz(*significand) - rep_clz(implicitBit); + *significand <<= shift; + return 1 - shift; +} + +static inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) { + *hi = *hi << count | *lo >> (typeWidth - count); + *lo = *lo << count; +} + +static inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int count) { + if (count < typeWidth) { + const bool sticky = *lo << (typeWidth - count); + *lo = *hi << (typeWidth - count) | *lo >> count | sticky; + *hi = *hi >> count; + } + else if (count < 2*typeWidth) { + const bool sticky = *hi << (2*typeWidth - count) | *lo; + *lo = *hi >> (count - typeWidth) | sticky; + *hi = 0; + } else { + const bool sticky = *hi | *lo; + *lo = sticky; + *hi = 0; + } +} + +#endif // FP_LIB_HEADER diff --git a/projects/compiler-rt/lib/gcc_personality_v0.c b/projects/compiler-rt/lib/gcc_personality_v0.c new file mode 100644 index 00000000..8a708cae --- /dev/null +++ b/projects/compiler-rt/lib/gcc_personality_v0.c @@ -0,0 +1,247 @@ +/* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + */ + +#include "int_lib.h" + +/* + * _Unwind_* stuff based on C++ ABI public documentation + * http://refspecs.freestandards.org/abi-eh-1.21.html + */ + +typedef enum { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + +typedef enum { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16 +} _Unwind_Action; + +typedef struct _Unwind_Context* _Unwind_Context_t; + +struct _Unwind_Exception { + uint64_t exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code reason, + struct _Unwind_Exception* exc); + uintptr_t private_1; + uintptr_t private_2; +}; + +extern const uint8_t* _Unwind_GetLanguageSpecificData(_Unwind_Context_t c); +extern void _Unwind_SetGR(_Unwind_Context_t c, int i, uintptr_t n); +extern void _Unwind_SetIP(_Unwind_Context_t, uintptr_t new_value); +extern uintptr_t _Unwind_GetIP(_Unwind_Context_t context); +extern uintptr_t _Unwind_GetRegionStart(_Unwind_Context_t context); + + +/* + * Pointer encodings documented at: + * http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html + */ + +#define DW_EH_PE_omit 0xff /* no data follows */ + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 +#define DW_EH_PE_indirect 0x80 /* gcc extension */ + + + +/* read a uleb128 encoded value and advance pointer */ +static uintptr_t readULEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t* p = *data; + do { + byte = *p++; + result |= (byte & 0x7f) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + return result; +} + +/* read a pointer encoded value and advance pointer */ +static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding) +{ + const uint8_t* p = *data; + uintptr_t result = 0; + + if ( encoding == DW_EH_PE_omit ) + return 0; + + /* first get value */ + switch (encoding & 0x0F) { + case DW_EH_PE_absptr: + result = *((uintptr_t*)p); + p += sizeof(uintptr_t); + break; + case DW_EH_PE_uleb128: + result = readULEB128(&p); + break; + case DW_EH_PE_udata2: + result = *((uint16_t*)p); + p += sizeof(uint16_t); + break; + case DW_EH_PE_udata4: + result = *((uint32_t*)p); + p += sizeof(uint32_t); + break; + case DW_EH_PE_udata8: + result = *((uint64_t*)p); + p += sizeof(uint64_t); + break; + case DW_EH_PE_sdata2: + result = *((int16_t*)p); + p += sizeof(int16_t); + break; + case DW_EH_PE_sdata4: + result = *((int32_t*)p); + p += sizeof(int32_t); + break; + case DW_EH_PE_sdata8: + result = *((int64_t*)p); + p += sizeof(int64_t); + break; + case DW_EH_PE_sleb128: + default: + /* not supported */ + compilerrt_abort(); + break; + } + + /* then add relative offset */ + switch ( encoding & 0x70 ) { + case DW_EH_PE_absptr: + /* do nothing */ + break; + case DW_EH_PE_pcrel: + result += (uintptr_t)(*data); + break; + case DW_EH_PE_textrel: + case DW_EH_PE_datarel: + case DW_EH_PE_funcrel: + case DW_EH_PE_aligned: + default: + /* not supported */ + compilerrt_abort(); + break; + } + + /* then apply indirection */ + if (encoding & DW_EH_PE_indirect) { + result = *((uintptr_t*)result); + } + + *data = p; + return result; +} + + +/* + * The C compiler makes references to __gcc_personality_v0 in + * the dwarf unwind information for translation units that use + * __attribute__((cleanup(xx))) on local variables. + * This personality routine is called by the system unwinder + * on each frame as the stack is unwound during a C++ exception + * throw through a C function compiled with -fexceptions. + */ +#if __arm__ +// the setjump-longjump based exceptions personality routine has a different name +_Unwind_Reason_Code __gcc_personality_sj0(int version, _Unwind_Action actions, + uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, + _Unwind_Context_t context) +#else +_Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions, + uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, + _Unwind_Context_t context) +#endif +{ + /* Since C does not have catch clauses, there is nothing to do during */ + /* phase 1 (the search phase). */ + if ( actions & _UA_SEARCH_PHASE ) + return _URC_CONTINUE_UNWIND; + + /* There is nothing to do if there is no LSDA for this frame. */ + const uint8_t* lsda = _Unwind_GetLanguageSpecificData(context); + if ( lsda == (uint8_t*) 0 ) + return _URC_CONTINUE_UNWIND; + + uintptr_t pc = _Unwind_GetIP(context)-1; + uintptr_t funcStart = _Unwind_GetRegionStart(context); + uintptr_t pcOffset = pc - funcStart; + + /* Parse LSDA header. */ + uint8_t lpStartEncoding = *lsda++; + if (lpStartEncoding != DW_EH_PE_omit) { + readEncodedPointer(&lsda, lpStartEncoding); + } + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding != DW_EH_PE_omit) { + readULEB128(&lsda); + } + /* Walk call-site table looking for range that includes current PC. */ + uint8_t callSiteEncoding = *lsda++; + uint32_t callSiteTableLength = readULEB128(&lsda); + const uint8_t* callSiteTableStart = lsda; + const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; + const uint8_t* p=callSiteTableStart; + while (p < callSiteTableEnd) { + uintptr_t start = readEncodedPointer(&p, callSiteEncoding); + uintptr_t length = readEncodedPointer(&p, callSiteEncoding); + uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding); + readULEB128(&p); /* action value not used for C code */ + if ( landingPad == 0 ) + continue; /* no landing pad for this entry */ + if ( (start <= pcOffset) && (pcOffset < (start+length)) ) { + /* Found landing pad for the PC. + * Set Instruction Pointer to so we re-enter function + * at landing pad. The landing pad is created by the compiler + * to take two parameters in registers. + */ + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), + (uintptr_t)exceptionObject); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); + _Unwind_SetIP(context, funcStart+landingPad); + return _URC_INSTALL_CONTEXT; + } + } + + /* No landing pad found, continue unwinding. */ + return _URC_CONTINUE_UNWIND; +} + diff --git a/projects/compiler-rt/lib/i386/Makefile.mk b/projects/compiler-rt/lib/i386/Makefile.mk new file mode 100644 index 00000000..1f5c680c --- /dev/null +++ b/projects/compiler-rt/lib/i386/Makefile.mk @@ -0,0 +1,20 @@ +#===- lib/i386/Makefile.mk ---------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := builtins +SubDirs := +OnlyArchs := i386 + +AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) +ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o) +Implementation := Optimized + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard lib/*.h $(Dir)/*.h) diff --git a/projects/compiler-rt/lib/i386/ashldi3.S b/projects/compiler-rt/lib/i386/ashldi3.S new file mode 100644 index 00000000..5488ad6e --- /dev/null +++ b/projects/compiler-rt/lib/i386/ashldi3.S @@ -0,0 +1,56 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// di_int __ashldi3(di_int input, int count); + +// This routine has some extra memory traffic, loading the 64-bit input via two +// 32-bit loads, then immediately storing it back to the stack via a single 64-bit +// store. This is to avoid a write-small, read-large stall. +// However, if callers of this routine can be safely assumed to store the argument +// via a 64-bt store, this is unnecessary memory traffic, and should be avoided. +// It can be turned off by defining the TRUST_CALLERS_USE_64_BIT_STORES macro. + +#ifdef __i386__ +#ifdef __SSE2__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__ashldi3) + movd 12(%esp), %xmm2 // Load count +#ifndef TRUST_CALLERS_USE_64_BIT_STORES + movd 4(%esp), %xmm0 + movd 8(%esp), %xmm1 + punpckldq %xmm1, %xmm0 // Load input +#else + movq 4(%esp), %xmm0 // Load input +#endif + psllq %xmm2, %xmm0 // shift input by count + movd %xmm0, %eax + psrlq $32, %xmm0 + movd %xmm0, %edx + ret + +#else // Use GPRs instead of SSE2 instructions, if they aren't available. + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__ashldi3) + movl 12(%esp), %ecx // Load count + movl 8(%esp), %edx // Load high + movl 4(%esp), %eax // Load low + + testl $0x20, %ecx // If count >= 32 + jnz 1f // goto 1 + shldl %cl, %eax, %edx // left shift high by count + shll %cl, %eax // left shift low by count + ret + +1: movl %eax, %edx // Move low to high + xorl %eax, %eax // clear low + shll %cl, %edx // shift high by count - 32 + ret + +#endif // __SSE2__ +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/ashrdi3.S b/projects/compiler-rt/lib/i386/ashrdi3.S new file mode 100644 index 00000000..b1445ddb --- /dev/null +++ b/projects/compiler-rt/lib/i386/ashrdi3.S @@ -0,0 +1,67 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// di_int __ashrdi3(di_int input, int count); + +#ifdef __i386__ +#ifdef __SSE2__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__ashrdi3) + movd 12(%esp), %xmm2 // Load count + movl 8(%esp), %eax +#ifndef TRUST_CALLERS_USE_64_BIT_STORES + movd 4(%esp), %xmm0 + movd 8(%esp), %xmm1 + punpckldq %xmm1, %xmm0 // Load input +#else + movq 4(%esp), %xmm0 // Load input +#endif + + psrlq %xmm2, %xmm0 // unsigned shift input by count + + testl %eax, %eax // check the sign-bit of the input + jns 1f // early out for positive inputs + + // If the input is negative, we need to construct the shifted sign bit + // to or into the result, as xmm does not have a signed right shift. + pcmpeqb %xmm1, %xmm1 // -1ULL + psrlq $58, %xmm1 // 0x3f + pandn %xmm1, %xmm2 // 63 - count + pcmpeqb %xmm1, %xmm1 // -1ULL + psubq %xmm1, %xmm2 // 64 - count + psllq %xmm2, %xmm1 // -1 << (64 - count) = leading sign bits + por %xmm1, %xmm0 + + // Move the result back to the general purpose registers and return +1: movd %xmm0, %eax + psrlq $32, %xmm0 + movd %xmm0, %edx + ret + +#else // Use GPRs instead of SSE2 instructions, if they aren't available. + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__ashrdi3) + movl 12(%esp), %ecx // Load count + movl 8(%esp), %edx // Load high + movl 4(%esp), %eax // Load low + + testl $0x20, %ecx // If count >= 32 + jnz 1f // goto 1 + + shrdl %cl, %edx, %eax // right shift low by count + sarl %cl, %edx // right shift high by count + ret + +1: movl %edx, %eax // Move high to low + sarl $31, %edx // clear high + sarl %cl, %eax // shift low by count - 32 + ret + +#endif // __SSE2__ +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/divdi3.S b/projects/compiler-rt/lib/i386/divdi3.S new file mode 100644 index 00000000..69593e32 --- /dev/null +++ b/projects/compiler-rt/lib/i386/divdi3.S @@ -0,0 +1,161 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// di_int __divdi3(di_int a, di_int b); + +// result = a / b. +// both inputs and the output are 64-bit signed integers. +// This will do whatever the underlying hardware is set to do on division by zero. +// No other exceptions are generated, as the divide cannot overflow. +// +// This is targeted at 32-bit x86 *only*, as this can be done directly in hardware +// on x86_64. The performance goal is ~40 cycles per divide, which is faster than +// currently possible via simulation of integer divides on the x87 unit. +// +// Stephen Canon, December 2008 + +#ifdef __i386__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__divdi3) + +/* This is currently implemented by wrapping the unsigned divide up in an absolute + value, then restoring the correct sign at the end of the computation. This could + certainly be improved upon. */ + + pushl %esi + movl 20(%esp), %edx // high word of b + movl 16(%esp), %eax // low word of b + movl %edx, %ecx + sarl $31, %ecx // (b < 0) ? -1 : 0 + xorl %ecx, %eax + xorl %ecx, %edx // EDX:EAX = (b < 0) ? not(b) : b + subl %ecx, %eax + sbbl %ecx, %edx // EDX:EAX = abs(b) + movl %edx, 20(%esp) + movl %eax, 16(%esp) // store abs(b) back to stack + movl %ecx, %esi // set aside sign of b + + movl 12(%esp), %edx // high word of b + movl 8(%esp), %eax // low word of b + movl %edx, %ecx + sarl $31, %ecx // (a < 0) ? -1 : 0 + xorl %ecx, %eax + xorl %ecx, %edx // EDX:EAX = (a < 0) ? not(a) : a + subl %ecx, %eax + sbbl %ecx, %edx // EDX:EAX = abs(a) + movl %edx, 12(%esp) + movl %eax, 8(%esp) // store abs(a) back to stack + xorl %ecx, %esi // sign of result = (sign of a) ^ (sign of b) + + pushl %ebx + movl 24(%esp), %ebx // Find the index i of the leading bit in b. + bsrl %ebx, %ecx // If the high word of b is zero, jump to + jz 9f // the code to handle that special case [9]. + + /* High word of b is known to be non-zero on this branch */ + + movl 20(%esp), %eax // Construct bhi, containing bits [1+i:32+i] of b + + shrl %cl, %eax // Practically, this means that bhi is given by: + shrl %eax // + notl %ecx // bhi = (high word of b) << (31 - i) | + shll %cl, %ebx // (low word of b) >> (1 + i) + orl %eax, %ebx // + movl 16(%esp), %edx // Load the high and low words of a, and jump + movl 12(%esp), %eax // to [1] if the high word is larger than bhi + cmpl %ebx, %edx // to avoid overflowing the upcoming divide. + jae 1f + + /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + divl %ebx // eax <-- qs, edx <-- r such that ahi:alo = bs*qs + r + + pushl %edi + notl %ecx + shrl %eax + shrl %cl, %eax // q = qs >> (1 + i) + movl %eax, %edi + mull 24(%esp) // q*blo + movl 16(%esp), %ebx + movl 20(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 28(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + sbbl $0, %edi // decrement q if remainder is negative + xorl %edx, %edx + movl %edi, %eax + + addl %esi, %eax // Restore correct sign to result + adcl %esi, %edx + xorl %esi, %eax + xorl %esi, %edx + popl %edi // Restore callee-save registers + popl %ebx + popl %esi + retl // Return + + +1: /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + subl %ebx, %edx // subtract bhi from ahi so that divide will not + divl %ebx // overflow, and find q and r such that + // + // ahi:alo = (1:q)*bhi + r + // + // Note that q is a number in (31-i).(1+i) + // fix point. + + pushl %edi + notl %ecx + shrl %eax + orl $0x80000000, %eax + shrl %cl, %eax // q = (1:qs) >> (1 + i) + movl %eax, %edi + mull 24(%esp) // q*blo + movl 16(%esp), %ebx + movl 20(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 28(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + sbbl $0, %edi // decrement q if remainder is negative + xorl %edx, %edx + movl %edi, %eax + + addl %esi, %eax // Restore correct sign to result + adcl %esi, %edx + xorl %esi, %eax + xorl %esi, %edx + popl %edi // Restore callee-save registers + popl %ebx + popl %esi + retl // Return + + +9: /* High word of b is zero on this branch */ + + movl 16(%esp), %eax // Find qhi and rhi such that + movl 20(%esp), %ecx // + xorl %edx, %edx // ahi = qhi*b + rhi with 0 ≤ rhi < b + divl %ecx // + movl %eax, %ebx // + movl 12(%esp), %eax // Find qlo such that + divl %ecx // + movl %ebx, %edx // rhi:alo = qlo*b + rlo with 0 ≤ rlo < b + + addl %esi, %eax // Restore correct sign to result + adcl %esi, %edx + xorl %esi, %eax + xorl %esi, %edx + popl %ebx // Restore callee-save registers + popl %esi + retl // Return + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/floatdidf.S b/projects/compiler-rt/lib/i386/floatdidf.S new file mode 100644 index 00000000..a953d26c --- /dev/null +++ b/projects/compiler-rt/lib/i386/floatdidf.S @@ -0,0 +1,35 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// double __floatundidf(du_int a); + +#ifdef __i386__ + +#ifndef __ELF__ +.const +#endif +.align 4 +twop52: .quad 0x4330000000000000 +twop32: .quad 0x41f0000000000000 + +#define REL_ADDR(_a) (_a)-0b(%eax) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatdidf) + cvtsi2sd 8(%esp), %xmm1 + movss 4(%esp), %xmm0 // low 32 bits of a + calll 0f +0: popl %eax + mulsd REL_ADDR(twop32), %xmm1 // a_hi as a double (without rounding) + movsd REL_ADDR(twop52), %xmm2 // 0x1.0p52 + subsd %xmm2, %xmm1 // a_hi - 0x1p52 (no rounding occurs) + orpd %xmm2, %xmm0 // 0x1p52 + a_lo (no rounding occurs) + addsd %xmm1, %xmm0 // a_hi + a_lo (round happens here) + movsd %xmm0, 4(%esp) + fldl 4(%esp) + ret + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/floatdisf.S b/projects/compiler-rt/lib/i386/floatdisf.S new file mode 100644 index 00000000..a98a46ee --- /dev/null +++ b/projects/compiler-rt/lib/i386/floatdisf.S @@ -0,0 +1,31 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// float __floatdisf(di_int a); + +// This routine has some extra memory traffic, loading the 64-bit input via two +// 32-bit loads, then immediately storing it back to the stack via a single 64-bit +// store. This is to avoid a write-small, read-large stall. +// However, if callers of this routine can be safely assumed to store the argument +// via a 64-bt store, this is unnecessary memory traffic, and should be avoided. +// It can be turned off by defining the TRUST_CALLERS_USE_64_BIT_STORES macro. + +#ifdef __i386__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatdisf) +#ifndef TRUST_CALLERS_USE_64_BIT_STORES + movd 4(%esp), %xmm0 + movd 8(%esp), %xmm1 + punpckldq %xmm1, %xmm0 + movq %xmm0, 4(%esp) +#endif + fildll 4(%esp) + fstps 4(%esp) + flds 4(%esp) + ret + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/floatdixf.S b/projects/compiler-rt/lib/i386/floatdixf.S new file mode 100644 index 00000000..412976fb --- /dev/null +++ b/projects/compiler-rt/lib/i386/floatdixf.S @@ -0,0 +1,29 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// float __floatdixf(di_int a); + +#ifdef __i386__ + +// This routine has some extra memory traffic, loading the 64-bit input via two +// 32-bit loads, then immediately storing it back to the stack via a single 64-bit +// store. This is to avoid a write-small, read-large stall. +// However, if callers of this routine can be safely assumed to store the argument +// via a 64-bt store, this is unnecessary memory traffic, and should be avoided. +// It can be turned off by defining the TRUST_CALLERS_USE_64_BIT_STORES macro. + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatdixf) +#ifndef TRUST_CALLERS_USE_64_BIT_STORES + movd 4(%esp), %xmm0 + movd 8(%esp), %xmm1 + punpckldq %xmm1, %xmm0 + movq %xmm0, 4(%esp) +#endif + fildll 4(%esp) + ret + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/floatundidf.S b/projects/compiler-rt/lib/i386/floatundidf.S new file mode 100644 index 00000000..6bba7e1c --- /dev/null +++ b/projects/compiler-rt/lib/i386/floatundidf.S @@ -0,0 +1,46 @@ +//===-- floatundidf.S - Implement __floatundidf for i386 ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements __floatundidf for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// double __floatundidf(du_int a); + +#ifdef __i386__ + +#ifndef __ELF__ +.const +#endif +.align 4 +twop52: .quad 0x4330000000000000 +twop84_plus_twop52: + .quad 0x4530000000100000 +twop84: .quad 0x4530000000000000 + +#define REL_ADDR(_a) (_a)-0b(%eax) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundidf) + movss 8(%esp), %xmm1 // high 32 bits of a + movss 4(%esp), %xmm0 // low 32 bits of a + calll 0f +0: popl %eax + orpd REL_ADDR(twop84), %xmm1 // 0x1p84 + a_hi (no rounding occurs) + subsd REL_ADDR(twop84_plus_twop52), %xmm1 // a_hi - 0x1p52 (no rounding occurs) + orpd REL_ADDR(twop52), %xmm0 // 0x1p52 + a_lo (no rounding occurs) + addsd %xmm1, %xmm0 // a_hi + a_lo (round happens here) + movsd %xmm0, 4(%esp) + fldl 4(%esp) + ret + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/floatundisf.S b/projects/compiler-rt/lib/i386/floatundisf.S new file mode 100644 index 00000000..1afd1d4d --- /dev/null +++ b/projects/compiler-rt/lib/i386/floatundisf.S @@ -0,0 +1,99 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// float __floatundisf(du_int a); + +// Note that there is a hardware instruction, fildll, that does most of what +// this function needs to do. However, because of our ia32 ABI, it will take +// a write-small read-large stall, so the software implementation here is +// actually several cycles faster. + +// This is a branch-free implementation. A branchy implementation might be +// faster for the common case if you know something a priori about the input +// distribution. + +/* branch-free x87 implementation - one cycle slower than without x87. + +#ifdef __i386__ + +.const +.align 3 + + .quad 0x43f0000000000000 +twop64: .quad 0x0000000000000000 + +#define TWOp64 twop64-0b(%ecx,%eax,8) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundisf) + movl 8(%esp), %eax + movd 8(%esp), %xmm1 + movd 4(%esp), %xmm0 + punpckldq %xmm1, %xmm0 + calll 0f +0: popl %ecx + sarl $31, %eax + movq %xmm0, 4(%esp) + fildll 4(%esp) + faddl TWOp64 + fstps 4(%esp) + flds 4(%esp) + ret + +#endif // __i386__ + +*/ + +/* branch-free, x87-free implementation - faster at the expense of code size */ + +#ifdef __i386__ + +#ifndef __ELF__ +.const +.align 3 +#else +.align 8 +#endif +twop52: .quad 0x4330000000000000 + .quad 0x0000000000000fff +sticky: .quad 0x0000000000000000 + .long 0x00000012 +twelve: .long 0x00000000 + +#define TWOp52 twop52-0b(%ecx) +#define STICKY sticky-0b(%ecx,%eax,8) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundisf) + movl 8(%esp), %eax + movd 8(%esp), %xmm1 + movd 4(%esp), %xmm0 + punpckldq %xmm1, %xmm0 + + calll 0f +0: popl %ecx + shrl %eax // high 31 bits of input as sint32 + addl $0x7ff80000, %eax + sarl $31, %eax // (big input) ? -1 : 0 + movsd STICKY, %xmm1 // (big input) ? 0xfff : 0 + movl $12, %edx + andl %eax, %edx // (big input) ? 12 : 0 + movd %edx, %xmm3 + andpd %xmm0, %xmm1 // (big input) ? input & 0xfff : 0 + movsd TWOp52, %xmm2 // 0x1.0p52 + psrlq %xmm3, %xmm0 // (big input) ? input >> 12 : input + orpd %xmm2, %xmm1 // 0x1.0p52 + ((big input) ? input & 0xfff : input) + orpd %xmm1, %xmm0 // 0x1.0p52 + ((big input) ? (input >> 12 | input & 0xfff) : input) + subsd %xmm2, %xmm0 // (double)((big input) ? (input >> 12 | input & 0xfff) : input) + cvtsd2ss %xmm0, %xmm0 // (float)((big input) ? (input >> 12 | input & 0xfff) : input) + pslld $23, %xmm3 + paddd %xmm3, %xmm0 // (float)input + movd %xmm0, 4(%esp) + flds 4(%esp) + ret + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/floatundixf.S b/projects/compiler-rt/lib/i386/floatundixf.S new file mode 100644 index 00000000..6e6710bd --- /dev/null +++ b/projects/compiler-rt/lib/i386/floatundixf.S @@ -0,0 +1,37 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// long double __floatundixf(du_int a);16 + +#ifdef __i386__ + +#ifndef __ELF__ +.const +#endif +.align 4 +twop52: .quad 0x4330000000000000 +twop84_plus_twop52_neg: + .quad 0xc530000000100000 +twop84: .quad 0x4530000000000000 + +#define REL_ADDR(_a) (_a)-0b(%eax) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundixf) + calll 0f +0: popl %eax + movss 8(%esp), %xmm0 // hi 32 bits of input + movss 4(%esp), %xmm1 // lo 32 bits of input + orpd REL_ADDR(twop84), %xmm0 // 2^84 + hi (as a double) + orpd REL_ADDR(twop52), %xmm1 // 2^52 + lo (as a double) + addsd REL_ADDR(twop84_plus_twop52_neg), %xmm0 // hi - 2^52 (no rounding occurs) + movsd %xmm1, 4(%esp) + fldl 4(%esp) + movsd %xmm0, 4(%esp) + faddl 4(%esp) + ret + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/lshrdi3.S b/projects/compiler-rt/lib/i386/lshrdi3.S new file mode 100644 index 00000000..cf411f22 --- /dev/null +++ b/projects/compiler-rt/lib/i386/lshrdi3.S @@ -0,0 +1,57 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// di_int __lshrdi3(di_int input, int count); + +// This routine has some extra memory traffic, loading the 64-bit input via two +// 32-bit loads, then immediately storing it back to the stack via a single 64-bit +// store. This is to avoid a write-small, read-large stall. +// However, if callers of this routine can be safely assumed to store the argument +// via a 64-bt store, this is unnecessary memory traffic, and should be avoided. +// It can be turned off by defining the TRUST_CALLERS_USE_64_BIT_STORES macro. + +#ifdef __i386__ +#ifdef __SSE2__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__lshrdi3) + movd 12(%esp), %xmm2 // Load count +#ifndef TRUST_CALLERS_USE_64_BIT_STORES + movd 4(%esp), %xmm0 + movd 8(%esp), %xmm1 + punpckldq %xmm1, %xmm0 // Load input +#else + movq 4(%esp), %xmm0 // Load input +#endif + psrlq %xmm2, %xmm0 // shift input by count + movd %xmm0, %eax + psrlq $32, %xmm0 + movd %xmm0, %edx + ret + +#else // Use GPRs instead of SSE2 instructions, if they aren't available. + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__lshrdi3) + movl 12(%esp), %ecx // Load count + movl 8(%esp), %edx // Load high + movl 4(%esp), %eax // Load low + + testl $0x20, %ecx // If count >= 32 + jnz 1f // goto 1 + + shrdl %cl, %edx, %eax // right shift low by count + shrl %cl, %edx // right shift high by count + ret + +1: movl %edx, %eax // Move high to low + xorl %edx, %edx // clear high + shrl %cl, %eax // shift low by count - 32 + ret + +#endif // __SSE2__ +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/moddi3.S b/projects/compiler-rt/lib/i386/moddi3.S new file mode 100644 index 00000000..8839cfc5 --- /dev/null +++ b/projects/compiler-rt/lib/i386/moddi3.S @@ -0,0 +1,166 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// di_int __moddi3(di_int a, di_int b); + +// result = remainder of a / b. +// both inputs and the output are 64-bit signed integers. +// This will do whatever the underlying hardware is set to do on division by zero. +// No other exceptions are generated, as the divide cannot overflow. +// +// This is targeted at 32-bit x86 *only*, as this can be done directly in hardware +// on x86_64. The performance goal is ~40 cycles per divide, which is faster than +// currently possible via simulation of integer divides on the x87 unit. +// + +// Stephen Canon, December 2008 + +#ifdef __i386__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__moddi3) + +/* This is currently implemented by wrapping the unsigned modulus up in an absolute + value. This could certainly be improved upon. */ + + pushl %esi + movl 20(%esp), %edx // high word of b + movl 16(%esp), %eax // low word of b + movl %edx, %ecx + sarl $31, %ecx // (b < 0) ? -1 : 0 + xorl %ecx, %eax + xorl %ecx, %edx // EDX:EAX = (b < 0) ? not(b) : b + subl %ecx, %eax + sbbl %ecx, %edx // EDX:EAX = abs(b) + movl %edx, 20(%esp) + movl %eax, 16(%esp) // store abs(b) back to stack + + movl 12(%esp), %edx // high word of b + movl 8(%esp), %eax // low word of b + movl %edx, %ecx + sarl $31, %ecx // (a < 0) ? -1 : 0 + xorl %ecx, %eax + xorl %ecx, %edx // EDX:EAX = (a < 0) ? not(a) : a + subl %ecx, %eax + sbbl %ecx, %edx // EDX:EAX = abs(a) + movl %edx, 12(%esp) + movl %eax, 8(%esp) // store abs(a) back to stack + movl %ecx, %esi // set aside sign of a + + pushl %ebx + movl 24(%esp), %ebx // Find the index i of the leading bit in b. + bsrl %ebx, %ecx // If the high word of b is zero, jump to + jz 9f // the code to handle that special case [9]. + + /* High word of b is known to be non-zero on this branch */ + + movl 20(%esp), %eax // Construct bhi, containing bits [1+i:32+i] of b + + shrl %cl, %eax // Practically, this means that bhi is given by: + shrl %eax // + notl %ecx // bhi = (high word of b) << (31 - i) | + shll %cl, %ebx // (low word of b) >> (1 + i) + orl %eax, %ebx // + movl 16(%esp), %edx // Load the high and low words of a, and jump + movl 12(%esp), %eax // to [2] if the high word is larger than bhi + cmpl %ebx, %edx // to avoid overflowing the upcoming divide. + jae 2f + + /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + divl %ebx // eax <-- qs, edx <-- r such that ahi:alo = bs*qs + r + + pushl %edi + notl %ecx + shrl %eax + shrl %cl, %eax // q = qs >> (1 + i) + movl %eax, %edi + mull 24(%esp) // q*blo + movl 16(%esp), %ebx + movl 20(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 28(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + + jnc 1f // if positive, this is the result. + addl 24(%esp), %ebx // otherwise + adcl 28(%esp), %ecx // ECX:EBX = a - (q-1)*b = result +1: movl %ebx, %eax + movl %ecx, %edx + + addl %esi, %eax // Restore correct sign to result + adcl %esi, %edx + xorl %esi, %eax + xorl %esi, %edx + popl %edi // Restore callee-save registers + popl %ebx + popl %esi + retl // Return + +2: /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + subl %ebx, %edx // subtract bhi from ahi so that divide will not + divl %ebx // overflow, and find q and r such that + // + // ahi:alo = (1:q)*bhi + r + // + // Note that q is a number in (31-i).(1+i) + // fix point. + + pushl %edi + notl %ecx + shrl %eax + orl $0x80000000, %eax + shrl %cl, %eax // q = (1:qs) >> (1 + i) + movl %eax, %edi + mull 24(%esp) // q*blo + movl 16(%esp), %ebx + movl 20(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 28(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + + jnc 3f // if positive, this is the result. + addl 24(%esp), %ebx // otherwise + adcl 28(%esp), %ecx // ECX:EBX = a - (q-1)*b = result +3: movl %ebx, %eax + movl %ecx, %edx + + addl %esi, %eax // Restore correct sign to result + adcl %esi, %edx + xorl %esi, %eax + xorl %esi, %edx + popl %edi // Restore callee-save registers + popl %ebx + popl %esi + retl // Return + +9: /* High word of b is zero on this branch */ + + movl 16(%esp), %eax // Find qhi and rhi such that + movl 20(%esp), %ecx // + xorl %edx, %edx // ahi = qhi*b + rhi with 0 ≤ rhi < b + divl %ecx // + movl %eax, %ebx // + movl 12(%esp), %eax // Find rlo such that + divl %ecx // + movl %edx, %eax // rhi:alo = qlo*b + rlo with 0 ≤ rlo < b + popl %ebx // + xorl %edx, %edx // and return 0:rlo + + addl %esi, %eax // Restore correct sign to result + adcl %esi, %edx + xorl %esi, %eax + xorl %esi, %edx + popl %esi + retl // Return + + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/muldi3.S b/projects/compiler-rt/lib/i386/muldi3.S new file mode 100644 index 00000000..e56a3553 --- /dev/null +++ b/projects/compiler-rt/lib/i386/muldi3.S @@ -0,0 +1,29 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// di_int __muldi3(di_int a, di_int b); + +#ifdef __i386__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__muldi3) + pushl %ebx + movl 16(%esp), %eax // b.lo + movl 12(%esp), %ecx // a.hi + imull %eax, %ecx // b.lo * a.hi + + movl 8(%esp), %edx // a.lo + movl 20(%esp), %ebx // b.hi + imull %edx, %ebx // a.lo * b.hi + + mull %edx // EDX:EAX = a.lo * b.lo + addl %ecx, %ebx // EBX = (a.lo*b.hi + a.hi*b.lo) + addl %ebx, %edx + + popl %ebx + retl + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/udivdi3.S b/projects/compiler-rt/lib/i386/udivdi3.S new file mode 100644 index 00000000..5abeaeaa --- /dev/null +++ b/projects/compiler-rt/lib/i386/udivdi3.S @@ -0,0 +1,114 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// du_int __udivdi3(du_int a, du_int b); + +// result = a / b. +// both inputs and the output are 64-bit unsigned integers. +// This will do whatever the underlying hardware is set to do on division by zero. +// No other exceptions are generated, as the divide cannot overflow. +// +// This is targeted at 32-bit x86 *only*, as this can be done directly in hardware +// on x86_64. The performance goal is ~40 cycles per divide, which is faster than +// currently possible via simulation of integer divides on the x87 unit. +// +// Stephen Canon, December 2008 + +#ifdef __i386__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__udivdi3) + + pushl %ebx + movl 20(%esp), %ebx // Find the index i of the leading bit in b. + bsrl %ebx, %ecx // If the high word of b is zero, jump to + jz 9f // the code to handle that special case [9]. + + /* High word of b is known to be non-zero on this branch */ + + movl 16(%esp), %eax // Construct bhi, containing bits [1+i:32+i] of b + + shrl %cl, %eax // Practically, this means that bhi is given by: + shrl %eax // + notl %ecx // bhi = (high word of b) << (31 - i) | + shll %cl, %ebx // (low word of b) >> (1 + i) + orl %eax, %ebx // + movl 12(%esp), %edx // Load the high and low words of a, and jump + movl 8(%esp), %eax // to [1] if the high word is larger than bhi + cmpl %ebx, %edx // to avoid overflowing the upcoming divide. + jae 1f + + /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + divl %ebx // eax <-- qs, edx <-- r such that ahi:alo = bs*qs + r + + pushl %edi + notl %ecx + shrl %eax + shrl %cl, %eax // q = qs >> (1 + i) + movl %eax, %edi + mull 20(%esp) // q*blo + movl 12(%esp), %ebx + movl 16(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 24(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + sbbl $0, %edi // decrement q if remainder is negative + xorl %edx, %edx + movl %edi, %eax + popl %edi + popl %ebx + retl + + +1: /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + subl %ebx, %edx // subtract bhi from ahi so that divide will not + divl %ebx // overflow, and find q and r such that + // + // ahi:alo = (1:q)*bhi + r + // + // Note that q is a number in (31-i).(1+i) + // fix point. + + pushl %edi + notl %ecx + shrl %eax + orl $0x80000000, %eax + shrl %cl, %eax // q = (1:qs) >> (1 + i) + movl %eax, %edi + mull 20(%esp) // q*blo + movl 12(%esp), %ebx + movl 16(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 24(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + sbbl $0, %edi // decrement q if remainder is negative + xorl %edx, %edx + movl %edi, %eax + popl %edi + popl %ebx + retl + + +9: /* High word of b is zero on this branch */ + + movl 12(%esp), %eax // Find qhi and rhi such that + movl 16(%esp), %ecx // + xorl %edx, %edx // ahi = qhi*b + rhi with 0 ≤ rhi < b + divl %ecx // + movl %eax, %ebx // + movl 8(%esp), %eax // Find qlo such that + divl %ecx // + movl %ebx, %edx // rhi:alo = qlo*b + rlo with 0 ≤ rlo < b + popl %ebx // + retl // and return qhi:qlo + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/i386/umoddi3.S b/projects/compiler-rt/lib/i386/umoddi3.S new file mode 100644 index 00000000..7fd84853 --- /dev/null +++ b/projects/compiler-rt/lib/i386/umoddi3.S @@ -0,0 +1,125 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// du_int __umoddi3(du_int a, du_int b); + +// result = remainder of a / b. +// both inputs and the output are 64-bit unsigned integers. +// This will do whatever the underlying hardware is set to do on division by zero. +// No other exceptions are generated, as the divide cannot overflow. +// +// This is targeted at 32-bit x86 *only*, as this can be done directly in hardware +// on x86_64. The performance goal is ~40 cycles per divide, which is faster than +// currently possible via simulation of integer divides on the x87 unit. +// + +// Stephen Canon, December 2008 + +#ifdef __i386__ + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__umoddi3) + + pushl %ebx + movl 20(%esp), %ebx // Find the index i of the leading bit in b. + bsrl %ebx, %ecx // If the high word of b is zero, jump to + jz 9f // the code to handle that special case [9]. + + /* High word of b is known to be non-zero on this branch */ + + movl 16(%esp), %eax // Construct bhi, containing bits [1+i:32+i] of b + + shrl %cl, %eax // Practically, this means that bhi is given by: + shrl %eax // + notl %ecx // bhi = (high word of b) << (31 - i) | + shll %cl, %ebx // (low word of b) >> (1 + i) + orl %eax, %ebx // + movl 12(%esp), %edx // Load the high and low words of a, and jump + movl 8(%esp), %eax // to [2] if the high word is larger than bhi + cmpl %ebx, %edx // to avoid overflowing the upcoming divide. + jae 2f + + /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + divl %ebx // eax <-- qs, edx <-- r such that ahi:alo = bs*qs + r + + pushl %edi + notl %ecx + shrl %eax + shrl %cl, %eax // q = qs >> (1 + i) + movl %eax, %edi + mull 20(%esp) // q*blo + movl 12(%esp), %ebx + movl 16(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 24(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + + jnc 1f // if positive, this is the result. + addl 20(%esp), %ebx // otherwise + adcl 24(%esp), %ecx // ECX:EBX = a - (q-1)*b = result +1: movl %ebx, %eax + movl %ecx, %edx + + popl %edi + popl %ebx + retl + + +2: /* High word of a is greater than or equal to (b >> (1 + i)) on this branch */ + + subl %ebx, %edx // subtract bhi from ahi so that divide will not + divl %ebx // overflow, and find q and r such that + // + // ahi:alo = (1:q)*bhi + r + // + // Note that q is a number in (31-i).(1+i) + // fix point. + + pushl %edi + notl %ecx + shrl %eax + orl $0x80000000, %eax + shrl %cl, %eax // q = (1:qs) >> (1 + i) + movl %eax, %edi + mull 20(%esp) // q*blo + movl 12(%esp), %ebx + movl 16(%esp), %ecx // ECX:EBX = a + subl %eax, %ebx + sbbl %edx, %ecx // ECX:EBX = a - q*blo + movl 24(%esp), %eax + imull %edi, %eax // q*bhi + subl %eax, %ecx // ECX:EBX = a - q*b + + jnc 3f // if positive, this is the result. + addl 20(%esp), %ebx // otherwise + adcl 24(%esp), %ecx // ECX:EBX = a - (q-1)*b = result +3: movl %ebx, %eax + movl %ecx, %edx + + popl %edi + popl %ebx + retl + + + +9: /* High word of b is zero on this branch */ + + movl 12(%esp), %eax // Find qhi and rhi such that + movl 16(%esp), %ecx // + xorl %edx, %edx // ahi = qhi*b + rhi with 0 ≤ rhi < b + divl %ecx // + movl %eax, %ebx // + movl 8(%esp), %eax // Find rlo such that + divl %ecx // + movl %edx, %eax // rhi:alo = qlo*b + rlo with 0 ≤ rlo < b + popl %ebx // + xorl %edx, %edx // and return 0:rlo + retl // + +#endif // __i386__ diff --git a/projects/compiler-rt/lib/int_endianness.h b/projects/compiler-rt/lib/int_endianness.h new file mode 100644 index 00000000..a64f9264 --- /dev/null +++ b/projects/compiler-rt/lib/int_endianness.h @@ -0,0 +1,111 @@ +/* ===-- int_endianness.h - configuration header for compiler-rt ------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file is a configuration header for compiler-rt. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef INT_ENDIANNESS_H +#define INT_ENDIANNESS_H + +#if defined(__SVR4) && defined(__sun) +#include + +#if defined(_BIG_ENDIAN) +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif defined(_LITTLE_ENDIAN) +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#else /* !_LITTLE_ENDIAN */ +#error "unknown endianness" +#endif /* !_LITTLE_ENDIAN */ + +#endif /* Solaris and AuroraUX. */ + +/* .. */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__minix) +#include + +#if _BYTE_ORDER == _BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif _BYTE_ORDER == _LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* _BYTE_ORDER */ + +#endif /* *BSD */ + +#if defined(__OpenBSD__) || defined(__Bitrig__) +#include + +#if _BYTE_ORDER == _BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif _BYTE_ORDER == _LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* _BYTE_ORDER */ + +#endif /* OpenBSD and Bitrig. */ + +/* .. */ + +/* Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the compiler (at least with GCC) */ +#if defined(__APPLE__) && defined(__MACH__) || defined(__ellcc__ ) + +#ifdef __BIG_ENDIAN__ +#if __BIG_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#endif +#endif /* __BIG_ENDIAN__ */ + +#ifdef __LITTLE_ENDIAN__ +#if __LITTLE_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif +#endif /* __LITTLE_ENDIAN__ */ + +#endif /* Mac OSX */ + +/* .. */ + +#if defined(__linux__) +#include + +#if __BYTE_ORDER == __BIG_ENDIAN +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* __BYTE_ORDER */ + +#endif /* GNU/Linux */ + +#if defined(_WIN32) + +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 + +#endif /* Windows */ + +/* . */ + +#if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN) +#error Unable to determine endian +#endif /* Check we found an endianness correctly. */ + +#endif /* INT_ENDIANNESS_H */ diff --git a/projects/compiler-rt/lib/int_lib.h b/projects/compiler-rt/lib/int_lib.h new file mode 100644 index 00000000..a87426c5 --- /dev/null +++ b/projects/compiler-rt/lib/int_lib.h @@ -0,0 +1,46 @@ +/* ===-- int_lib.h - configuration header for compiler-rt -----------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file is a configuration header for compiler-rt. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef INT_LIB_H +#define INT_LIB_H + +/* Assumption: Signed integral is 2's complement. */ +/* Assumption: Right shift of signed negative is arithmetic shift. */ +/* Assumption: Endianness is little or big (not mixed). */ + +/* ABI macro definitions */ + +#if __ARM_EABI__ +# define ARM_EABI_FNALIAS(aeabi_name, name) \ + void __aeabi_##aeabi_name() __attribute__((alias("__" #name))); +# define COMPILER_RT_ABI __attribute__((pcs("aapcs"))) +#else +# define ARM_EABI_FNALIAS(aeabi_name, name) +# define COMPILER_RT_ABI +#endif + +/* Include the standard compiler builtin headers we use functionality from. */ +#include +#include +#include +#include + +/* Include the commonly used internal type definitions. */ +#include "int_types.h" + +/* Include internal utility function declarations. */ +#include "int_util.h" + +#endif /* INT_LIB_H */ diff --git a/projects/compiler-rt/lib/int_math.h b/projects/compiler-rt/lib/int_math.h new file mode 100644 index 00000000..d6b4bdae --- /dev/null +++ b/projects/compiler-rt/lib/int_math.h @@ -0,0 +1,67 @@ +/* ===-- int_math.h - internal math inlines ---------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This file is not part of the interface of this library. + * + * This file defines substitutes for the libm functions used in some of the + * compiler-rt implementations, defined in such a way that there is not a direct + * dependency on libm or math.h. Instead, we use the compiler builtin versions + * where available. This reduces our dependencies on the system SDK by foisting + * the responsibility onto the compiler. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef INT_MATH_H +#define INT_MATH_H + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#define CRT_INFINITY __builtin_huge_valf() + +#define crt_isinf(x) __builtin_isinf((x)) +#define crt_isnan(x) __builtin_isnan((x)) + +/* Define crt_isfinite in terms of the builtin if available, otherwise provide + * an alternate version in terms of our other functions. This supports some + * versions of GCC which didn't have __builtin_isfinite. + */ +#if __has_builtin(__builtin_isfinite) +# define crt_isfinite(x) __builtin_isfinite((x)) +#else +# define crt_isfinite(x) \ + __extension__(({ \ + __typeof((x)) x_ = (x); \ + !crt_isinf(x_) && !crt_isnan(x_); \ + })) +#endif + +#define crt_copysign(x, y) __builtin_copysign((x), (y)) +#define crt_copysignf(x, y) __builtin_copysignf((x), (y)) +#define crt_copysignl(x, y) __builtin_copysignl((x), (y)) + +#define crt_fabs(x) __builtin_fabs((x)) +#define crt_fabsf(x) __builtin_fabsf((x)) +#define crt_fabsl(x) __builtin_fabsl((x)) + +#define crt_fmax(x, y) __builtin_fmax((x), (y)) +#define crt_fmaxf(x, y) __builtin_fmaxf((x), (y)) +#define crt_fmaxl(x, y) __builtin_fmaxl((x), (y)) + +#define crt_logb(x) __builtin_logb((x)) +#define crt_logbf(x) __builtin_logbf((x)) +#define crt_logbl(x) __builtin_logbl((x)) + +#define crt_scalbn(x, y) __builtin_scalbn((x), (y)) +#define crt_scalbnf(x, y) __builtin_scalbnf((x), (y)) +#define crt_scalbnl(x, y) __builtin_scalbnl((x), (y)) + +#endif /* INT_MATH_H */ diff --git a/projects/compiler-rt/lib/int_types.h b/projects/compiler-rt/lib/int_types.h new file mode 100644 index 00000000..fcce390f --- /dev/null +++ b/projects/compiler-rt/lib/int_types.h @@ -0,0 +1,140 @@ +/* ===-- int_lib.h - configuration header for compiler-rt -----------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file is not part of the interface of this library. + * + * This file defines various standard types, most importantly a number of unions + * used to access parts of larger types. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef INT_TYPES_H +#define INT_TYPES_H + +#include "int_endianness.h" + +typedef int si_int; +typedef unsigned su_int; + +typedef long long di_int; +typedef unsigned long long du_int; + +typedef union +{ + di_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + su_int low; + si_int high; +#else + si_int high; + su_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} dwords; + +typedef union +{ + du_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + su_int low; + su_int high; +#else + su_int high; + su_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} udwords; + +#if __x86_64 + +typedef int ti_int __attribute__ ((mode (TI))); +typedef unsigned tu_int __attribute__ ((mode (TI))); + +typedef union +{ + ti_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + du_int low; + di_int high; +#else + di_int high; + du_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} twords; + +typedef union +{ + tu_int all; + struct + { +#if _YUGA_LITTLE_ENDIAN + du_int low; + du_int high; +#else + du_int high; + du_int low; +#endif /* _YUGA_LITTLE_ENDIAN */ + }s; +} utwords; + +static inline ti_int make_ti(di_int h, di_int l) { + twords r; + r.s.high = h; + r.s.low = l; + return r.all; +} + +static inline tu_int make_tu(du_int h, du_int l) { + utwords r; + r.s.high = h; + r.s.low = l; + return r.all; +} + +#endif /* __x86_64 */ + +typedef union +{ + su_int u; + float f; +} float_bits; + +typedef union +{ + udwords u; + double f; +} double_bits; + +typedef struct +{ +#if _YUGA_LITTLE_ENDIAN + udwords low; + udwords high; +#else + udwords high; + udwords low; +#endif /* _YUGA_LITTLE_ENDIAN */ +} uqwords; + +typedef union +{ + uqwords u; + long double f; +} long_double_bits; + +#endif /* INT_TYPES_H */ + diff --git a/projects/compiler-rt/lib/int_util.c b/projects/compiler-rt/lib/int_util.c new file mode 100644 index 00000000..323e4617 --- /dev/null +++ b/projects/compiler-rt/lib/int_util.c @@ -0,0 +1,61 @@ +/* ===-- int_util.c - Implement internal utilities --------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_util.h" +#include "int_lib.h" + +/* NOTE: The definitions in this file are declared weak because we clients to be + * able to arbitrarily package individual functions into separate .a files. If + * we did not declare these weak, some link situations might end up seeing + * duplicate strong definitions of the same symbol. + * + * We can't use this solution for kernel use (which may not support weak), but + * currently expect that when built for kernel use all the functionality is + * packaged into a single library. + */ + +#ifdef KERNEL_USE + +extern void panic(const char *, ...) __attribute__((noreturn)); +#ifndef _WIN32 +__attribute__((visibility("hidden"))) +#endif +void compilerrt_abort_impl(const char *file, int line, const char *function) { + panic("%s:%d: abort in %s", file, line, function); +} + +#elif __APPLE__ + +/* from libSystem.dylib */ +extern void __assert_rtn(const char *func, const char *file, + int line, const char * message) __attribute__((noreturn)); + +#ifndef _WIN32 +__attribute__((weak)) +__attribute__((visibility("hidden"))) +#endif +void compilerrt_abort_impl(const char *file, int line, const char *function) { + __assert_rtn(function, file, line, "libcompiler_rt abort"); +} + +#else + +/* Get the system definition of abort() */ +#include + +#ifndef _WIN32 +__attribute__((weak)) +__attribute__((visibility("hidden"))) +#endif +void compilerrt_abort_impl(const char *file, int line, const char *function) { + abort(); +} + +#endif diff --git a/projects/compiler-rt/lib/int_util.h b/projects/compiler-rt/lib/int_util.h new file mode 100644 index 00000000..1348b85e --- /dev/null +++ b/projects/compiler-rt/lib/int_util.h @@ -0,0 +1,29 @@ +/* ===-- int_util.h - internal utility functions ----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===-----------------------------------------------------------------------=== + * + * This file is not part of the interface of this library. + * + * This file defines non-inline utilities which are available for use in the + * library. The function definitions themselves are all contained in int_util.c + * which will always be compiled into any compiler-rt library. + * + * ===-----------------------------------------------------------------------=== + */ + +#ifndef INT_UTIL_H +#define INT_UTIL_H + +/** \brief Trigger a program abort (or panic for kernel code). */ +#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, \ + __FUNCTION__) + +void compilerrt_abort_impl(const char *file, int line, + const char *function) __attribute__((noreturn)); + +#endif /* INT_UTIL_H */ diff --git a/projects/compiler-rt/lib/interception/CMakeLists.txt b/projects/compiler-rt/lib/interception/CMakeLists.txt new file mode 100644 index 00000000..1dfdaffb --- /dev/null +++ b/projects/compiler-rt/lib/interception/CMakeLists.txt @@ -0,0 +1,32 @@ +# Build for the runtime interception helper library. + +set(INTERCEPTION_SOURCES + interception_linux.cc + interception_mac.cc + interception_win.cc + interception_type_test.cc + ) + +include_directories(..) + +set(INTERCEPTION_CFLAGS ${SANITIZER_COMMON_CFLAGS}) + +if(APPLE) + # Build universal binary on APPLE. + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTInterception ${os} + ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${INTERCEPTION_SOURCES} + CFLAGS ${INTERCEPTION_CFLAGS}) + endforeach() +elseif(ANDROID) + add_library(RTInterception.arm.android OBJECT ${INTERCEPTION_SOURCES}) + set_target_compile_flags(RTInterception.arm.android + ${INTERCEPTION_CFLAGS}) +else() + # Otherwise, build separate libraries for each target. + foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH}) + add_compiler_rt_object_library(RTInterception ${arch} + SOURCES ${INTERCEPTION_SOURCES} CFLAGS ${INTERCEPTION_CFLAGS}) + endforeach() +endif() diff --git a/projects/compiler-rt/lib/interception/Makefile.mk b/projects/compiler-rt/lib/interception/Makefile.mk new file mode 100644 index 00000000..88aa6cbc --- /dev/null +++ b/projects/compiler-rt/lib/interception/Makefile.mk @@ -0,0 +1,23 @@ +#===- lib/interception/Makefile.mk -------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := interception +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the interception functions. +InterceptionFunctions := $(Sources:%.cc=%) diff --git a/projects/compiler-rt/lib/interception/interception.h b/projects/compiler-rt/lib/interception/interception.h new file mode 100644 index 00000000..baddd6c1 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception.h @@ -0,0 +1,257 @@ +//===-- interception.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Machinery for providing replacements/wrappers for system functions. +//===----------------------------------------------------------------------===// + +#ifndef INTERCEPTION_H +#define INTERCEPTION_H + +#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) +# error "Interception doesn't work on this operating system." +#endif + +#include "sanitizer_common/sanitizer_internal_defs.h" + +// These typedefs should be used only in the interceptor definitions to replace +// the standard system types (e.g. SSIZE_T instead of ssize_t) +typedef __sanitizer::uptr SIZE_T; +typedef __sanitizer::sptr SSIZE_T; +typedef __sanitizer::sptr PTRDIFF_T; +typedef __sanitizer::s64 INTMAX_T; +typedef __sanitizer::OFF_T OFF_T; +typedef __sanitizer::OFF64_T OFF64_T; + +// How to add an interceptor: +// Suppose you need to wrap/replace system function (generally, from libc): +// int foo(const char *bar, double baz); +// You'll need to: +// 1) define INTERCEPTOR(int, foo, const char *bar, double baz) { ... } in +// your source file. See the notes below for cases when +// INTERCEPTOR_WITH_SUFFIX(...) should be used instead. +// 2) Call "INTERCEPT_FUNCTION(foo)" prior to the first call of "foo". +// INTERCEPT_FUNCTION(foo) evaluates to "true" iff the function was +// intercepted successfully. +// You can access original function by calling REAL(foo)(bar, baz). +// By default, REAL(foo) will be visible only inside your interceptor, and if +// you want to use it in other parts of RTL, you'll need to: +// 3a) add DECLARE_REAL(int, foo, const char*, double) to a +// header file. +// However, if the call "INTERCEPT_FUNCTION(foo)" and definition for +// INTERCEPTOR(..., foo, ...) are in different files, you'll instead need to: +// 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double) +// to a header file. + +// Notes: 1. Things may not work properly if macro INTERCEPTOR(...) {...} or +// DECLARE_REAL(...) are located inside namespaces. +// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo)" to +// effectively redirect calls from "foo" to "zoo". In this case +// you aren't required to implement +// INTERCEPTOR(int, foo, const char *bar, double baz) {...} +// but instead you'll have to add +// DECLARE_REAL(int, foo, const char *bar, double baz) in your +// source file (to define a pointer to overriden function). +// 3. Some Mac functions have symbol variants discriminated by +// additional suffixes, e.g. _$UNIX2003 (see +// https://developer.apple.com/library/mac/#releasenotes/Darwin/SymbolVariantsRelNotes/index.html +// for more details). To intercept such functions you need to use the +// INTERCEPTOR_WITH_SUFFIX(...) macro. + +// How it works: +// To replace system functions on Linux we just need to declare functions +// with same names in our library and then obtain the real function pointers +// using dlsym(). +// There is one complication. A user may also intercept some of the functions +// we intercept. To resolve this we declare our interceptors with __interceptor_ +// prefix, and then make actual interceptors weak aliases to __interceptor_ +// functions. +// +// This is not so on Mac OS, where the two-level namespace makes +// our replacement functions invisible to other libraries. This may be overcomed +// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared +// libraries in Chromium were noticed when doing so. +// Instead we create a dylib containing a __DATA,__interpose section that +// associates library functions with their wrappers. When this dylib is +// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all +// the calls to interposed functions done through stubs to the wrapper +// functions. +// As it's decided at compile time which functions are to be intercepted on Mac, +// INTERCEPT_FUNCTION() is effectively a no-op on this system. + +#if defined(__APPLE__) +#include // For __DARWIN_ALIAS_C(). + +// Just a pair of pointers. +struct interpose_substitution { + const uptr replacement; + const uptr original; +}; + +// For a function foo() create a global pair of pointers { wrap_foo, foo } in +// the __DATA,__interpose section. +// As a result all the calls to foo() will be routed to wrap_foo() at runtime. +#define INTERPOSER(func_name) __attribute__((used)) \ +const interpose_substitution substitution_##func_name[] \ + __attribute__((section("__DATA, __interpose"))) = { \ + { reinterpret_cast(WRAP(func_name)), \ + reinterpret_cast(func_name) } \ +} + +// For a function foo() and a wrapper function bar() create a global pair +// of pointers { bar, foo } in the __DATA,__interpose section. +// As a result all the calls to foo() will be routed to bar() at runtime. +#define INTERPOSER_2(func_name, wrapper_name) __attribute__((used)) \ +const interpose_substitution substitution_##func_name[] \ + __attribute__((section("__DATA, __interpose"))) = { \ + { reinterpret_cast(wrapper_name), \ + reinterpret_cast(func_name) } \ +} + +# define WRAP(x) wrap_##x +# define WRAPPER_NAME(x) "wrap_"#x +# define INTERCEPTOR_ATTRIBUTE +# define DECLARE_WRAPPER(ret_type, func, ...) + +#elif defined(_WIN32) +# if defined(_DLL) // DLL CRT +# define WRAP(x) x +# define WRAPPER_NAME(x) #x +# define INTERCEPTOR_ATTRIBUTE +# else // Static CRT +# define WRAP(x) wrap_##x +# define WRAPPER_NAME(x) "wrap_"#x +# define INTERCEPTOR_ATTRIBUTE +# endif +# define DECLARE_WRAPPER(ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__); +# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \ + extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__); +#else +# define WRAP(x) __interceptor_ ## x +# define WRAPPER_NAME(x) "__interceptor_" #x +# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) +# define DECLARE_WRAPPER(ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__) \ + __attribute__((weak, alias("__interceptor_" #func), visibility("default"))); +#endif + +#if !defined(__APPLE__) +# define PTR_TO_REAL(x) real_##x +# define REAL(x) __interception::PTR_TO_REAL(x) +# define FUNC_TYPE(x) x##_f + +# define DECLARE_REAL(ret_type, func, ...) \ + typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ + namespace __interception { \ + extern FUNC_TYPE(func) PTR_TO_REAL(func); \ + } +#else // __APPLE__ +# define REAL(x) x +# define DECLARE_REAL(ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__); +#endif // __APPLE__ + +#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ + DECLARE_REAL(ret_type, func, __VA_ARGS__) \ + extern "C" ret_type WRAP(func)(__VA_ARGS__); + +// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR +// macros does its job. In exceptional cases you may need to call REAL(foo) +// without defining INTERCEPTOR(..., foo, ...). For example, if you override +// foo with an interceptor for other function. +#if !defined(__APPLE__) +# define DEFINE_REAL(ret_type, func, ...) \ + typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ + namespace __interception { \ + FUNC_TYPE(func) PTR_TO_REAL(func); \ + } +#else +# define DEFINE_REAL(ret_type, func, ...) +#endif + +#if !defined(__APPLE__) +#define INTERCEPTOR(ret_type, func, ...) \ + DEFINE_REAL(ret_type, func, __VA_ARGS__) \ + DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \ + extern "C" \ + INTERCEPTOR_ATTRIBUTE \ + ret_type WRAP(func)(__VA_ARGS__) + +// We don't need INTERCEPTOR_WITH_SUFFIX on non-Darwin for now. +#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \ + INTERCEPTOR(ret_type, func, __VA_ARGS__) + +#else // __APPLE__ + +#define INTERCEPTOR_ZZZ(suffix, ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__) suffix; \ + extern "C" ret_type WRAP(func)(__VA_ARGS__); \ + INTERPOSER(func); \ + extern "C" INTERCEPTOR_ATTRIBUTE ret_type WRAP(func)(__VA_ARGS__) + +#define INTERCEPTOR(ret_type, func, ...) \ + INTERCEPTOR_ZZZ(/*no symbol variants*/, ret_type, func, __VA_ARGS__) + +#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \ + INTERCEPTOR_ZZZ(__DARWIN_ALIAS_C(func), ret_type, func, __VA_ARGS__) + +// Override |overridee| with |overrider|. +#define OVERRIDE_FUNCTION(overridee, overrider) \ + INTERPOSER_2(overridee, WRAP(overrider)) +#endif + +#if defined(_WIN32) +# define INTERCEPTOR_WINAPI(ret_type, func, ...) \ + typedef ret_type (__stdcall *FUNC_TYPE(func))(__VA_ARGS__); \ + namespace __interception { \ + FUNC_TYPE(func) PTR_TO_REAL(func); \ + } \ + DECLARE_WRAPPER_WINAPI(ret_type, func, __VA_ARGS__) \ + extern "C" \ + INTERCEPTOR_ATTRIBUTE \ + ret_type __stdcall WRAP(func)(__VA_ARGS__) +#endif + +// ISO C++ forbids casting between pointer-to-function and pointer-to-object, +// so we use casting via an integral type __interception::uptr, +// assuming that system is POSIX-compliant. Using other hacks seem +// challenging, as we don't even pass function type to +// INTERCEPT_FUNCTION macro, only its name. +namespace __interception { +#if defined(_WIN64) +typedef unsigned long long uptr; // NOLINT +#else +typedef unsigned long uptr; // NOLINT +#endif // _WIN64 +} // namespace __interception + +#define INCLUDED_FROM_INTERCEPTION_LIB + +#if defined(__linux__) +# include "interception_linux.h" +# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_LINUX(func, symver) +#elif defined(__APPLE__) +# include "interception_mac.h" +# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_MAC(func, symver) +#else // defined(_WIN32) +# include "interception_win.h" +# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_WIN(func, symver) +#endif + +#undef INCLUDED_FROM_INTERCEPTION_LIB + +#endif // INTERCEPTION_H diff --git a/projects/compiler-rt/lib/interception/interception_linux.cc b/projects/compiler-rt/lib/interception/interception_linux.cc new file mode 100644 index 00000000..53f42881 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception_linux.cc @@ -0,0 +1,36 @@ +//===-- interception_linux.cc -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific interception methods. +//===----------------------------------------------------------------------===// + +#ifdef __linux__ +#include "interception.h" + +#include // for dlsym + +namespace __interception { +bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, + uptr real, uptr wrapper) { + *func_addr = (uptr)dlsym(RTLD_NEXT, func_name); + return real == wrapper; +} + +#if !defined(__ANDROID__) // android does not have dlvsym +void *GetFuncAddrVer(const char *func_name, const char *ver) { + return dlvsym(RTLD_NEXT, func_name, ver); +} +#endif // !defined(__ANDROID__) + +} // namespace __interception + + +#endif // __linux__ diff --git a/projects/compiler-rt/lib/interception/interception_linux.h b/projects/compiler-rt/lib/interception/interception_linux.h new file mode 100644 index 00000000..cea95732 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception_linux.h @@ -0,0 +1,47 @@ +//===-- interception_linux.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific interception methods. +//===----------------------------------------------------------------------===// + +#ifdef __linux__ + +#if !defined(INCLUDED_FROM_INTERCEPTION_LIB) +# error "interception_linux.h should be included from interception library only" +#endif + +#ifndef INTERCEPTION_LINUX_H +#define INTERCEPTION_LINUX_H + +namespace __interception { +// returns true if a function with the given name was found. +bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, + uptr real, uptr wrapper); +void *GetFuncAddrVer(const char *func_name, const char *ver); +} // namespace __interception + +#define INTERCEPT_FUNCTION_LINUX(func) \ + ::__interception::GetRealFunctionAddress( \ + #func, (::__interception::uptr*)&REAL(func), \ + (::__interception::uptr)&(func), \ + (::__interception::uptr)&WRAP(func)) + +#if !defined(__ANDROID__) // android does not have dlvsym +# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \ + ::__interception::real_##func = (func##_f)(unsigned long) \ + ::__interception::GetFuncAddrVer(#func, symver) +#else +# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \ + INTERCEPT_FUNCTION_LINUX(func) +#endif // !defined(__ANDROID__) + +#endif // INTERCEPTION_LINUX_H +#endif // __linux__ diff --git a/projects/compiler-rt/lib/interception/interception_mac.cc b/projects/compiler-rt/lib/interception/interception_mac.cc new file mode 100644 index 00000000..b035cf99 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception_mac.cc @@ -0,0 +1,20 @@ +//===-- interception_mac.cc -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific interception methods. +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ + +#include "interception.h" + + +#endif // __APPLE__ diff --git a/projects/compiler-rt/lib/interception/interception_mac.h b/projects/compiler-rt/lib/interception/interception_mac.h new file mode 100644 index 00000000..e5a35c69 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception_mac.h @@ -0,0 +1,28 @@ +//===-- interception_mac.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific interception methods. +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ + +#if !defined(INCLUDED_FROM_INTERCEPTION_LIB) +# error "interception_mac.h should be included from interception.h only" +#endif + +#ifndef INTERCEPTION_MAC_H +#define INTERCEPTION_MAC_H + +#define INTERCEPT_FUNCTION_MAC(func) +#define INTERCEPT_FUNCTION_VER_MAC(func, symver) + +#endif // INTERCEPTION_MAC_H +#endif // __APPLE__ diff --git a/projects/compiler-rt/lib/interception/interception_type_test.cc b/projects/compiler-rt/lib/interception/interception_type_test.cc new file mode 100644 index 00000000..7b79b783 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception_type_test.cc @@ -0,0 +1,39 @@ +//===-- interception_type_test.cc -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Compile-time tests of the internal type definitions. +//===----------------------------------------------------------------------===// + +#if defined(__linux__) || defined(__APPLE__) + +#include "interception.h" +#include +#include +#include + +COMPILER_CHECK(sizeof(SIZE_T) == sizeof(size_t)); +COMPILER_CHECK(sizeof(SSIZE_T) == sizeof(ssize_t)); +COMPILER_CHECK(sizeof(PTRDIFF_T) == sizeof(ptrdiff_t)); +COMPILER_CHECK(sizeof(INTMAX_T) == sizeof(intmax_t)); + +#ifndef __APPLE__ +COMPILER_CHECK(sizeof(OFF64_T) == sizeof(off64_t)); +#endif + +// The following are the cases when pread (and friends) is used instead of +// pread64. In those cases we need OFF_T to match off_t. We don't care about the +// rest (they depend on _FILE_OFFSET_BITS setting when building an application). +# if defined(__ANDROID__) || !defined _FILE_OFFSET_BITS || \ + _FILE_OFFSET_BITS != 64 +COMPILER_CHECK(sizeof(OFF_T) == sizeof(off_t)); +# endif + +#endif diff --git a/projects/compiler-rt/lib/interception/interception_win.cc b/projects/compiler-rt/lib/interception/interception_win.cc new file mode 100644 index 00000000..abbab249 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception_win.cc @@ -0,0 +1,151 @@ +//===-- interception_linux.cc -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific interception methods. +//===----------------------------------------------------------------------===// + +#ifdef _WIN32 + +#include "interception.h" +#include + +namespace __interception { + +bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) { + const char *DLLS[] = { + "msvcr80.dll", + "msvcr90.dll", + "kernel32.dll", + NULL + }; + *func_addr = 0; + for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) { + *func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name); + } + return (*func_addr != 0); +} + +// FIXME: internal_str* and internal_mem* functions should be moved from the +// ASan sources into interception/. + +static void _memset(void *p, int value, size_t sz) { + for (size_t i = 0; i < sz; ++i) + ((char*)p)[i] = (char)value; +} + +static void _memcpy(void *dst, void *src, size_t sz) { + char *dst_c = (char*)dst, + *src_c = (char*)src; + for (size_t i = 0; i < sz; ++i) + dst_c[i] = src_c[i]; +} + +static void WriteJumpInstruction(char *jmp_from, char *to) { + // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset fromt jmp_from + // to the next instruction to the destination. + ptrdiff_t offset = to - jmp_from - 5; + *jmp_from = '\xE9'; + *(ptrdiff_t*)(jmp_from + 1) = offset; +} + +bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) { +#ifdef _WIN64 +# error OverrideFunction was not tested on x64 +#endif + // Basic idea: + // We write 5 bytes (jmp-to-new_func) at the beginning of the 'old_func' + // to override it. We want to be able to execute the original 'old_func' from + // the wrapper, so we need to keep the leading 5+ bytes ('head') of the + // original instructions somewhere with a "jmp old_func+head". + // We call these 'head'+5 bytes of instructions a "trampoline". + + // Trampolines are allocated from a common pool. + const int POOL_SIZE = 1024; + static char *pool = NULL; + static size_t pool_used = 0; + if (pool == NULL) { + pool = (char*)VirtualAlloc(NULL, POOL_SIZE, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + // FIXME: set PAGE_EXECUTE_READ access after setting all interceptors? + if (pool == NULL) + return false; + _memset(pool, 0xCC /* int 3 */, POOL_SIZE); + } + + char* old_bytes = (char*)old_func; + char* trampoline = pool + pool_used; + + // Find out the number of bytes of the instructions we need to copy to the + // island and store it in 'head'. + size_t head = 0; + while (head < 5) { + switch (old_bytes[head]) { + case '\x55': // push ebp + case '\x56': // push esi + case '\x57': // push edi + head++; + continue; + } + switch (*(unsigned short*)(old_bytes + head)) { // NOLINT + case 0xFF8B: // 8B FF = mov edi, edi + case 0xEC8B: // 8B EC = mov ebp, esp + case 0xC033: // 33 C0 = xor eax, eax + head += 2; + continue; + case 0xEC83: // 83 EC XX = sub esp, XX + head += 3; + continue; + case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX + head += 6; + continue; + } + switch (0x00FFFFFF & *(unsigned int*)(old_bytes + head)) { + case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh] + case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh] + case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh] + case 0x247C8B: // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh] + head += 4; + continue; + } + + // Unknown instruction! + return false; + } + + if (pool_used + head + 5 > POOL_SIZE) + return false; + + // Now put the "jump to trampoline" instruction into the original code. + DWORD old_prot, unused_prot; + if (!VirtualProtect((void*)old_func, head, PAGE_EXECUTE_READWRITE, + &old_prot)) + return false; + + // Put the needed instructions into the trampoline bytes. + _memcpy(trampoline, old_bytes, head); + WriteJumpInstruction(trampoline + head, old_bytes + head); + *orig_old_func = (uptr)trampoline; + pool_used += head + 5; + + // Intercept the 'old_func'. + WriteJumpInstruction(old_bytes, (char*)new_func); + _memset(old_bytes + 5, 0xCC /* int 3 */, head - 5); + + if (!VirtualProtect((void*)old_func, head, old_prot, &unused_prot)) + return false; // not clear if this failure bothers us. + + return true; +} + +} // namespace __interception + +#endif // _WIN32 diff --git a/projects/compiler-rt/lib/interception/interception_win.h b/projects/compiler-rt/lib/interception/interception_win.h new file mode 100644 index 00000000..f2727c92 --- /dev/null +++ b/projects/compiler-rt/lib/interception/interception_win.h @@ -0,0 +1,48 @@ +//===-- interception_linux.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific interception methods. +//===----------------------------------------------------------------------===// + +#ifdef _WIN32 + +#if !defined(INCLUDED_FROM_INTERCEPTION_LIB) +# error "interception_win.h should be included from interception library only" +#endif + +#ifndef INTERCEPTION_WIN_H +#define INTERCEPTION_WIN_H + +namespace __interception { +// returns true if a function with the given name was found. +bool GetRealFunctionAddress(const char *func_name, uptr *func_addr); + +// returns true if the old function existed, false on failure. +bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func); +} // namespace __interception + +#if defined(_DLL) +# define INTERCEPT_FUNCTION_WIN(func) \ + ::__interception::GetRealFunctionAddress( \ + #func, (::__interception::uptr*)&REAL(func)) +#else +# define INTERCEPT_FUNCTION_WIN(func) \ + ::__interception::OverrideFunction( \ + (::__interception::uptr)func, \ + (::__interception::uptr)WRAP(func), \ + (::__interception::uptr*)&REAL(func)) +#endif + +#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \ + INTERCEPT_FUNCTION_WIN(func) + +#endif // INTERCEPTION_WIN_H +#endif // _WIN32 diff --git a/projects/compiler-rt/lib/lit.common.cfg b/projects/compiler-rt/lib/lit.common.cfg new file mode 100644 index 00000000..6c2b4cca --- /dev/null +++ b/projects/compiler-rt/lib/lit.common.cfg @@ -0,0 +1,62 @@ +# -*- Python -*- + +# Configuration file for 'lit' test runner. +# This file contains common rules for various compiler-rt testsuites. +# It is mostly copied from lit.cfg used by Clang. +import os +import platform + +import lit.formats + +# Setup test format +execute_external = (platform.system() != 'Windows' + or lit_config.getBashPath() not in [None, ""]) +config.test_format = lit.formats.ShTest(execute_external) + +# Setup clang binary. +clang_path = getattr(config, 'clang', None) +if (not clang_path) or (not os.path.exists(clang_path)): + lit_config.fatal("Can't find Clang on path %r" % clang_path) + +# Clear some environment variables that might affect Clang. +possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS', + 'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH', + 'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH', + 'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH', + 'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING', + 'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX', + 'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS', + 'LIBCLANG_RESOURCE_USAGE', + 'LIBCLANG_CODE_COMPLETION_LOGGING'] +# Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it. +if platform.system() != 'Windows': + possibly_dangerous_env_vars.append('INCLUDE') +for name in possibly_dangerous_env_vars: + if name in config.environment: + del config.environment[name] + +# Tweak PATH to include llvm tools dir. +llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) +if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): + lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) +path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH'])) +config.environment['PATH'] = path + +# Define path to external llvm-symbolizer tool. +config.llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") + +# Use ugly construction to explicitly prohibit "clang", "clang++" etc. +# in RUN lines. +config.substitutions.append( + (' clang', """\n\n*** Do not use 'clangXXX' in tests, + instead define '%clangXXX' substitution in lit config. ***\n\n""") ) + +# Add supported compiler_rt architectures to a list of available features. +compiler_rt_arch = getattr(config, 'compiler_rt_arch', None) +if compiler_rt_arch: + for arch in compiler_rt_arch.split(";"): + config.available_features.add(arch + "-supported-target") + +compiler_rt_debug = getattr(config, 'compiler_rt_debug', False) +if not compiler_rt_debug: + config.available_features.add('compiler-rt-optimized') diff --git a/projects/compiler-rt/lib/lit.common.configured.in b/projects/compiler-rt/lib/lit.common.configured.in new file mode 100644 index 00000000..558655cb --- /dev/null +++ b/projects/compiler-rt/lib/lit.common.configured.in @@ -0,0 +1,27 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Generic config options for all compiler-rt lit tests. +config.target_triple = "@TARGET_TRIPLE@" +config.host_arch = "@HOST_ARCH@" +config.host_os = "@HOST_OS@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.clang = "@LLVM_BINARY_DIR@/bin/clang" +config.compiler_rt_arch = "@COMPILER_RT_SUPPORTED_ARCH@" +config.python_executable = "@PYTHON_EXECUTABLE@" +config.compiler_rt_debug = @COMPILER_RT_DEBUG_PYBOOL@ + +# LLVM tools dir can be passed in lit parameters, so try to +# apply substitution. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params +except KeyError,e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) + +# Setup attributes common for all compiler-rt projects. +lit_config.load_config(config, "@COMPILER_RT_SOURCE_DIR@/lib/lit.common.cfg") diff --git a/projects/compiler-rt/lib/lit.common.unit.cfg b/projects/compiler-rt/lib/lit.common.unit.cfg new file mode 100644 index 00000000..2bd8f376 --- /dev/null +++ b/projects/compiler-rt/lib/lit.common.unit.cfg @@ -0,0 +1,30 @@ +# -*- Python -*- + +# Configuration file for 'lit' test runner. +# This file contains common config setup rules for unit tests in various +# compiler-rt testsuites. + +import os + +import lit.formats + +# Setup test format +llvm_build_mode = getattr(config, "llvm_build_mode", "Debug") +config.test_format = lit.formats.GoogleTest(llvm_build_mode, "Test") + +# Setup test suffixes. +config.suffixes = [] + +# Tweak PATH to include llvm tools dir. +llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) +if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)): + lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir) +path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH'])) +config.environment['PATH'] = path + +# Propagate the temp directory. Windows requires this because it uses \Windows\ +# if none of these are present. +if 'TMP' in os.environ: + config.environment['TMP'] = os.environ['TMP'] +if 'TEMP' in os.environ: + config.environment['TEMP'] = os.environ['TEMP'] diff --git a/projects/compiler-rt/lib/lit.common.unit.configured.in b/projects/compiler-rt/lib/lit.common.unit.configured.in new file mode 100644 index 00000000..430816b2 --- /dev/null +++ b/projects/compiler-rt/lib/lit.common.unit.configured.in @@ -0,0 +1,23 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Generic config options for all compiler-rt unit tests. +config.target_triple = "@TARGET_TRIPLE@" +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" +config.host_os = "@HOST_OS@" + +# LLVM tools dir and build mode can be passed in lit parameters, +# so try to apply substitution. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params + config.llvm_build_mode = config.llvm_build_mode % lit_config.params +except KeyError,e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) + +# Setup attributes common for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_SOURCE_DIR@/lib/lit.common.unit.cfg") diff --git a/projects/compiler-rt/lib/lsan/CMakeLists.txt b/projects/compiler-rt/lib/lsan/CMakeLists.txt new file mode 100644 index 00000000..3018a066 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/CMakeLists.txt @@ -0,0 +1,60 @@ +include_directories(..) + +set(LSAN_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti) + +set(LSAN_COMMON_SOURCES + lsan_common.cc + lsan_common_linux.cc) + +set(LSAN_SOURCES + lsan.cc + lsan_allocator.cc + lsan_interceptors.cc + lsan_preinit.cc + lsan_thread.cc) + +set(LSAN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +# The common files need to build on every arch supported by ASan. +# (Even if they build into dummy object files.) +filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH + x86_64 i386 powerpc64) + +# Architectures supported by the standalone LSan. +filter_available_targets(LSAN_SUPPORTED_ARCH + x86_64) + +set(LSAN_RUNTIME_LIBRARIES) + +if(APPLE) + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTLSanCommon ${os} + ARCH ${LSAN_COMMON_SUPPORTED_ARCH} + SOURCES ${LSAN_COMMON_SOURCES} + CFLAGS ${LSAN_CFLAGS}) + endforeach() +elseif(NOT ANDROID) + foreach(arch ${LSAN_COMMON_SUPPORTED_ARCH}) + add_compiler_rt_object_library(RTLSanCommon ${arch} + SOURCES ${LSAN_COMMON_SOURCES} + CFLAGS ${LSAN_CFLAGS}) + endforeach() + + foreach(arch ${LSAN_SUPPORTED_ARCH}) + add_compiler_rt_static_runtime(clang_rt.lsan-${arch} ${arch} + SOURCES ${LSAN_SOURCES} + $ + $ + $ + $ + CFLAGS ${LSAN_CFLAGS}) + list(APPEND LSAN_RUNTIME_LIBRARIES clang_rt.lsan-${arch}) + endforeach() +endif() + +if (LLVM_INCLUDE_TESTS) + add_subdirectory(tests) +endif() +add_subdirectory(lit_tests) diff --git a/projects/compiler-rt/lib/lsan/Makefile.mk b/projects/compiler-rt/lib/lsan/Makefile.mk new file mode 100644 index 00000000..2a6b41c9 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/Makefile.mk @@ -0,0 +1,28 @@ +#===- lib/lsan/Makefile.mk ---------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := lsan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../interception/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the lsan functions. +LsanFunctions := $(Sources:%.cc=%) + +# lsan functions used in another sanitizers. +LsanCommonSources := $(foreach file,$(wildcard $(Dir)/lsan_common*.cc),$(notdir $(file))) +LsanCommonFunctions := $(LsanCommonSources:%.cc=%) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.cfg b/projects/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.cfg new file mode 100644 index 00000000..ae919817 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.cfg @@ -0,0 +1,32 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root") +lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg") +if not os.path.exists(lsan_lit_cfg): + lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg) +lit_config.load_config(config, lsan_lit_cfg) + +config.name = 'LeakSanitizer-AddressSanitizer' + +clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=address " + +config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + + clang_lsan_cxxflags + " ")) ) + +clang_lsan_cflags = config.clang_cflags + " -fsanitize=address " + +config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " + + clang_lsan_cflags + " ")) ) + +config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' diff --git a/projects/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in b/projects/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in new file mode 100644 index 00000000..9cf6572c --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/AsanConfig/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/AsanConfig/lit.cfg") diff --git a/projects/compiler-rt/lib/lsan/lit_tests/CMakeLists.txt b/projects/compiler-rt/lib/lsan/lit_tests/CMakeLists.txt new file mode 100644 index 00000000..526d71bf --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/CMakeLists.txt @@ -0,0 +1,37 @@ +set(LSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) +set(LSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) + +set(LSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/LsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/AsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) + +if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT APPLE AND NOT ANDROID) + set(LSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + ${LSAN_RUNTIME_LIBRARIES}) + foreach(arch ${LSAN_SUPPORTED_ARCH}) + list(APPEND LSAN_TEST_DEPS clang_rt.asan-${arch}) + endforeach() + if(LLVM_INCLUDE_TESTS) + list(APPEND LSAN_TEST_DEPS LsanUnitTests) + endif() + add_lit_testsuite(check-lsan "Running the LeakSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/Unit + DEPENDS ${LSAN_TEST_DEPS}) + set_target_properties(check-lsan PROPERTIES FOLDER "LSan tests") +endif() diff --git a/projects/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.cfg b/projects/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.cfg new file mode 100644 index 00000000..84faf916 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.cfg @@ -0,0 +1,30 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +lsan_lit_src_root = get_required_attr(config, "lsan_lit_src_root") +lsan_lit_cfg = os.path.join(lsan_lit_src_root, "lit.common.cfg") +if not os.path.exists(lsan_lit_cfg): + lit_config.fatal("Can't find common LSan lit config at: %r" % lsan_lit_cfg) +lit_config.load_config(config, lsan_lit_cfg) + +config.name = 'LeakSanitizer-Standalone' + +clang_lsan_cxxflags = config.clang_cxxflags + " -fsanitize=leak " + +config.substitutions.append( ("%clangxx_lsan ", (" " + config.clang + " " + + clang_lsan_cxxflags + " ")) ) + +clang_lsan_cflags = config.clang_cflags + " -fsanitize=leak " + +config.substitutions.append( ("%clang_lsan ", (" " + config.clang + " " + + clang_lsan_cflags + " ")) ) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in b/projects/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in new file mode 100644 index 00000000..2a6d724c --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/LsanConfig/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.lsan_lit_src_root = "@LSAN_LIT_SOURCE_DIR@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@LSAN_LIT_SOURCE_DIR@/LsanConfig/lit.cfg") diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc new file mode 100644 index 00000000..475a66ec --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/SharedLibs/huge_tls_lib_so.cc @@ -0,0 +1,12 @@ +// A loadable module with a large thread local section, which would require +// allocation of a new TLS storage chunk when loaded with dlopen(). We use it +// to test the reachability of such chunks in LSan tests. + +// This must be large enough that it doesn't fit into preallocated static TLS +// space (see STATIC_TLS_SURPLUS in glibc). +__thread void *huge_thread_local_array[(1 << 20) / sizeof(void *)]; // NOLINT + +extern "C" void **StoreToTLS(void *p) { + huge_thread_local_array[0] = p; + return &huge_thread_local_array[0]; +} diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg new file mode 100644 index 00000000..b3677c17 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc new file mode 100644 index 00000000..ab368245 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc @@ -0,0 +1,45 @@ +// Regression test for thread lifetime tracking. Thread data should be +// considered live during the thread's termination, at least until the +// user-installed TSD destructors have finished running (since they may contain +// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it +// makes its best effort. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %t 2>&1 | FileCheck %s + +#include +#include +#include +#include + +#include "sanitizer/lsan_interface.h" + +pthread_key_t key; +__thread void *p; + +void key_destructor(void *arg) { + // Generally this may happen on a different thread. + __lsan_do_leak_check(); +} + +void *thread_func(void *arg) { + p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + int res = pthread_setspecific(key, (void*)1); + assert(res == 0); + return 0; +} + +int main() { + int res = pthread_key_create(&key, &key_destructor); + assert(res == 0); + pthread_t thread_id; + res = pthread_create(&thread_id, 0, thread_func, 0); + assert(res == 0); + res = pthread_join(thread_id, 0); + assert(res == 0); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: leaked 1337 byte object at [[ADDR]] diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/disabler.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/disabler.cc new file mode 100644 index 00000000..db0cd8fa --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/disabler.cc @@ -0,0 +1,23 @@ +// Test for ScopedDisabler. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include +#include + +#include "sanitizer/lsan_interface.h" + +int main() { + void **p; + { + __lsan::ScopedDisabler d; + p = new void *; + } + *reinterpret_cast(p) = malloc(666); + void *q = malloc(1337); + // Break optimization. + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc new file mode 100644 index 00000000..94e4fc39 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/disabler_in_tsd_destructor.cc @@ -0,0 +1,38 @@ +// Regression test. Disabler should not depend on TSD validity. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %t + +#include +#include +#include +#include + +#include "sanitizer/lsan_interface.h" + +pthread_key_t key; + +void key_destructor(void *arg) { + __lsan::ScopedDisabler d; + void *p = malloc(1337); + // Break optimization. + fprintf(stderr, "Test alloc: %p.\n", p); + pthread_setspecific(key, 0); +} + +void *thread_func(void *arg) { + int res = pthread_setspecific(key, (void*)1); + assert(res == 0); + return 0; +} + +int main() { + int res = pthread_key_create(&key, &key_destructor); + assert(res == 0); + pthread_t thread_id; + res = pthread_create(&thread_id, 0, thread_func, 0); + assert(res == 0); + res = pthread_join(thread_id, 0); + assert(res == 0); + return 0; +} diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc new file mode 100644 index 00000000..be0ed0a6 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/do_leak_check_override.cc @@ -0,0 +1,36 @@ +// Test for __lsan_do_leak_check(). We test it by making the leak check run +// before global destructors, which also tests compatibility with HeapChecker's +// "normal" mode (LSan runs in "strict" mode by default). +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck --check-prefix=CHECK-strict %s +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck --check-prefix=CHECK-normal %s + +#include +#include +#include + +struct LeakyGlobal { + LeakyGlobal() { + p = malloc(1337); + } + ~LeakyGlobal() { + p = 0; + } + void *p; +}; + +LeakyGlobal leaky_global; + +int main(int argc, char *argv[]) { + // Register leak check to run before global destructors. + if (argc > 1) + atexit(&__lsan_do_leak_check); + void *p = malloc(666); + printf("Test alloc: %p\n", p); + printf("Test alloc in leaky global: %p\n", leaky_global.p); + return 0; +} + +// CHECK-strict: SUMMARY: {{(Leak|Address)}}Sanitizer: 2003 byte(s) leaked in 2 allocation(s) +// CHECK-normal: SUMMARY: {{(Leak|Address)}}Sanitizer: 666 byte(s) leaked in 1 allocation(s) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/fork.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/fork.cc new file mode 100644 index 00000000..69258d9a --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/fork.cc @@ -0,0 +1,24 @@ +// Test that thread local data is handled correctly after forking without exec(). +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 + +#include +#include +#include +#include +#include + +__thread void *thread_local_var; + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } + return 0; +} diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/fork_threaded.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/fork_threaded.cc new file mode 100644 index 00000000..24a58610 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/fork_threaded.cc @@ -0,0 +1,43 @@ +// Test that thread local data is handled correctly after forking without +// exec(). In this test leak checking is initiated from a non-main thread. +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 + +#include +#include +#include +#include +#include +#include + +__thread void *thread_local_var; + +void *exit_thread_func(void *arg) { + exit(0); +} + +void ExitFromThread() { + pthread_t tid; + int res; + res = pthread_create(&tid, 0, exit_thread_func, 0); + assert(res == 0); + pthread_join(tid, 0); +} + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } else { + // Spawn a thread and call exit() from there, to check that we track main + // thread's pid correctly even if leak checking is initiated from another + // thread. + ExitFromThread(); + } + return 0; +} diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc new file mode 100644 index 00000000..1cecb2a5 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/high_allocator_contention.cc @@ -0,0 +1,48 @@ +// A benchmark that executes malloc/free pairs in parallel. +// Usage: ./a.out number_of_threads total_number_of_allocations +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 5 1000000 2>&1 +#include +#include +#include +#include + +int num_threads; +int total_num_alloc; +const int kMaxNumThreads = 5000; +pthread_t tid[kMaxNumThreads]; + +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +bool go = false; + +void *thread_fun(void *arg) { + pthread_mutex_lock(&mutex); + while (!go) pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < total_num_alloc / num_threads; i++) { + void *p = malloc(10); + __asm__ __volatile__("" : : "r"(p) : "memory"); + free((void *)p); + } + return 0; +} + +int main(int argc, char** argv) { + assert(argc == 3); + num_threads = atoi(argv[1]); + assert(num_threads > 0); + assert(num_threads <= kMaxNumThreads); + total_num_alloc = atoi(argv[2]); + assert(total_num_alloc > 0); + printf("%d threads, %d allocations in each\n", num_threads, + total_num_alloc / num_threads); + for (int i = 0; i < num_threads; i++) + pthread_create(&tid[i], 0, thread_fun, 0); + pthread_mutex_lock(&mutex); + go = true; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < num_threads; i++) pthread_join(tid[i], 0); + return 0; +} diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/ignore_object.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/ignore_object.cc new file mode 100644 index 00000000..cbc743b7 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/ignore_object.cc @@ -0,0 +1,30 @@ +// Test for __lsan_ignore_object(). +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0:verbosity=3" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include +#include + +#include "sanitizer/lsan_interface.h" + +int main() { + { + // The first malloc call can cause an allocation in libdl. Ignore it here so + // it doesn't show up in our output. + __lsan::ScopedDisabler d; + malloc(1); + } + // Explicitly ignored object. + void **p = new void *; + // Transitively ignored object. + *p = malloc(666); + // Non-ignored object. + volatile void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + __lsan_ignore_object(p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: ignoring heap object at [[ADDR]] +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc new file mode 100644 index 00000000..2a6c7255 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/ignore_object_errors.cc @@ -0,0 +1,22 @@ +// Test for incorrect use of __lsan_ignore_object(). +// RUN: LSAN_BASE="verbosity=2" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %t 2>&1 | FileCheck %s + +#include +#include + +#include "sanitizer/lsan_interface.h" + +int main() { + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + __lsan_ignore_object(p); + __lsan_ignore_object(p); + free(p); + __lsan_ignore_object(p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: heap object at [[ADDR]] is already being ignored +// CHECK: no heap object found at [[ADDR]] diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc new file mode 100644 index 00000000..57d05659 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc @@ -0,0 +1,18 @@ +// Test that LargeMmapAllocator's chunks aren't reachable via some internal data structure. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include +#include + +int main() { + // maxsize in primary allocator is always less than this (1 << 25). + void *large_alloc = malloc(33554432); + fprintf(stderr, "Test alloc: %p.\n", large_alloc); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 33554432 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc new file mode 100644 index 00000000..38c1063b --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/leak_check_at_exit.cc @@ -0,0 +1,19 @@ +// Test for the leak_check_at_exit flag. +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS="verbosity=1" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS="verbosity=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-do +// RUN: LSAN_OPTIONS="verbosity=1:leak_check_at_exit=0" ASAN_OPTIONS="$ASAN_OPTIONS:leak_check_at_exit=0" %t 2>&1 | FileCheck %s --check-prefix=CHECK-dont + +#include +#include + +int main(int argc, char *argv[]) { + printf("printf to break optimization\n"); + if (argc > 1) + __lsan_do_leak_check(); + return 0; +} + +// CHECK-do: SUMMARY: {{(Leak|Address)}}Sanitizer: +// CHECK-dont-NOT: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/link_turned_off.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/link_turned_off.cc new file mode 100644 index 00000000..93628a1d --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/link_turned_off.cc @@ -0,0 +1,24 @@ +// Test for disabling LSan at link-time. +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t foo 2>&1 | FileCheck %s + +#include + +int argc_copy; + +extern "C" { +int __lsan_is_turned_off() { + return (argc_copy == 1); +} +} + +int main(int argc, char *argv[]) { + volatile int *x = new int; + *x = 42; + argc_copy = argc; + return 0; +} + +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 4 byte(s) leaked in 1 allocation(s) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/pointer_to_self.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/pointer_to_self.cc new file mode 100644 index 00000000..0d2818d2 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/pointer_to_self.cc @@ -0,0 +1,18 @@ +// Regression test: pointers to self should not confuse LSan into thinking the +// object is indirectly leaked. Only external pointers count. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s + +#include +#include + +int main() { + void *p = malloc(1337); + *reinterpret_cast(p) = p; + fprintf(stderr, "Test alloc: %p.\n", p); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c new file mode 100644 index 00000000..085412b4 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/sanity_check_pure_c.c @@ -0,0 +1,10 @@ +// Check that we can build C code. +// RUN: %clang_lsan %s -o %t +#ifdef __cplusplus +#error "This test must be built in C mode" +#endif + +int main() { + // FIXME: ideally this should somehow check that we don't have libstdc++ + return 0; +} diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc new file mode 100644 index 00000000..fabfb4ff --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc @@ -0,0 +1,42 @@ +// Test that out-of-scope local variables are ignored by LSan. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=1" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s + +#include +#include + +void **pp; + +// Put pointer far enough on the stack that LSan has space to run in without +// overwriting it. +// Hopefully the argument p will be passed on a register, saving us from false +// negatives. +__attribute__((noinline)) +void *PutPointerOnStaleStack(void *p) { + void *locals[2048]; + locals[0] = p; + pp = &locals[0]; + fprintf(stderr, "Test alloc: %p.\n", locals[0]); + return 0; +} + +int main() { + PutPointerOnStaleStack(malloc(1337)); + return 0; +} + +// This must run after LSan, to ensure LSan didn't overwrite the pointer before +// it had a chance to see it. If LSan is invoked with atexit(), this works. +// Otherwise, we need a different method. +__attribute__((destructor)) +void ConfirmPointerHasSurvived() { + fprintf(stderr, "Value after LSan: %p.\n", *pp); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK-sanity: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: +// CHECK-sanity: Value after LSan: [[ADDR]]. diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_default.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_default.cc new file mode 100644 index 00000000..9a165f87 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_default.cc @@ -0,0 +1,29 @@ +// Test for ScopedDisabler. +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include +#include + +#include "sanitizer/lsan_interface.h" + +extern "C" +const char *__lsan_default_suppressions() { + return "leak:*LSanTestLeakingFunc*"; +} + +void LSanTestLeakingFunc() { + void *p = malloc(666); + fprintf(stderr, "Test alloc: %p.\n", p); +} + +int main() { + LSanTestLeakingFunc(); + void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: Suppressions used: +// CHECK: 1 666 *LSanTestLeakingFunc* +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc new file mode 100644 index 00000000..9a165f87 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc @@ -0,0 +1,29 @@ +// Test for ScopedDisabler. +// RUN: LSAN_BASE="use_registers=0:use_stacks=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s + +#include +#include + +#include "sanitizer/lsan_interface.h" + +extern "C" +const char *__lsan_default_suppressions() { + return "leak:*LSanTestLeakingFunc*"; +} + +void LSanTestLeakingFunc() { + void *p = malloc(666); + fprintf(stderr, "Test alloc: %p.\n", p); +} + +int main() { + LSanTestLeakingFunc(); + void *q = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", q); + return 0; +} +// CHECK: Suppressions used: +// CHECK: 1 666 *LSanTestLeakingFunc* +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp new file mode 100644 index 00000000..8d8e560c --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/suppressions_file.cc.supp @@ -0,0 +1 @@ +leak:*LSanTestLeakingFunc* diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/swapcontext.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/swapcontext.cc new file mode 100644 index 00000000..a06685ca --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/swapcontext.cc @@ -0,0 +1,42 @@ +// We can't unwind stack if we're running coroutines on heap-allocated +// memory. Make sure we don't report these leaks. + +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 +// RUN: not %t foo 2>&1 | FileCheck %s + +#include +#include +#include + +const int kStackSize = 1 << 20; + +void Child() { + int child_stack; + printf("Child: %p\n", &child_stack); + int *leaked = new int[666]; +} + +int main(int argc, char *argv[]) { + char stack_memory[kStackSize + 1]; + char *heap_memory = new char[kStackSize + 1]; + char *child_stack = (argc > 1) ? stack_memory : heap_memory; + + printf("Child stack: %p\n", child_stack); + ucontext_t orig_context; + ucontext_t child_context; + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + child_context.uc_link = &orig_context; + makecontext(&child_context, Child, 0); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 1; + } + + delete[] heap_memory; + return 0; +} + +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 2664 byte(s) leaked in 1 allocation(s) diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_after_return.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_after_return.cc new file mode 100644 index 00000000..93b0ea60 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_after_return.cc @@ -0,0 +1,23 @@ +// Test that fake stack (introduced by ASan's use-after-return mode) is included +// in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -O2 -o %t +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %t 2>&1 + +#include +#include + +int main() { + void *stack_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", stack_var); + // Take pointer to variable, to ensure it's not optimized into a register. + fprintf(stderr, "Stack var at: %p.\n", &stack_var); + // Do not return from main to prevent the pointer from going out of scope. + exit(0); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc new file mode 100644 index 00000000..5a7c48bd --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc @@ -0,0 +1,21 @@ +// Test that initialized globals are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include + +void *data_var = (void *)1; + +int main() { + data_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", data_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc new file mode 100644 index 00000000..e1d045e3 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc @@ -0,0 +1,21 @@ +// Test that uninitialized globals are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_globals=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include + +void *bss_var; + +int main() { + bss_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", bss_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_registers.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_registers.cc new file mode 100644 index 00000000..a7d8a69d --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_registers.cc @@ -0,0 +1,51 @@ +// Test that registers of running threads are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0" +// RUN: %clangxx_lsan -pthread %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_registers=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include +#include +#include + +extern "C" +void *registers_thread_func(void *arg) { + int *sync = reinterpret_cast(arg); + void *p = malloc(1337); + // To store the pointer, choose a register which is unlikely to be reused by + // a function call. +#if defined(__i386__) + asm ( "mov %0, %%esi" + : + : "r" (p) + ); +#elif defined(__x86_64__) + asm ( "mov %0, %%r15" + : + : "r" (p) + ); +#else +#error "Test is not supported on this architecture." +#endif + fprintf(stderr, "Test alloc: %p.\n", p); + fflush(stderr); + __sync_fetch_and_xor(sync, 1); + while (true) + pthread_yield(); +} + +int main() { + int sync = 0; + pthread_t thread_id; + int res = pthread_create(&thread_id, 0, registers_thread_func, &sync); + assert(res == 0); + while (!__sync_fetch_and_xor(&sync, 0)) + pthread_yield(); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks.cc new file mode 100644 index 00000000..4287a96b --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks.cc @@ -0,0 +1,20 @@ +// Test that stack of main thread is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include + +int main() { + void *stack_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", stack_var); + // Do not return from main to prevent the pointer from going out of scope. + exit(0); +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc new file mode 100644 index 00000000..c7dfaf8a --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc @@ -0,0 +1,36 @@ +// Test that stacks of non-main threads are included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_registers=0" +// RUN: %clangxx_lsan -pthread %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include +#include +#include + +extern "C" +void *stacks_thread_func(void *arg) { + int *sync = reinterpret_cast(arg); + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + fflush(stderr); + __sync_fetch_and_xor(sync, 1); + while (true) + pthread_yield(); +} + +int main() { + int sync = 0; + pthread_t thread_id; + int res = pthread_create(&thread_id, 0, stacks_thread_func, &sync); + assert(res == 0); + while (!__sync_fetch_and_xor(&sync, 0)) + pthread_yield(); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc new file mode 100644 index 00000000..2570b63f --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc @@ -0,0 +1,33 @@ +// Test that dynamically allocated TLS space is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx %p/SharedLibs/huge_tls_lib_so.cc -fPIC -shared -o %t-so.so +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + "-so.so"; + + void *handle = dlopen(path.c_str(), RTLD_LAZY); + assert(handle != 0); + typedef void **(* store_t)(void *p); + store_t StoreToTLS = (store_t)dlsym(handle, "StoreToTLS"); + assert(dlerror() == 0); + + void *p = malloc(1337); + void **p_in_tls = StoreToTLS(p); + assert(*p_in_tls == p); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc new file mode 100644 index 00000000..3dea41ed --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc @@ -0,0 +1,37 @@ +// Test that dynamically allocated thread-specific storage is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include +#include +#include + +// From glibc: this many keys are stored in the thread descriptor directly. +const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; + +int main() { + static const unsigned kDummyKeysCount = PTHREAD_KEY_2NDLEVEL_SIZE; + int res; + pthread_key_t dummy_keys[kDummyKeysCount]; + for (unsigned i = 0; i < kDummyKeysCount; i++) { + res = pthread_key_create(&dummy_keys[i], NULL); + assert(res == 0); + } + pthread_key_t key; + res = pthread_key_create(&key, NULL); + assert(key >= PTHREAD_KEY_2NDLEVEL_SIZE); + assert(res == 0); + void *p = malloc(1337); + res = pthread_setspecific(key, p); + assert(res == 0); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc new file mode 100644 index 00000000..b75f1515 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc @@ -0,0 +1,31 @@ +// Test that statically allocated thread-specific storage is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include +#include +#include + +// From glibc: this many keys are stored in the thread descriptor directly. +const unsigned PTHREAD_KEY_2NDLEVEL_SIZE = 32; + +int main() { + pthread_key_t key; + int res; + res = pthread_key_create(&key, NULL); + assert(res == 0); + assert(key < PTHREAD_KEY_2NDLEVEL_SIZE); + void *p = malloc(1337); + res = pthread_setspecific(key, p); + assert(res == 0); + fprintf(stderr, "Test alloc: %p.\n", p); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_static.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_static.cc new file mode 100644 index 00000000..9ccb2b2b --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_static.cc @@ -0,0 +1,21 @@ +// Test that statically allocated TLS space is included in the root set. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_tls=1" %t 2>&1 +// RUN: LSAN_OPTIONS="" %t 2>&1 + +#include +#include + +__thread void *tls_var; + +int main() { + tls_var = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", tls_var); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_unaligned.cc b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_unaligned.cc new file mode 100644 index 00000000..bc75f11b --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/TestCases/use_unaligned.cc @@ -0,0 +1,23 @@ +// Test that unaligned pointers are detected correctly. +// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=0" not %t 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE:"use_unaligned=1" %t 2>&1 + +#include +#include +#include + +void *arr[2]; + +int main() { + void *p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + char *char_arr = (char *)arr; + memcpy(char_arr + 1, &p, sizeof(p)); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: Directly leaked 1337 byte object at [[ADDR]] +// CHECK: LeakSanitizer: detected memory leaks +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/projects/compiler-rt/lib/lsan/lit_tests/Unit/lit.site.cfg.in b/projects/compiler-rt/lib/lsan/lit_tests/Unit/lit.site.cfg.in new file mode 100644 index 00000000..a3a4e9ad --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/Unit/lit.site.cfg.in @@ -0,0 +1,12 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") + +# Setup config name. +config.name = 'LeakSanitizer-Unit' +# Setup test source and exec root. For unit tests, we define +# it as build directory with LSan unit tests. +config.test_exec_root = "@LSAN_BINARY_DIR@/tests" +config.test_source_root = config.test_exec_root diff --git a/projects/compiler-rt/lib/lsan/lit_tests/lit.common.cfg b/projects/compiler-rt/lib/lsan/lit_tests/lit.common.cfg new file mode 100644 index 00000000..96dc1b1f --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lit_tests/lit.common.cfg @@ -0,0 +1,43 @@ +# -*- Python -*- + +# Common configuration for running leak detection tests under LSan/ASan. + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup source root. +lsan_lit_src_root = get_required_attr(config, 'lsan_lit_src_root') +config.test_source_root = os.path.join(lsan_lit_src_root, 'TestCases') + +clang_cxxflags = ("--driver-mode=g++ " + + "-g " + + "-O0 " + + "-m64 ") + +clang_cflags = ("-g " + + "-O0 " + + "-m64 ") + +config.clang_cxxflags = clang_cxxflags + +config.substitutions.append( ("%clangxx ", (" " + config.clang + " " + + clang_cxxflags + " ")) ) + +config.clang_cflags = clang_cflags + +config.substitutions.append( ("%clang ", (" " + config.clang + " " + + clang_cflags + " ")) ) + +# LeakSanitizer tests are currently supported on x86-64 Linux only. +if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64']: + config.unsupported = True + +config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/projects/compiler-rt/lib/lsan/lsan.cc b/projects/compiler-rt/lib/lsan/lsan.cc new file mode 100644 index 00000000..058bbdba --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan.cc @@ -0,0 +1,71 @@ +//=-- lsan.cc -------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Standalone LSan RTL. +// +//===----------------------------------------------------------------------===// + +#include "lsan.h" + +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "lsan_allocator.h" +#include "lsan_common.h" +#include "lsan_thread.h" + +bool lsan_inited; +bool lsan_init_is_running; + +namespace __lsan { + +static void InitializeCommonFlags() { + CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); + cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); + cf->malloc_context_size = 30; + cf->detect_leaks = true; + + ParseCommonFlagsFromString(GetEnv("LSAN_OPTIONS")); +} + +} // namespace __lsan + +using namespace __lsan; // NOLINT + +extern "C" void __lsan_init() { + CHECK(!lsan_init_is_running); + if (lsan_inited) + return; + lsan_init_is_running = true; + SanitizerToolName = "LeakSanitizer"; + InitializeCommonFlags(); + InitializeAllocator(); + InitTlsSize(); + InitializeInterceptors(); + InitializeThreadRegistry(); + u32 tid = ThreadCreate(0, 0, true); + CHECK_EQ(tid, 0); + ThreadStart(tid, GetTid()); + SetCurrentThread(tid); + + // Start symbolizer process if necessary. + if (common_flags()->symbolize) { + Symbolizer::Init(common_flags()->external_symbolizer_path); + } else { + Symbolizer::Disable(); + } + + InitCommonLsan(); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) + Atexit(DoLeakCheck); + lsan_inited = true; + lsan_init_is_running = false; +} + diff --git a/projects/compiler-rt/lib/lsan/lsan.h b/projects/compiler-rt/lib/lsan/lsan.h new file mode 100644 index 00000000..3e7f76b0 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan.h @@ -0,0 +1,27 @@ +//=-- lsan.h --------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Private header for standalone LSan RTL. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __lsan { + +void InitializeInterceptors(); + +} // namespace __lsan + +extern bool lsan_inited; +extern bool lsan_init_is_running; + +extern "C" void __lsan_init(); diff --git a/projects/compiler-rt/lib/lsan/lsan_allocator.cc b/projects/compiler-rt/lib/lsan/lsan_allocator.cc new file mode 100644 index 00000000..f7eee131 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_allocator.cc @@ -0,0 +1,198 @@ +//=-- lsan_allocator.cc ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// See lsan_allocator.h for details. +// +//===----------------------------------------------------------------------===// + +#include "lsan_allocator.h" + +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "lsan_common.h" + +extern "C" void *memset(void *ptr, int value, uptr num); + +namespace __lsan { + +static const uptr kMaxAllowedMallocSize = 8UL << 30; +static const uptr kAllocatorSpace = 0x600000000000ULL; +static const uptr kAllocatorSize = 0x40000000000ULL; // 4T. + +struct ChunkMetadata { + bool allocated : 8; // Must be first. + ChunkTag tag : 2; + uptr requested_size : 54; + u32 stack_trace_id; +}; + +typedef SizeClassAllocator64 PrimaryAllocator; +typedef SizeClassAllocatorLocalCache AllocatorCache; +typedef LargeMmapAllocator<> SecondaryAllocator; +typedef CombinedAllocator Allocator; + +static Allocator allocator; +static THREADLOCAL AllocatorCache cache; + +void InitializeAllocator() { + allocator.Init(); +} + +void AllocatorThreadFinish() { + allocator.SwallowCache(&cache); +} + +static ChunkMetadata *Metadata(void *p) { + return reinterpret_cast(allocator.GetMetaData(p)); +} + +static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { + if (!p) return; + ChunkMetadata *m = Metadata(p); + CHECK(m); + m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; + m->stack_trace_id = StackDepotPut(stack.trace, stack.size); + m->requested_size = size; + atomic_store(reinterpret_cast(m), 1, memory_order_relaxed); +} + +static void RegisterDeallocation(void *p) { + if (!p) return; + ChunkMetadata *m = Metadata(p); + CHECK(m); + atomic_store(reinterpret_cast(m), 0, memory_order_relaxed); +} + +void *Allocate(const StackTrace &stack, uptr size, uptr alignment, + bool cleared) { + if (size == 0) + size = 1; + if (size > kMaxAllowedMallocSize) { + Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); + return 0; + } + void *p = allocator.Allocate(&cache, size, alignment, false); + // Do not rely on the allocator to clear the memory (it's slow). + if (cleared && allocator.FromPrimary(p)) + memset(p, 0, size); + RegisterAllocation(stack, p, size); + return p; +} + +void Deallocate(void *p) { + RegisterDeallocation(p); + allocator.Deallocate(&cache, p); +} + +void *Reallocate(const StackTrace &stack, void *p, uptr new_size, + uptr alignment) { + RegisterDeallocation(p); + if (new_size > kMaxAllowedMallocSize) { + Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); + allocator.Deallocate(&cache, p); + return 0; + } + p = allocator.Reallocate(&cache, p, new_size, alignment); + RegisterAllocation(stack, p, new_size); + return p; +} + +void GetAllocatorCacheRange(uptr *begin, uptr *end) { + *begin = (uptr)&cache; + *end = *begin + sizeof(cache); +} + +uptr GetMallocUsableSize(void *p) { + ChunkMetadata *m = Metadata(p); + if (!m) return 0; + return m->requested_size; +} + +///// Interface to the common LSan module. ///// + +void LockAllocator() { + allocator.ForceLock(); +} + +void UnlockAllocator() { + allocator.ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&allocator; + *end = *begin + sizeof(allocator); +} + +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast(p); + uptr chunk = reinterpret_cast(allocator.GetBlockBeginFastLocked(p)); + if (!chunk) return 0; + // LargeMmapAllocator considers pointers to the meta-region of a chunk to be + // valid, but we don't want that. + if (addr < chunk) return 0; + ChunkMetadata *m = Metadata(reinterpret_cast(chunk)); + CHECK(m); + if (m->allocated && addr < chunk + m->requested_size) + return chunk; + return 0; +} + +uptr GetUserBegin(uptr chunk) { + return chunk; +} + +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = Metadata(reinterpret_cast(chunk)); + CHECK(metadata_); +} + +bool LsanMetadata::allocated() const { + return reinterpret_cast(metadata_)->allocated; +} + +ChunkTag LsanMetadata::tag() const { + return reinterpret_cast(metadata_)->tag; +} + +void LsanMetadata::set_tag(ChunkTag value) { + reinterpret_cast(metadata_)->tag = value; +} + +uptr LsanMetadata::requested_size() const { + return reinterpret_cast(metadata_)->requested_size; +} + +u32 LsanMetadata::stack_trace_id() const { + return reinterpret_cast(metadata_)->stack_trace_id; +} + +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + allocator.ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + void *chunk = allocator.GetBlockBegin(p); + if (!chunk || p < chunk) return kIgnoreObjectInvalid; + ChunkMetadata *m = Metadata(chunk); + CHECK(m); + if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) { + if (m->tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->tag = kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} +} // namespace __lsan diff --git a/projects/compiler-rt/lib/lsan/lsan_allocator.h b/projects/compiler-rt/lib/lsan/lsan_allocator.h new file mode 100644 index 00000000..00c55ae0 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_allocator.h @@ -0,0 +1,39 @@ +//=-- lsan_allocator.h ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Allocator for standalone LSan. +// +//===----------------------------------------------------------------------===// + +#ifndef LSAN_ALLOCATOR_H +#define LSAN_ALLOCATOR_H + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +namespace __lsan { + +void *Allocate(const StackTrace &stack, uptr size, uptr alignment, + bool cleared); +void Deallocate(void *p); +void *Reallocate(const StackTrace &stack, void *p, uptr new_size, + uptr alignment); +uptr GetMallocUsableSize(void *p); + +template +void ForEachChunk(const Callable &callback); + +void GetAllocatorCacheRange(uptr *begin, uptr *end); +void AllocatorThreadFinish(); +void InitializeAllocator(); + +} // namespace __lsan + +#endif // LSAN_ALLOCATOR_H diff --git a/projects/compiler-rt/lib/lsan/lsan_common.cc b/projects/compiler-rt/lib/lsan/lsan_common.cc new file mode 100644 index 00000000..15258841 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_common.cc @@ -0,0 +1,592 @@ +//=-- lsan_common.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Implementation of common leak checking functionality. +// +//===----------------------------------------------------------------------===// + +#include "lsan_common.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" +#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_report_decorator.h" + +#if CAN_SANITIZE_LEAKS +namespace __lsan { + +// This mutex is used to prevent races between DoLeakCheck and IgnoreObject. +BlockingMutex global_mutex(LINKER_INITIALIZED); + +THREADLOCAL int disable_counter; +bool DisabledInThisThread() { return disable_counter > 0; } + +Flags lsan_flags; + +static void InitializeFlags() { + Flags *f = flags(); + // Default values. + f->report_objects = false; + f->resolution = 0; + f->max_leaks = 0; + f->exitcode = 23; + f->suppressions=""; + f->use_registers = true; + f->use_globals = true; + f->use_stacks = true; + f->use_tls = true; + f->use_unaligned = false; + f->verbosity = 0; + f->log_pointers = false; + f->log_threads = false; + + const char *options = GetEnv("LSAN_OPTIONS"); + if (options) { + ParseFlag(options, &f->use_registers, "use_registers"); + ParseFlag(options, &f->use_globals, "use_globals"); + ParseFlag(options, &f->use_stacks, "use_stacks"); + ParseFlag(options, &f->use_tls, "use_tls"); + ParseFlag(options, &f->use_unaligned, "use_unaligned"); + ParseFlag(options, &f->report_objects, "report_objects"); + ParseFlag(options, &f->resolution, "resolution"); + CHECK_GE(&f->resolution, 0); + ParseFlag(options, &f->max_leaks, "max_leaks"); + CHECK_GE(&f->max_leaks, 0); + ParseFlag(options, &f->verbosity, "verbosity"); + ParseFlag(options, &f->log_pointers, "log_pointers"); + ParseFlag(options, &f->log_threads, "log_threads"); + ParseFlag(options, &f->exitcode, "exitcode"); + ParseFlag(options, &f->suppressions, "suppressions"); + } +} + +SuppressionContext *suppression_ctx; + +void InitializeSuppressions() { + CHECK(!suppression_ctx); + ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; + suppression_ctx = new(placeholder_) SuppressionContext; + char *suppressions_from_file; + uptr buffer_size; + if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file, + &buffer_size, 1 << 26 /* max_len */)) + suppression_ctx->Parse(suppressions_from_file); + if (flags()->suppressions[0] && !buffer_size) { + Printf("LeakSanitizer: failed to read suppressions file '%s'\n", + flags()->suppressions); + Die(); + } + if (&__lsan_default_suppressions) + suppression_ctx->Parse(__lsan_default_suppressions()); +} + +void InitCommonLsan() { + InitializeFlags(); + if (common_flags()->detect_leaks) { + // Initialization which can fail or print warnings should only be done if + // LSan is actually enabled. + InitializeSuppressions(); + InitializePlatformSpecificModules(); + } +} + +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Error() { return Red(); } + const char *Leak() { return Blue(); } + const char *End() { return Default(); } +}; + +static inline bool CanBeAHeapPointer(uptr p) { + // Since our heap is located in mmap-ed memory, we can assume a sensible lower + // bound on heap addresses. + const uptr kMinAddress = 4 * 4096; + if (p < kMinAddress) return false; +#ifdef __x86_64__ + // Accept only canonical form user-space addresses. + return ((p >> 47) == 0); +#else + return true; +#endif +} + +// Scans the memory range, looking for byte patterns that point into allocator +// chunks. Marks those chunks with |tag| and adds them to |frontier|. +// There are two usage modes for this function: finding reachable or ignored +// chunks (|tag| = kReachable or kIgnored) and finding indirectly leaked chunks +// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill, +// so |frontier| = 0. +void ScanRangeForPointers(uptr begin, uptr end, + Frontier *frontier, + const char *region_type, ChunkTag tag) { + const uptr alignment = flags()->pointer_alignment(); + if (flags()->log_pointers) + Report("Scanning %s range %p-%p.\n", region_type, begin, end); + uptr pp = begin; + if (pp % alignment) + pp = pp + alignment - pp % alignment; + for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT + void *p = *reinterpret_cast(pp); + if (!CanBeAHeapPointer(reinterpret_cast(p))) continue; + uptr chunk = PointsIntoChunk(p); + if (!chunk) continue; + // Pointers to self don't count. This matters when tag == kIndirectlyLeaked. + if (chunk == begin) continue; + LsanMetadata m(chunk); + // Reachable beats ignored beats leaked. + if (m.tag() == kReachable) continue; + if (m.tag() == kIgnored && tag != kReachable) continue; + m.set_tag(tag); + if (flags()->log_pointers) + Report("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p, + chunk, chunk + m.requested_size(), m.requested_size()); + if (frontier) + frontier->push_back(chunk); + } +} + +void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) { + Frontier *frontier = reinterpret_cast(arg); + ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable); +} + +// Scans thread data (stacks and TLS) for heap pointers. +static void ProcessThreads(SuspendedThreadsList const &suspended_threads, + Frontier *frontier) { + InternalScopedBuffer registers(SuspendedThreadsList::RegisterCount()); + uptr registers_begin = reinterpret_cast(registers.data()); + uptr registers_end = registers_begin + registers.size(); + for (uptr i = 0; i < suspended_threads.thread_count(); i++) { + uptr os_id = static_cast(suspended_threads.GetThreadID(i)); + if (flags()->log_threads) Report("Processing thread %d.\n", os_id); + uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end; + bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end, + &tls_begin, &tls_end, + &cache_begin, &cache_end); + if (!thread_found) { + // If a thread can't be found in the thread registry, it's probably in the + // process of destruction. Log this event and move on. + if (flags()->log_threads) + Report("Thread %d not found in registry.\n", os_id); + continue; + } + uptr sp; + bool have_registers = + (suspended_threads.GetRegistersAndSP(i, registers.data(), &sp) == 0); + if (!have_registers) { + Report("Unable to get registers from thread %d.\n"); + // If unable to get SP, consider the entire stack to be reachable. + sp = stack_begin; + } + + if (flags()->use_registers && have_registers) + ScanRangeForPointers(registers_begin, registers_end, frontier, + "REGISTERS", kReachable); + + if (flags()->use_stacks) { + if (flags()->log_threads) + Report("Stack at %p-%p, SP = %p.\n", stack_begin, stack_end, sp); + if (sp < stack_begin || sp >= stack_end) { + // SP is outside the recorded stack range (e.g. the thread is running a + // signal handler on alternate stack). Again, consider the entire stack + // range to be reachable. + if (flags()->log_threads) + Report("WARNING: stack pointer not in stack range.\n"); + } else { + // Shrink the stack range to ignore out-of-scope values. + stack_begin = sp; + } + ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", + kReachable); + ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier); + } + + if (flags()->use_tls) { + if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end); + if (cache_begin == cache_end) { + ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable); + } else { + // Because LSan should not be loaded with dlopen(), we can assume + // that allocator cache will be part of static TLS image. + CHECK_LE(tls_begin, cache_begin); + CHECK_GE(tls_end, cache_end); + if (tls_begin < cache_begin) + ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS", + kReachable); + if (tls_end > cache_end) + ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable); + } + } + } +} + +static void FloodFillTag(Frontier *frontier, ChunkTag tag) { + while (frontier->size()) { + uptr next_chunk = frontier->back(); + frontier->pop_back(); + LsanMetadata m(next_chunk); + ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier, + "HEAP", tag); + } +} + +// ForEachChunk callback. If the chunk is marked as leaked, marks all chunks +// which are reachable from it as indirectly leaked. +static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) { + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() != kReachable) { + ScanRangeForPointers(chunk, chunk + m.requested_size(), + /* frontier */ 0, "HEAP", kIndirectlyLeaked); + } +} + +// ForEachChunk callback. If chunk is marked as ignored, adds its address to +// frontier. +static void CollectIgnoredCb(uptr chunk, void *arg) { + CHECK(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() == kIgnored) + reinterpret_cast(arg)->push_back(chunk); +} + +// Sets the appropriate tag on each chunk. +static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { + // Holds the flood fill frontier. + Frontier frontier(GetPageSizeCached()); + + if (flags()->use_globals) + ProcessGlobalRegions(&frontier); + ProcessThreads(suspended_threads, &frontier); + FloodFillTag(&frontier, kReachable); + // The check here is relatively expensive, so we do this in a separate flood + // fill. That way we can skip the check for chunks that are reachable + // otherwise. + if (flags()->log_pointers) + Report("Processing platform-specific allocations.\n"); + ProcessPlatformSpecificAllocations(&frontier); + FloodFillTag(&frontier, kReachable); + + if (flags()->log_pointers) + Report("Scanning ignored chunks.\n"); + CHECK_EQ(0, frontier.size()); + ForEachChunk(CollectIgnoredCb, &frontier); + FloodFillTag(&frontier, kIgnored); + + // Iterate over leaked chunks and mark those that are reachable from other + // leaked chunks. + if (flags()->log_pointers) + Report("Scanning leaked chunks.\n"); + ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */); +} + +static void PrintStackTraceById(u32 stack_trace_id) { + CHECK(stack_trace_id); + uptr size = 0; + const uptr *trace = StackDepotGet(stack_trace_id, &size); + StackTrace::PrintStack(trace, size); +} + +// ForEachChunk callback. Aggregates unreachable chunks into a LeakReport. +static void CollectLeaksCb(uptr chunk, void *arg) { + CHECK(arg); + LeakReport *leak_report = reinterpret_cast(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (!m.allocated()) return; + if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { + uptr resolution = flags()->resolution; + if (resolution > 0) { + uptr size = 0; + const uptr *trace = StackDepotGet(m.stack_trace_id(), &size); + size = Min(size, resolution); + leak_report->Add(StackDepotPut(trace, size), m.requested_size(), m.tag()); + } else { + leak_report->Add(m.stack_trace_id(), m.requested_size(), m.tag()); + } + } +} + +// ForEachChunkCallback. Prints addresses of unreachable chunks. +static void PrintLeakedCb(uptr chunk, void *arg) { + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (!m.allocated()) return; + if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { + Printf("%s leaked %zu byte object at %p.\n", + m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly", + m.requested_size(), chunk); + } +} + +static void PrintMatchedSuppressions() { + InternalMmapVector matched(1); + suppression_ctx->GetMatched(&matched); + if (!matched.size()) + return; + const char *line = "-----------------------------------------------------"; + Printf("%s\n", line); + Printf("Suppressions used:\n"); + Printf(" count bytes template\n"); + for (uptr i = 0; i < matched.size(); i++) + Printf("%7zu %10zu %s\n", static_cast(matched[i]->hit_count), + matched[i]->weight, matched[i]->templ); + Printf("%s\n\n", line); +} + +static void PrintLeaked() { + Printf("\n"); + Printf("Reporting individual objects:\n"); + ForEachChunk(PrintLeakedCb, 0 /* arg */); +} + +struct DoLeakCheckParam { + bool success; + LeakReport leak_report; +}; + +static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads, + void *arg) { + DoLeakCheckParam *param = reinterpret_cast(arg); + CHECK(param); + CHECK(!param->success); + CHECK(param->leak_report.IsEmpty()); + ClassifyAllChunks(suspended_threads); + ForEachChunk(CollectLeaksCb, ¶m->leak_report); + if (!param->leak_report.IsEmpty() && flags()->report_objects) + PrintLeaked(); + param->success = true; +} + +void DoLeakCheck() { + EnsureMainThreadIDIsCorrect(); + BlockingMutexLock l(&global_mutex); + static bool already_done; + if (already_done) return; + already_done = true; + if (&__lsan_is_turned_off && __lsan_is_turned_off()) + return; + + DoLeakCheckParam param; + param.success = false; + LockThreadRegistry(); + LockAllocator(); + StopTheWorld(DoLeakCheckCallback, ¶m); + UnlockAllocator(); + UnlockThreadRegistry(); + + if (!param.success) { + Report("LeakSanitizer has encountered a fatal error.\n"); + Die(); + } + uptr have_unsuppressed = param.leak_report.ApplySuppressions(); + if (have_unsuppressed) { + Decorator d; + Printf("\n" + "=================================================================" + "\n"); + Printf("%s", d.Error()); + Report("ERROR: LeakSanitizer: detected memory leaks\n"); + Printf("%s", d.End()); + param.leak_report.PrintLargest(flags()->max_leaks); + } + if (have_unsuppressed || (flags()->verbosity >= 1)) { + PrintMatchedSuppressions(); + param.leak_report.PrintSummary(); + } + if (have_unsuppressed && flags()->exitcode) + internal__exit(flags()->exitcode); +} + +static Suppression *GetSuppressionForAddr(uptr addr) { + static const uptr kMaxAddrFrames = 16; + InternalScopedBuffer addr_frames(kMaxAddrFrames); + for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo(); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); + for (uptr i = 0; i < addr_frames_num; i++) { + Suppression* s; + if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) || + suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s) || + suppression_ctx->Match(addr_frames[i].module, SuppressionLeak, &s)) + return s; + } + return 0; +} + +static Suppression *GetSuppressionForStack(u32 stack_trace_id) { + uptr size = 0; + const uptr *trace = StackDepotGet(stack_trace_id, &size); + for (uptr i = 0; i < size; i++) { + Suppression *s = + GetSuppressionForAddr(StackTrace::GetPreviousInstructionPc(trace[i])); + if (s) return s; + } + return 0; +} + +///// LeakReport implementation. ///// + +// A hard limit on the number of distinct leaks, to avoid quadratic complexity +// in LeakReport::Add(). We don't expect to ever see this many leaks in +// real-world applications. +// FIXME: Get rid of this limit by changing the implementation of LeakReport to +// use a hash table. +const uptr kMaxLeaksConsidered = 5000; + +void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) { + CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked); + bool is_directly_leaked = (tag == kDirectlyLeaked); + for (uptr i = 0; i < leaks_.size(); i++) + if (leaks_[i].stack_trace_id == stack_trace_id && + leaks_[i].is_directly_leaked == is_directly_leaked) { + leaks_[i].hit_count++; + leaks_[i].total_size += leaked_size; + return; + } + if (leaks_.size() == kMaxLeaksConsidered) return; + Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id, + is_directly_leaked, /* is_suppressed */ false }; + leaks_.push_back(leak); +} + +static bool LeakComparator(const Leak &leak1, const Leak &leak2) { + if (leak1.is_directly_leaked == leak2.is_directly_leaked) + return leak1.total_size > leak2.total_size; + else + return leak1.is_directly_leaked; +} + +void LeakReport::PrintLargest(uptr num_leaks_to_print) { + CHECK(leaks_.size() <= kMaxLeaksConsidered); + Printf("\n"); + if (leaks_.size() == kMaxLeaksConsidered) + Printf("Too many leaks! Only the first %zu leaks encountered will be " + "reported.\n", + kMaxLeaksConsidered); + + uptr unsuppressed_count = 0; + for (uptr i = 0; i < leaks_.size(); i++) + if (!leaks_[i].is_suppressed) unsuppressed_count++; + if (num_leaks_to_print > 0 && num_leaks_to_print < unsuppressed_count) + Printf("The %zu largest leak(s):\n", num_leaks_to_print); + InternalSort(&leaks_, leaks_.size(), LeakComparator); + uptr leaks_printed = 0; + Decorator d; + for (uptr i = 0; i < leaks_.size(); i++) { + if (leaks_[i].is_suppressed) continue; + Printf("%s", d.Leak()); + Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n", + leaks_[i].is_directly_leaked ? "Direct" : "Indirect", + leaks_[i].total_size, leaks_[i].hit_count); + Printf("%s", d.End()); + PrintStackTraceById(leaks_[i].stack_trace_id); + leaks_printed++; + if (leaks_printed == num_leaks_to_print) break; + } + if (leaks_printed < unsuppressed_count) { + uptr remaining = unsuppressed_count - leaks_printed; + Printf("Omitting %zu more leak(s).\n", remaining); + } +} + +void LeakReport::PrintSummary() { + CHECK(leaks_.size() <= kMaxLeaksConsidered); + uptr bytes = 0, allocations = 0; + for (uptr i = 0; i < leaks_.size(); i++) { + if (leaks_[i].is_suppressed) continue; + bytes += leaks_[i].total_size; + allocations += leaks_[i].hit_count; + } + InternalScopedBuffer summary(kMaxSummaryLength); + internal_snprintf(summary.data(), summary.size(), + "%zu byte(s) leaked in %zu allocation(s).", bytes, + allocations); + ReportErrorSummary(summary.data()); +} + +uptr LeakReport::ApplySuppressions() { + uptr unsuppressed_count = 0; + for (uptr i = 0; i < leaks_.size(); i++) { + Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id); + if (s) { + s->weight += leaks_[i].total_size; + s->hit_count += leaks_[i].hit_count; + leaks_[i].is_suppressed = true; + } else { + unsuppressed_count++; + } + } + return unsuppressed_count; +} +} // namespace __lsan +#endif // CAN_SANITIZE_LEAKS + +using namespace __lsan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_ignore_object(const void *p) { +#if CAN_SANITIZE_LEAKS + if (!common_flags()->detect_leaks) + return; + // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not + // locked. + BlockingMutexLock l(&global_mutex); + IgnoreObjectResult res = IgnoreObjectLocked(p); + if (res == kIgnoreObjectInvalid && flags()->verbosity >= 2) + Report("__lsan_ignore_object(): no heap object found at %p", p); + if (res == kIgnoreObjectAlreadyIgnored && flags()->verbosity >= 2) + Report("__lsan_ignore_object(): " + "heap object at %p is already being ignored\n", p); + if (res == kIgnoreObjectSuccess && flags()->verbosity >= 3) + Report("__lsan_ignore_object(): ignoring heap object at %p\n", p); +#endif // CAN_SANITIZE_LEAKS +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_disable() { +#if CAN_SANITIZE_LEAKS + __lsan::disable_counter++; +#endif +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_enable() { +#if CAN_SANITIZE_LEAKS + if (!__lsan::disable_counter && common_flags()->detect_leaks) { + Report("Unmatched call to __lsan_enable().\n"); + Die(); + } + __lsan::disable_counter--; +#endif +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_do_leak_check() { +#if CAN_SANITIZE_LEAKS + if (common_flags()->detect_leaks) + __lsan::DoLeakCheck(); +#endif // CAN_SANITIZE_LEAKS +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __lsan_is_turned_off() { + return 0; +} +#endif +} // extern "C" diff --git a/projects/compiler-rt/lib/lsan/lsan_common.h b/projects/compiler-rt/lib/lsan/lsan_common.h new file mode 100644 index 00000000..d490f8ba --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_common.h @@ -0,0 +1,178 @@ +//=-- lsan_common.h -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Private LSan header. +// +//===----------------------------------------------------------------------===// + +#ifndef LSAN_COMMON_H +#define LSAN_COMMON_H + +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_platform.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +#if SANITIZER_LINUX && defined(__x86_64__) +#define CAN_SANITIZE_LEAKS 1 +#else +#define CAN_SANITIZE_LEAKS 0 +#endif + +namespace __lsan { + +// Chunk tags. +enum ChunkTag { + kDirectlyLeaked = 0, // default + kIndirectlyLeaked = 1, + kReachable = 2, + kIgnored = 3 +}; + +struct Flags { + uptr pointer_alignment() const { + return use_unaligned ? 1 : sizeof(uptr); + } + + // Print addresses of leaked objects after main leak report. + bool report_objects; + // Aggregate two objects into one leak if this many stack frames match. If + // zero, the entire stack trace must match. + int resolution; + // The number of leaks reported. + int max_leaks; + // If nonzero kill the process with this exit code upon finding leaks. + int exitcode; + // Suppressions file name. + const char* suppressions; + + // Flags controlling the root set of reachable memory. + // Global variables (.data and .bss). + bool use_globals; + // Thread stacks. + bool use_stacks; + // Thread registers. + bool use_registers; + // TLS and thread-specific storage. + bool use_tls; + + // Consider unaligned pointers valid. + bool use_unaligned; + + // User-visible verbosity. + int verbosity; + + // Debug logging. + bool log_pointers; + bool log_threads; +}; + +extern Flags lsan_flags; +inline Flags *flags() { return &lsan_flags; } + +struct Leak { + uptr hit_count; + uptr total_size; + u32 stack_trace_id; + bool is_directly_leaked; + bool is_suppressed; +}; + +// Aggregates leaks by stack trace prefix. +class LeakReport { + public: + LeakReport() : leaks_(1) {} + void Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag); + void PrintLargest(uptr max_leaks); + void PrintSummary(); + bool IsEmpty() { return leaks_.size() == 0; } + uptr ApplySuppressions(); + private: + InternalMmapVector leaks_; +}; + +typedef InternalMmapVector Frontier; + +// Platform-specific functions. +void InitializePlatformSpecificModules(); +void ProcessGlobalRegions(Frontier *frontier); +void ProcessPlatformSpecificAllocations(Frontier *frontier); + +void ScanRangeForPointers(uptr begin, uptr end, + Frontier *frontier, + const char *region_type, ChunkTag tag); + +enum IgnoreObjectResult { + kIgnoreObjectSuccess, + kIgnoreObjectAlreadyIgnored, + kIgnoreObjectInvalid +}; + +// Functions called from the parent tool. +void InitCommonLsan(); +void DoLeakCheck(); +bool DisabledInThisThread(); + +// The following must be implemented in the parent tool. + +void ForEachChunk(ForEachChunkCallback callback, void *arg); +// Returns the address range occupied by the global allocator object. +void GetAllocatorGlobalRange(uptr *begin, uptr *end); +// Wrappers for allocator's ForceLock()/ForceUnlock(). +void LockAllocator(); +void UnlockAllocator(); +// Wrappers for ThreadRegistry access. +void LockThreadRegistry(); +void UnlockThreadRegistry(); +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end); +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg); +// If called from the main thread, updates the main thread's TID in the thread +// registry. We need this to handle processes that fork() without a subsequent +// exec(), which invalidates the recorded TID. To update it, we must call +// gettid() from the main thread. Our solution is to call this function before +// leak checking and also before every call to pthread_create() (to handle cases +// where leak checking is initiated from a non-main thread). +void EnsureMainThreadIDIsCorrect(); +// If p points into a chunk that has been allocated to the user, returns its +// user-visible address. Otherwise, returns 0. +uptr PointsIntoChunk(void *p); +// Returns address of user-visible chunk contained in this allocator chunk. +uptr GetUserBegin(uptr chunk); +// Helper for __lsan_ignore_object(). +IgnoreObjectResult IgnoreObjectLocked(const void *p); +// Wrapper for chunk metadata operations. +class LsanMetadata { + public: + // Constructor accepts address of user-visible chunk. + explicit LsanMetadata(uptr chunk); + bool allocated() const; + ChunkTag tag() const; + void set_tag(ChunkTag value); + uptr requested_size() const; + u32 stack_trace_id() const; + private: + void *metadata_; +}; + +} // namespace __lsan + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __lsan_is_turned_off(); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char *__lsan_default_suppressions(); +} // extern "C" + +#endif // LSAN_COMMON_H diff --git a/projects/compiler-rt/lib/lsan/lsan_common_linux.cc b/projects/compiler-rt/lib/lsan/lsan_common_linux.cc new file mode 100644 index 00000000..ef8857fe --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_common_linux.cc @@ -0,0 +1,141 @@ +//=-- lsan_common_linux.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Implementation of common leak checking functionality. Linux-specific code. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#include "lsan_common.h" + +#if CAN_SANITIZE_LEAKS && SANITIZER_LINUX +#include + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __lsan { + +static const char kLinkerName[] = "ld"; +// We request 2 modules matching "ld", so we can print a warning if there's more +// than one match. But only the first one is actually used. +static char linker_placeholder[2 * sizeof(LoadedModule)] ALIGNED(64); +static LoadedModule *linker = 0; + +static bool IsLinker(const char* full_name) { + return LibraryNameIs(full_name, kLinkerName); +} + +void InitializePlatformSpecificModules() { + internal_memset(linker_placeholder, 0, sizeof(linker_placeholder)); + uptr num_matches = GetListOfModules( + reinterpret_cast(linker_placeholder), 2, IsLinker); + if (num_matches == 1) { + linker = reinterpret_cast(linker_placeholder); + return; + } + if (num_matches == 0) + Report("LeakSanitizer: Dynamic linker not found. " + "TLS will not be handled correctly.\n"); + else if (num_matches > 1) + Report("LeakSanitizer: Multiple modules match \"%s\". " + "TLS will not be handled correctly.\n", kLinkerName); + linker = 0; +} + +static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, + void *data) { + Frontier *frontier = reinterpret_cast(data); + for (uptr j = 0; j < info->dlpi_phnum; j++) { + const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]); + // We're looking for .data and .bss sections, which reside in writeable, + // loadable segments. + if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) || + (phdr->p_memsz == 0)) + continue; + uptr begin = info->dlpi_addr + phdr->p_vaddr; + uptr end = begin + phdr->p_memsz; + uptr allocator_begin = 0, allocator_end = 0; + GetAllocatorGlobalRange(&allocator_begin, &allocator_end); + if (begin <= allocator_begin && allocator_begin < end) { + CHECK_LE(allocator_begin, allocator_end); + CHECK_LT(allocator_end, end); + if (begin < allocator_begin) + ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL", + kReachable); + if (allocator_end < end) + ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL", + kReachable); + } else { + ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable); + } + } + return 0; +} + +// Scans global variables for heap pointers. +void ProcessGlobalRegions(Frontier *frontier) { + // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of + // deadlocking by running this under StopTheWorld. However, the lock is + // reentrant, so we should be able to fix this by acquiring the lock before + // suspending threads. + dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); +} + +static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) { + CHECK(stack_id); + uptr size = 0; + const uptr *trace = map->Get(stack_id, &size); + // The top frame is our malloc/calloc/etc. The next frame is the caller. + if (size >= 2) + return trace[1]; + return 0; +} + +struct ProcessPlatformAllocParam { + Frontier *frontier; + StackDepotReverseMap *stack_depot_reverse_map; +}; + +// ForEachChunk callback. Identifies unreachable chunks which must be treated as +// reachable. Marks them as reachable and adds them to the frontier. +static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { + CHECK(arg); + ProcessPlatformAllocParam *param = + reinterpret_cast(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() != kReachable) { + u32 stack_id = m.stack_trace_id(); + uptr caller_pc = 0; + if (stack_id > 0) + caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map); + // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark + // it as reachable, as we can't properly report its allocation stack anyway. + if (caller_pc == 0 || linker->containsAddress(caller_pc)) { + m.set_tag(kReachable); + param->frontier->push_back(chunk); + } + } +} + +// Handles dynamically allocated TLS blocks by treating all chunks allocated +// from ld-linux.so as reachable. +void ProcessPlatformSpecificAllocations(Frontier *frontier) { + if (!flags()->use_tls) return; + if (!linker) return; + StackDepotReverseMap stack_depot_reverse_map; + ProcessPlatformAllocParam arg = {frontier, &stack_depot_reverse_map}; + ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg); +} + +} // namespace __lsan +#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/lsan/lsan_interceptors.cc b/projects/compiler-rt/lib/lsan/lsan_interceptors.cc new file mode 100644 index 00000000..400230b8 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_interceptors.cc @@ -0,0 +1,295 @@ +//=-- lsan_interceptors.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Interceptors for standalone LSan. +// +//===----------------------------------------------------------------------===// + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include "lsan.h" +#include "lsan_allocator.h" +#include "lsan_thread.h" + +using namespace __lsan; + +extern "C" { +int pthread_attr_init(void *attr); +int pthread_attr_destroy(void *attr); +int pthread_attr_getdetachstate(void *attr, int *v); +int pthread_key_create(unsigned *key, void (*destructor)(void* v)); +int pthread_setspecific(unsigned key, const void *v); +} + +#define GET_STACK_TRACE \ + StackTrace stack; \ + { \ + uptr stack_top = 0, stack_bottom = 0; \ + ThreadContext *t; \ + bool fast = common_flags()->fast_unwind_on_malloc; \ + if (fast && (t = CurrentThreadContext())) { \ + stack_top = t->stack_end(); \ + stack_bottom = t->stack_begin(); \ + } \ + stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \ + StackTrace::GetCurrentPc(), \ + GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ + } + +#define ENSURE_LSAN_INITED do { \ + CHECK(!lsan_init_is_running); \ + if (!lsan_inited) \ + __lsan_init(); \ +} while (0) + +///// Malloc/free interceptors. ///// + +const bool kAlwaysClearMemory = true; + +namespace std { + struct nothrow_t; +} + +INTERCEPTOR(void*, malloc, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + return Allocate(stack, size, 1, kAlwaysClearMemory); +} + +INTERCEPTOR(void, free, void *p) { + ENSURE_LSAN_INITED; + Deallocate(p); +} + +INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (lsan_init_is_running) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const uptr kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr allocated; + uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + size *= nmemb; + return Allocate(stack, size, 1, true); +} + +INTERCEPTOR(void*, realloc, void *q, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + return Reallocate(stack, q, size, 1); +} + +INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + return Allocate(stack, size, alignment, kAlwaysClearMemory); +} + +INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); + // FIXME: Return ENOMEM if user requested more than max alloc size. + return 0; +} + +INTERCEPTOR(void*, valloc, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + if (size == 0) + size = GetPageSizeCached(); + return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); +} + +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + ENSURE_LSAN_INITED; + return GetMallocUsableSize(ptr); +} + +struct fake_mallinfo { + int x[10]; +}; + +INTERCEPTOR(struct fake_mallinfo, mallinfo, void) { + struct fake_mallinfo res; + internal_memset(&res, 0, sizeof(res)); + return res; +} + +INTERCEPTOR(int, mallopt, int cmd, int value) { + return -1; +} + +INTERCEPTOR(void*, pvalloc, uptr size) { + ENSURE_LSAN_INITED; + GET_STACK_TRACE; + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); +} + +INTERCEPTOR(void, cfree, void *p) ALIAS("free"); + +#define OPERATOR_NEW_BODY \ + ENSURE_LSAN_INITED; \ + GET_STACK_TRACE; \ + return Allocate(stack, size, 1, kAlwaysClearMemory); + +INTERCEPTOR_ATTRIBUTE +void *operator new(uptr size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new[](uptr size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new(uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } + +#define OPERATOR_DELETE_BODY \ + ENSURE_LSAN_INITED; \ + Deallocate(ptr); + +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const &) { + OPERATOR_DELETE_BODY; +} + +// We need this to intercept the __libc_memalign calls that are used to +// allocate dynamic TLS space in ld-linux.so. +INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s) ALIAS("memalign"); + +///// Thread initialization and finalization. ///// + +static unsigned g_thread_finalize_key; + +static void thread_finalize(void *v) { + uptr iter = (uptr)v; + if (iter > 1) { + if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { + Report("LeakSanitizer: failed to set thread key.\n"); + Die(); + } + return; + } + ThreadFinish(); +} + +struct ThreadParam { + void *(*callback)(void *arg); + void *param; + atomic_uintptr_t tid; +}; + +extern "C" void *__lsan_thread_start_func(void *arg) { + ThreadParam *p = (ThreadParam*)arg; + void* (*callback)(void *arg) = p->callback; + void *param = p->param; + // Wait until the last iteration to maximize the chance that we are the last + // destructor to run. + if (pthread_setspecific(g_thread_finalize_key, + (void*)kPthreadDestructorIterations)) { + Report("LeakSanitizer: failed to set thread key.\n"); + Die(); + } + int tid = 0; + while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) + internal_sched_yield(); + atomic_store(&p->tid, 0, memory_order_release); + SetCurrentThread(tid); + ThreadStart(tid, GetTid()); + return callback(param); +} + +INTERCEPTOR(int, pthread_create, void *th, void *attr, + void *(*callback)(void *), void *param) { + ENSURE_LSAN_INITED; + EnsureMainThreadIDIsCorrect(); + __sanitizer_pthread_attr_t myattr; + if (attr == 0) { + pthread_attr_init(&myattr); + attr = &myattr; + } + AdjustStackSizeLinux(attr); + int detached = 0; + pthread_attr_getdetachstate(attr, &detached); + ThreadParam p; + p.callback = callback; + p.param = param; + atomic_store(&p.tid, 0, memory_order_relaxed); + int res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p); + if (res == 0) { + int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th, detached); + CHECK_NE(tid, 0); + atomic_store(&p.tid, tid, memory_order_release); + while (atomic_load(&p.tid, memory_order_acquire) != 0) + internal_sched_yield(); + } + if (attr == &myattr) + pthread_attr_destroy(&myattr); + return res; +} + +INTERCEPTOR(int, pthread_join, void *th, void **ret) { + ENSURE_LSAN_INITED; + int tid = ThreadTid((uptr)th); + int res = REAL(pthread_join)(th, ret); + if (res == 0) + ThreadJoin(tid); + return res; +} + +namespace __lsan { + +void InitializeInterceptors() { + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(cfree); + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(realloc); + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(__libc_memalign); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(pvalloc); + INTERCEPT_FUNCTION(malloc_usable_size); + INTERCEPT_FUNCTION(mallinfo); + INTERCEPT_FUNCTION(mallopt); + INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_join); + + if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { + Report("LeakSanitizer: failed to create thread key.\n"); + Die(); + } +} + +} // namespace __lsan diff --git a/projects/compiler-rt/lib/lsan/lsan_preinit.cc b/projects/compiler-rt/lib/lsan/lsan_preinit.cc new file mode 100644 index 00000000..e6639516 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_preinit.cc @@ -0,0 +1,26 @@ +//===-- lsan_preinit.cc ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// +// Call __lsan_init at the very early stage of process startup. +//===----------------------------------------------------------------------===// + +#include "lsan.h" + +#ifndef LSAN_USE_PREINIT_ARRAY +#define LSAN_USE_PREINIT_ARRAY 1 +#endif + +#if LSAN_USE_PREINIT_ARRAY && !defined(PIC) + // We force __lsan_init to be called before anyone else by placing it into + // .preinit_array section. + __attribute__((section(".preinit_array"), used)) + void (*__local_lsan_preinit)(void) = __lsan_init; +#endif diff --git a/projects/compiler-rt/lib/lsan/lsan_thread.cc b/projects/compiler-rt/lib/lsan/lsan_thread.cc new file mode 100644 index 00000000..0f8efc09 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_thread.cc @@ -0,0 +1,160 @@ +//=-- lsan_thread.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// See lsan_thread.h for details. +// +//===----------------------------------------------------------------------===// + +#include "lsan_thread.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_thread_registry.h" +#include "lsan_allocator.h" + +namespace __lsan { + +const u32 kInvalidTid = (u32) -1; + +static ThreadRegistry *thread_registry; +static THREADLOCAL u32 current_thread_tid = kInvalidTid; + +static ThreadContextBase *CreateThreadContext(u32 tid) { + void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext"); + return new(mem) ThreadContext(tid); +} + +static const uptr kMaxThreads = 1 << 13; +static const uptr kThreadQuarantineSize = 64; + +void InitializeThreadRegistry() { + static char thread_registry_placeholder[sizeof(ThreadRegistry)] ALIGNED(64); + thread_registry = new(thread_registry_placeholder) + ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize); +} + +u32 GetCurrentThread() { + return current_thread_tid; +} + +void SetCurrentThread(u32 tid) { + current_thread_tid = tid; +} + +ThreadContext::ThreadContext(int tid) + : ThreadContextBase(tid), + stack_begin_(0), + stack_end_(0), + cache_begin_(0), + cache_end_(0), + tls_begin_(0), + tls_end_(0) {} + +struct OnStartedArgs { + uptr stack_begin, stack_end, + cache_begin, cache_end, + tls_begin, tls_end; +}; + +void ThreadContext::OnStarted(void *arg) { + OnStartedArgs *args = reinterpret_cast(arg); + stack_begin_ = args->stack_begin; + stack_end_ = args->stack_end; + tls_begin_ = args->tls_begin; + tls_end_ = args->tls_end; + cache_begin_ = args->cache_begin; + cache_end_ = args->cache_end; +} + +void ThreadContext::OnFinished() { + AllocatorThreadFinish(); +} + +u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) { + return thread_registry->CreateThread(user_id, detached, parent_tid, + /* arg */ 0); +} + +void ThreadStart(u32 tid, uptr os_id) { + OnStartedArgs args; + uptr stack_size = 0; + uptr tls_size = 0; + GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size, + &args.tls_begin, &tls_size); + args.stack_end = args.stack_begin + stack_size; + args.tls_end = args.tls_begin + tls_size; + GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); + thread_registry->StartThread(tid, os_id, &args); +} + +void ThreadFinish() { + thread_registry->FinishThread(GetCurrentThread()); +} + +ThreadContext *CurrentThreadContext() { + if (!thread_registry) return 0; + if (GetCurrentThread() == kInvalidTid) + return 0; + // No lock needed when getting current thread. + return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread()); +} + +static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { + uptr uid = (uptr)arg; + if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { + return true; + } + return false; +} + +u32 ThreadTid(uptr uid) { + return thread_registry->FindThread(FindThreadByUid, (void*)uid); +} + +void ThreadJoin(u32 tid) { + CHECK_NE(tid, kInvalidTid); + thread_registry->JoinThread(tid, /* arg */0); +} + +void EnsureMainThreadIDIsCorrect() { + if (GetCurrentThread() == 0) + CurrentThreadContext()->os_id = GetTid(); +} + +///// Interface to the common LSan module. ///// + +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end) { + ThreadContext *context = static_cast( + thread_registry->FindThreadContextByOsIDLocked(os_id)); + if (!context) return false; + *stack_begin = context->stack_begin(); + *stack_end = context->stack_end(); + *tls_begin = context->tls_begin(); + *tls_end = context->tls_end(); + *cache_begin = context->cache_begin(); + *cache_end = context->cache_end(); + return true; +} + +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg) { +} + +void LockThreadRegistry() { + thread_registry->Lock(); +} + +void UnlockThreadRegistry() { + thread_registry->Unlock(); +} + +} // namespace __lsan diff --git a/projects/compiler-rt/lib/lsan/lsan_thread.h b/projects/compiler-rt/lib/lsan/lsan_thread.h new file mode 100644 index 00000000..4641b32e --- /dev/null +++ b/projects/compiler-rt/lib/lsan/lsan_thread.h @@ -0,0 +1,53 @@ +//=-- lsan_thread.h -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Thread registry for standalone LSan. +// +//===----------------------------------------------------------------------===// + +#ifndef LSAN_THREAD_H +#define LSAN_THREAD_H + +#include "sanitizer_common/sanitizer_thread_registry.h" + +namespace __lsan { + +class ThreadContext : public ThreadContextBase { + public: + explicit ThreadContext(int tid); + void OnStarted(void *arg); + void OnFinished(); + uptr stack_begin() { return stack_begin_; } + uptr stack_end() { return stack_end_; } + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } + uptr cache_begin() { return cache_begin_; } + uptr cache_end() { return cache_end_; } + private: + uptr stack_begin_, stack_end_, + cache_begin_, cache_end_, + tls_begin_, tls_end_; +}; + +void InitializeThreadRegistry(); + +void ThreadStart(u32 tid, uptr os_id); +void ThreadFinish(); +u32 ThreadCreate(u32 tid, uptr uid, bool detached); +void ThreadJoin(u32 tid); +u32 ThreadTid(uptr uid); + +u32 GetCurrentThread(); +void SetCurrentThread(u32 tid); +ThreadContext *CurrentThreadContext(); +void EnsureMainThreadIDIsCorrect(); +} // namespace __lsan + +#endif // LSAN_THREAD_H diff --git a/projects/compiler-rt/lib/lsan/tests/CMakeLists.txt b/projects/compiler-rt/lib/lsan/tests/CMakeLists.txt new file mode 100644 index 00000000..2221e065 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/tests/CMakeLists.txt @@ -0,0 +1,58 @@ +include(CheckCXXCompilerFlag) +include(CompilerRTCompile) +include(CompilerRTLink) + +include_directories(..) +include_directories(../..) + +set(LSAN_TESTS_SRC + lsan_dummy_unittest.cc) + +set(LSAN_TESTS_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${LSAN_SRC_DIR}) + +set(LSAN_TEST_LINK_FLAGS_COMMON + -lstdc++ -ldl -lpthread -lm) + +add_custom_target(LsanUnitTests) +set_target_properties(LsanUnitTests PROPERTIES + FOLDER "LSan unit tests") + +# Compile source for the given architecture, using compiler +# options in ${ARGN}, and add it to the object list. +macro(lsan_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + clang_compile(${output_obj} ${source} + CFLAGS ${ARGN} ${TARGET_CFLAGS} + DEPS gtest ${LSAN_RUNTIME_LIBRARIES}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +function(add_lsan_test test_suite test_name arch) + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(${test_suite} ${test_name} + OBJECTS ${ARGN} + DEPS ${LSAN_RUNTIME_LIBRARIES} ${ARGN} + LINK_FLAGS ${LSAN_TEST_LINK_FLAGS_COMMON} + ${TARGET_LINK_FLAGS}) +endfunction() + +macro(add_lsan_tests_for_arch arch) + set(LSAN_TESTS_OBJ) + set(LSAN_TEST_SOURCES ${LSAN_TESTS_SRC} + ${COMPILER_RT_GTEST_SOURCE}) + foreach(source ${LSAN_TEST_SOURCES}) + lsan_compile(LSAN_TESTS_OBJ ${source} ${arch} ${LSAN_TESTS_CFLAGS}) + endforeach() + add_lsan_test(LsanUnitTests Lsan-${arch}-Test ${arch} ${LSAN_TESTS_OBJ}) +endmacro() + +# Build tests for 64-bit Linux only. +if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64) + add_lsan_tests_for_arch(x86_64) +endif() diff --git a/projects/compiler-rt/lib/lsan/tests/lsan_dummy_unittest.cc b/projects/compiler-rt/lib/lsan/tests/lsan_dummy_unittest.cc new file mode 100644 index 00000000..54684007 --- /dev/null +++ b/projects/compiler-rt/lib/lsan/tests/lsan_dummy_unittest.cc @@ -0,0 +1,22 @@ +//===-- lsan_dummy_unittest.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +TEST(LeakSanitizer, EmptyTest) { + // Empty test to suppress LIT warnings about lack of tests. +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/projects/compiler-rt/lib/lsan/tests/lsan_testlib.cc b/projects/compiler-rt/lib/lsan/tests/lsan_testlib.cc new file mode 100644 index 00000000..8db6cf1e --- /dev/null +++ b/projects/compiler-rt/lib/lsan/tests/lsan_testlib.cc @@ -0,0 +1,25 @@ +//===-- lsan_testlib.cc ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Standalone LSan tool as a shared library, to be used with LD_PRELOAD. +// +//===----------------------------------------------------------------------===// +/* Usage: +clang++ ../sanitizer_common/sanitizer_*.cc ../interception/interception_*.cc \ + lsan*.cc tests/lsan_testlib.cc -I. -I.. -g -ldl -lpthread -fPIC -shared -O2 \ + -DLSAN_USE_PREINIT_ARRAY=0 -o lsan.so +LD_PRELOAD=./lsan.so /your/app +*/ +#include "lsan.h" + +__attribute__((constructor)) +void constructor() { + __lsan_init(); +} diff --git a/projects/compiler-rt/lib/lshrdi3.c b/projects/compiler-rt/lib/lshrdi3.c new file mode 100644 index 00000000..6b1ea923 --- /dev/null +++ b/projects/compiler-rt/lib/lshrdi3.c @@ -0,0 +1,43 @@ +/* ===-- lshrdi3.c - Implement __lshrdi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __lshrdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: logical a >> b */ + +/* Precondition: 0 <= b < bits_in_dword */ + +ARM_EABI_FNALIAS(llsr, lshrdi3) + +COMPILER_RT_ABI di_int +__lshrdi3(di_int a, si_int b) +{ + const int bits_in_word = (int)(sizeof(si_int) * CHAR_BIT); + udwords input; + udwords result; + input.all = a; + if (b & bits_in_word) /* bits_in_word <= b < bits_in_dword */ + { + result.s.high = 0; + result.s.low = input.s.high >> (b - bits_in_word); + } + else /* 0 <= b < bits_in_word */ + { + if (b == 0) + return a; + result.s.high = input.s.high >> b; + result.s.low = (input.s.high << (bits_in_word - b)) | (input.s.low >> b); + } + return result.all; +} diff --git a/projects/compiler-rt/lib/lshrti3.c b/projects/compiler-rt/lib/lshrti3.c new file mode 100644 index 00000000..be768143 --- /dev/null +++ b/projects/compiler-rt/lib/lshrti3.c @@ -0,0 +1,45 @@ +/* ===-- lshrti3.c - Implement __lshrti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __lshrti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: logical a >> b */ + +/* Precondition: 0 <= b < bits_in_tword */ + +ti_int +__lshrti3(ti_int a, si_int b) +{ + const int bits_in_dword = (int)(sizeof(di_int) * CHAR_BIT); + utwords input; + utwords result; + input.all = a; + if (b & bits_in_dword) /* bits_in_dword <= b < bits_in_tword */ + { + result.s.high = 0; + result.s.low = input.s.high >> (b - bits_in_dword); + } + else /* 0 <= b < bits_in_dword */ + { + if (b == 0) + return a; + result.s.high = input.s.high >> b; + result.s.low = (input.s.high << (bits_in_dword - b)) | (input.s.low >> b); + } + return result.all; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/moddi3.c b/projects/compiler-rt/lib/moddi3.c new file mode 100644 index 00000000..2f3b9cc4 --- /dev/null +++ b/projects/compiler-rt/lib/moddi3.c @@ -0,0 +1,32 @@ +/*===-- moddi3.c - Implement __moddi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __moddi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +COMPILER_RT_ABI du_int __udivmoddi4(du_int a, du_int b, du_int* rem); + +/* Returns: a % b */ + +COMPILER_RT_ABI di_int +__moddi3(di_int a, di_int b) +{ + const int bits_in_dword_m1 = (int)(sizeof(di_int) * CHAR_BIT) - 1; + di_int s = b >> bits_in_dword_m1; /* s = b < 0 ? -1 : 0 */ + b = (b ^ s) - s; /* negate if s == -1 */ + s = a >> bits_in_dword_m1; /* s = a < 0 ? -1 : 0 */ + a = (a ^ s) - s; /* negate if s == -1 */ + di_int r; + __udivmoddi4(a, b, (du_int*)&r); + return (r ^ s) - s; /* negate if s == -1 */ +} diff --git a/projects/compiler-rt/lib/modsi3.c b/projects/compiler-rt/lib/modsi3.c new file mode 100644 index 00000000..d16213c4 --- /dev/null +++ b/projects/compiler-rt/lib/modsi3.c @@ -0,0 +1,25 @@ +/* ===-- modsi3.c - Implement __modsi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __modsi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +su_int COMPILER_RT_ABI __divsi3(si_int a, si_int b); + +/* Returns: a % b */ + +COMPILER_RT_ABI si_int +__modsi3(si_int a, si_int b) +{ + return a - __divsi3(a, b) * b; +} diff --git a/projects/compiler-rt/lib/modti3.c b/projects/compiler-rt/lib/modti3.c new file mode 100644 index 00000000..752202d4 --- /dev/null +++ b/projects/compiler-rt/lib/modti3.c @@ -0,0 +1,36 @@ +/* ===-- modti3.c - Implement __modti3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __modti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); + +/*Returns: a % b */ + +ti_int +__modti3(ti_int a, ti_int b) +{ + const int bits_in_tword_m1 = (int)(sizeof(ti_int) * CHAR_BIT) - 1; + ti_int s = b >> bits_in_tword_m1; /* s = b < 0 ? -1 : 0 */ + b = (b ^ s) - s; /* negate if s == -1 */ + s = a >> bits_in_tword_m1; /* s = a < 0 ? -1 : 0 */ + a = (a ^ s) - s; /* negate if s == -1 */ + ti_int r; + __udivmodti4(a, b, (tu_int*)&r); + return (r ^ s) - s; /* negate if s == -1 */ +} + +#endif diff --git a/projects/compiler-rt/lib/msan/CMakeLists.txt b/projects/compiler-rt/lib/msan/CMakeLists.txt new file mode 100644 index 00000000..06f3f65d --- /dev/null +++ b/projects/compiler-rt/lib/msan/CMakeLists.txt @@ -0,0 +1,50 @@ +include_directories(..) + +# Runtime library sources and build flags. +set(MSAN_RTL_SOURCES + msan.cc + msan_allocator.cc + msan_interceptors.cc + msan_linux.cc + msan_new_delete.cc + msan_report.cc + ) +set(MSAN_RTL_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti + -fPIE + # Prevent clang from generating libc calls. + -ffreestanding) + +# Static runtime library. +set(MSAN_RUNTIME_LIBRARIES) +set(arch "x86_64") +if(CAN_TARGET_${arch}) + add_compiler_rt_static_runtime(clang_rt.msan-${arch} ${arch} + SOURCES ${MSAN_RTL_SOURCES} + $ + $ + $ + CFLAGS ${MSAN_RTL_CFLAGS}) + list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}) + if(UNIX) + add_sanitizer_rt_symbols(clang_rt.msan-${arch} msan.syms.extra) + list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}-symbols) + endif() +endif() + +add_compiler_rt_resource_file(msan_blacklist msan_blacklist.txt) + +# We should only build MSan unit tests if we can build instrumented libcxx. +set(MSAN_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx) +if(EXISTS ${MSAN_LIBCXX_PATH}/) + set(MSAN_CAN_INSTRUMENT_LIBCXX TRUE) +else() + set(MSAN_CAN_INSTRUMENT_LIBCXX FALSE) +endif() + +if(LLVM_INCLUDE_TESTS) + add_subdirectory(tests) +endif() + +add_subdirectory(lit_tests) diff --git a/projects/compiler-rt/lib/msan/Makefile.mk b/projects/compiler-rt/lib/msan/Makefile.mk new file mode 100644 index 00000000..99e3b036 --- /dev/null +++ b/projects/compiler-rt/lib/msan/Makefile.mk @@ -0,0 +1,24 @@ +#===- lib/msan/Makefile.mk ---------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := msan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../interception/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the msan functions. +MsanFunctions := $(Sources:%.cc=%) diff --git a/projects/compiler-rt/lib/msan/lit_tests/CMakeLists.txt b/projects/compiler-rt/lib/msan/lit_tests/CMakeLists.txt new file mode 100644 index 00000000..38d1e59e --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/CMakeLists.txt @@ -0,0 +1,31 @@ +set(MSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) +set(MSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +if(MSAN_CAN_INSTRUMENT_LIBCXX) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) +endif() + +if(COMPILER_RT_CAN_EXECUTE_TESTS AND CAN_TARGET_x86_64) + # Run MSan tests only if we're sure we may produce working binaries. + set(MSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + ${MSAN_RUNTIME_LIBRARIES} + msan_blacklist) + set(MSAN_TEST_PARAMS + msan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + if(LLVM_INCLUDE_TESTS AND MSAN_CAN_INSTRUMENT_LIBCXX) + list(APPEND MSAN_TEST_DEPS MsanUnitTests) + endif() + add_lit_testsuite(check-msan "Running the MemorySanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS ${MSAN_TEST_PARAMS} + DEPENDS ${MSAN_TEST_DEPS} + ) + set_target_properties(check-msan PROPERTIES FOLDER "MSan tests") +endif() diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/glob.cc b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob.cc new file mode 100644 index 00000000..387ce3cf --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); + + glob_t globbuf; + int res = glob(buf, 0, 0, &globbuf); + + printf("%d %s\n", errno, strerror(errno)); + assert(res == 0); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_altdirfunc.cc b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_altdirfunc.cc new file mode 100644 index 00000000..b8200c3e --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_altdirfunc.cc @@ -0,0 +1,78 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +static void my_gl_closedir(void *dir) { + if (!dir) + exit(1); + closedir((DIR *)dir); +} + +static struct dirent *my_gl_readdir(void *dir) { + if (!dir) + exit(1); + struct dirent *d = readdir((DIR *)dir); + if (d) __msan_poison(d, d->d_reclen); // hehe + return d; +} + +static void *my_gl_opendir(const char *s) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + return opendir(s); +} + +static int my_gl_lstat(const char *s, struct stat *st) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + if (!st) + exit(1); + return lstat(s, st); +} + +static int my_gl_stat(const char *s, struct stat *st) { + assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); + if (!st) + exit(1); + return lstat(s, st); +} + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); + + glob_t globbuf; + globbuf.gl_closedir = my_gl_closedir; + globbuf.gl_readdir = my_gl_readdir; + globbuf.gl_opendir = my_gl_opendir; + globbuf.gl_lstat = my_gl_lstat; + globbuf.gl_stat = my_gl_stat; + for (int i = 0; i < 10000; ++i) { + int res = glob(buf, GLOB_ALTDIRFUNC | GLOB_MARK, 0, &globbuf); + assert(res == 0); + printf("%d %s\n", errno, strerror(errno)); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + __msan_poison(globbuf.gl_pathv[0], strlen(globbuf.gl_pathv[0]) + 1); + __msan_poison(globbuf.gl_pathv[1], strlen(globbuf.gl_pathv[1]) + 1); + globfree(&globbuf); + } + + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_nomatch.cc b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_nomatch.cc new file mode 100644 index 00000000..0262034a --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_nomatch.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p + +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*c"); + + glob_t globbuf; + int res = glob(buf, 0, 0, &globbuf); + assert(res == GLOB_NOMATCH); + assert(globbuf.gl_pathc == 0); + if (globbuf.gl_pathv == 0) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_test_root/aa b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_test_root/aa new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_test_root/ab b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_test_root/ab new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_test_root/ba b/projects/compiler-rt/lib/msan/lit_tests/Linux/glob_test_root/ba new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/lit.local.cfg b/projects/compiler-rt/lib/msan/lit_tests/Linux/lit.local.cfg new file mode 100644 index 00000000..57271b80 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/syscalls.cc b/projects/compiler-rt/lib/msan/lit_tests/Linux/syscalls.cc new file mode 100644 index 00000000..ec308bfe --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/Linux/syscalls.cc @@ -0,0 +1,100 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t 2>&1 + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general + sanity of their behaviour. */ + +int main(int argc, char *argv[]) { + char buf[1000]; + const int kTen = 10; + const int kFortyTwo = 42; + memset(buf, 0, sizeof(buf)); + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_pre_recvmsg(0, buf, 0); + __sanitizer_syscall_pre_rt_sigpending(buf, kTen); + __sanitizer_syscall_pre_getdents(0, buf, kTen); + __sanitizer_syscall_pre_getdents64(0, buf, kTen); + + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_post_recvmsg(0, 0, buf, 0); + __sanitizer_syscall_post_rt_sigpending(-1, buf, kTen); + __sanitizer_syscall_post_getdents(0, 0, buf, kTen); + __sanitizer_syscall_post_getdents64(0, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == -1); + + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_post_recvmsg(kTen, 0, buf, 0); + + // Tell the kernel that the output struct size is 10 bytes, verify that those + // bytes are unpoisoned, and the next byte is not. + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_rt_sigpending(0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_getdents(kTen, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_getdents64(kTen, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_getres(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + // Failed syscall does not write to the buffer. + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(-1, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == 0); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_read(5, 42, buf, 10); + assert(__msan_test_shadow(buf, sizeof(buf)) == 5); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_newfstatat(0, 5, "/path/to/file", buf, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(struct stat)); + + __msan_poison(buf, sizeof(buf)); + int prio = 0; + __sanitizer_syscall_post_mq_timedreceive(kFortyTwo, 5, buf, sizeof(buf), &prio, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); + assert(__msan_test_shadow(&prio, sizeof(prio)) == -1); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_ptrace(0, PTRACE_PEEKUSER, kFortyTwo, 0xABCD, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(void *)); + + __msan_poison(buf, sizeof(buf)); + struct iocb iocb[2]; + struct iocb *iocbp[2] = { &iocb[0], &iocb[1] }; + memset(iocb, 0, sizeof(iocb)); + iocb[0].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[0].aio_buf = (__u64)buf; + iocb[0].aio_nbytes = kFortyTwo; + iocb[1].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[1].aio_buf = (__u64)(&buf[kFortyTwo]); + iocb[1].aio_nbytes = kFortyTwo; + __sanitizer_syscall_post_io_submit(1, 0, 2, &iocbp); + assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); + + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/Linux/tcgetattr.cc b/projects/compiler-rt/lib/msan/lit_tests/Linux/tcgetattr.cc new file mode 100644 index 00000000..e6e101db --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/Linux/tcgetattr.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p + +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + int fd = getpt(); + assert(fd >= 0); + + struct termios t; + int res = tcgetattr(fd, &t); + assert(!res); + + if (t.c_iflag == 0) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc b/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc new file mode 100644 index 00000000..8930a715 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc @@ -0,0 +1,14 @@ +#include + +#include "dso-origin.h" + +void my_access(int *p) { + volatile int tmp; + // Force initialize-ness check. + if (*p) + tmp = 1; +} + +void *my_alloc(unsigned sz) { + return malloc(sz); +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/dso-origin.h b/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/dso-origin.h new file mode 100644 index 00000000..ff926b3f --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/dso-origin.h @@ -0,0 +1,4 @@ +extern "C" { +void my_access(int *p); +void *my_alloc(unsigned sz); +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/lit.local.cfg b/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/lit.local.cfg new file mode 100644 index 00000000..b3677c17 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/projects/compiler-rt/lib/msan/lit_tests/Unit/lit.site.cfg.in b/projects/compiler-rt/lib/msan/lit_tests/Unit/lit.site.cfg.in new file mode 100644 index 00000000..8e67f557 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/Unit/lit.site.cfg.in @@ -0,0 +1,13 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") + +# Setup config name. +config.name = 'MemorySanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with MSan unit tests. +config.test_exec_root = "@MSAN_BINARY_DIR@/tests" +config.test_source_root = config.test_exec_root diff --git a/projects/compiler-rt/lib/msan/lit_tests/allocator_returns_null.cc b/projects/compiler-rt/lib/msan/lit_tests/allocator_returns_null.cc new file mode 100644 index 00000000..aaa85cce --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/allocator_returns_null.cc @@ -0,0 +1,81 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_msan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL + +#include +#include +#include +#include +#include +#include +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: MemorySanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 diff --git a/projects/compiler-rt/lib/msan/lit_tests/backtrace.cc b/projects/compiler-rt/lib/msan/lit_tests/backtrace.cc new file mode 100644 index 00000000..48684c29 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/backtrace.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include +#include +#include +#include + +__attribute__((noinline)) +void f() { + void *buf[10]; + int sz = backtrace(buf, sizeof(buf) / sizeof(*buf)); + assert(sz > 0); + for (int i = 0; i < sz; ++i) + if (!buf[i]) + exit(1); + char **s = backtrace_symbols(buf, sz); + assert(s > 0); + for (int i = 0; i < sz; ++i) + printf("%d\n", strlen(s[i])); +} + +int main(void) { + f(); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/c-strdup.c b/projects/compiler-rt/lib/msan/lit_tests/c-strdup.c new file mode 100644 index 00000000..7772f0f3 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/c-strdup.c @@ -0,0 +1,17 @@ +// RUN: %clang_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O1 %s -o %t && %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O2 %s -o %t && %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O3 %s -o %t && %t >%t.out 2>&1 + +// Test that strdup in C programs is intercepted. +// GLibC headers translate strdup to __strdup at -O1 and higher. + +#include +#include +int main(int argc, char **argv) { + char buf[] = "abc"; + char *p = strdup(buf); + if (*p) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/cxa_atexit.cc b/projects/compiler-rt/lib/msan/lit_tests/cxa_atexit.cc new file mode 100644 index 00000000..f3641aad --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/cxa_atexit.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p + +// PR17377: C++ module destructors get stale argument shadow. + +#include +#include +class A { +public: + // This destructor get stale argument shadow left from the call to f(). + ~A() { + if (this) + exit(0); + } +}; + +A a; + +__attribute__((noinline)) +void f(long x) { +} + +int main(void) { + long x; + long * volatile p = &x; + // This call poisons TLS shadow for the first function argument. + f(*p); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/default_blacklist.cc b/projects/compiler-rt/lib/msan/lit_tests/default_blacklist.cc new file mode 100644 index 00000000..32cc0225 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/default_blacklist.cc @@ -0,0 +1,3 @@ +// Test that MSan uses the default blacklist from resource directory. +// RUN: %clangxx_msan -### %s 2>&1 | FileCheck %s +// CHECK: fsanitize-blacklist={{.*}}msan_blacklist.txt diff --git a/projects/compiler-rt/lib/msan/lit_tests/dlerror.cc b/projects/compiler-rt/lib/msan/lit_tests/dlerror.cc new file mode 100644 index 00000000..281b3164 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/dlerror.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include +#include +#include + +int main(void) { + void *p = dlopen("/bad/file/name", RTLD_NOW); + assert(!p); + char *s = dlerror(); + printf("%s, %zu\n", s, strlen(s)); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/dso-origin.cc b/projects/compiler-rt/lib/msan/lit_tests/dso-origin.cc new file mode 100644 index 00000000..13661c65 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/dso-origin.cc @@ -0,0 +1,25 @@ +// Build a library with origin tracking and an executable w/o origin tracking. +// Test that origin tracking is enabled at runtime. +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %p/SharedLibs/dso-origin-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_msan -m64 -O0 %s %t-so.so -o %t && not %t 2>&1 | FileCheck %s + +#include + +#include "SharedLibs/dso-origin.h" + +int main(int argc, char **argv) { + int *x = (int *)my_alloc(sizeof(int)); + my_access(x); + delete x; + + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in my_access .*dso-origin-so.cc:}} + // CHECK: {{#1 0x.* in main .*dso-origin.cc:}}[[@LINE-5]] + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{#0 0x.* in .*malloc}} + // CHECK: {{#1 0x.* in my_alloc .*dso-origin-so.cc:}} + // CHECK: {{#2 0x.* in main .*dso-origin.cc:}}[[@LINE-10]] + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*dso-origin-so.cc:.* my_access}} + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/errno.cc b/projects/compiler-rt/lib/msan/lit_tests/errno.cc new file mode 100644 index 00000000..af27ad0b --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/errno.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include +#include +#include + +int main() +{ + int x; + int *volatile p = &x; + errno = *p; + int res = read(-1, 0, 0); + assert(res == -1); + if (errno) printf("errno %d\n", errno); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/getaddrinfo-positive.cc b/projects/compiler-rt/lib/msan/lit_tests/getaddrinfo-positive.cc new file mode 100644 index 00000000..7fde1fdf --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/getaddrinfo-positive.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include +#include +#include +#include + +volatile int z; + +int main(void) { + struct addrinfo *ai; + struct addrinfo hint; + int res = getaddrinfo("localhost", NULL, NULL, &ai); + if (ai) z = 1; // OK + res = getaddrinfo("localhost", NULL, &hint, &ai); + // CHECK: UMR in __interceptor_getaddrinfo at offset 0 inside + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main {{.*}}getaddrinfo-positive.cc:[[@LINE-3]] + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/getaddrinfo.cc b/projects/compiler-rt/lib/msan/lit_tests/getaddrinfo.cc new file mode 100644 index 00000000..0518cf47 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/getaddrinfo.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include +#include +#include + +void poison_stack_ahead() { + char buf[100000]; + // With -O0 this poisons a large chunk of stack. +} + +int main(void) { + poison_stack_ahead(); + + struct addrinfo *ai; + + // This should trigger loading of libnss_dns and friends. + // Those libraries are typically uninstrumented.They will call strlen() on a + // stack-allocated buffer, which is very likely to be poisoned. Test that we + // don't report this as an UMR. + int res = getaddrinfo("not-in-etc-hosts", NULL, NULL, &ai); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/getline.cc b/projects/compiler-rt/lib/msan/lit_tests/getline.cc new file mode 100644 index 00000000..27168a88 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/getline.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_msan -O0 %s -o %t && %t %p + +#include +#include +#include +#include + +int main(int argc, char **argv) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "getline_test_data"); + + FILE *fp = fopen(buf, "r"); + assert(fp); + + char *line = 0; + size_t len = 0; + int n = getline(&line, &len, fp); + assert(n == 6); + assert(strcmp(line, "abcde\n") == 0); + + n = getline(&line, &len, fp); + assert(n == 6); + assert(strcmp(line, "12345\n") == 0); + + free(line); + fclose(fp); + + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/getline_test_data b/projects/compiler-rt/lib/msan/lit_tests/getline_test_data new file mode 100644 index 00000000..5ba1d4ce --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/getline_test_data @@ -0,0 +1,2 @@ +abcde +12345 diff --git a/projects/compiler-rt/lib/msan/lit_tests/heap-origin.cc b/projects/compiler-rt/lib/msan/lit_tests/heap-origin.cc new file mode 100644 index 00000000..dfe7edd2 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/heap-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + return *x; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*heap-origin.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by a heap allocation + // CHECK-ORIGINS: {{#0 0x.* in .*malloc}} + // CHECK-ORIGINS: {{#1 0x.* in main .*heap-origin.cc:}}[[@LINE-7]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*heap-origin.cc:.* main}} +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/initgroups.cc b/projects/compiler-rt/lib/msan/lit_tests/initgroups.cc new file mode 100644 index 00000000..adba5369 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/initgroups.cc @@ -0,0 +1,11 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include + +int main(void) { + initgroups("root", 0); + // The above fails unless you are root. Does not matter, MSan false positive + // (which we are testing for) happens anyway. + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/inline.cc b/projects/compiler-rt/lib/msan/lit_tests/inline.cc new file mode 100644 index 00000000..4aeb1558 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/inline.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -O3 %s -o %t && %t + +// Test that no_sanitize_memory attribute applies even when the function would +// be normally inlined. + +#include + +__attribute__((no_sanitize_memory)) +int f(int *p) { + if (*p) // BOOOM?? Nope! + exit(0); + return 0; +} + +int main(int argc, char **argv) { + int x; + int * volatile p = &x; + int res = f(p); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/insertvalue_origin.cc b/projects/compiler-rt/lib/msan/lit_tests/insertvalue_origin.cc new file mode 100644 index 00000000..769ea45f --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/insertvalue_origin.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +// Test origin propagation through insertvalue IR instruction. + +#include +#include + +struct mypair { + int64_t x; + int y; +}; + +mypair my_make_pair(int64_t x, int y) { + mypair p; + p.x = x; + p.y = y; + return p; +} + +int main() { + int64_t * volatile p = new int64_t; + mypair z = my_make_pair(*p, 0); + if (z.x) + printf("zzz\n"); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-3]] + + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-8]] + delete p; + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/ioctl.cc b/projects/compiler-rt/lib/msan/lit_tests/ioctl.cc new file mode 100644 index 00000000..caff80c2 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/ioctl.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + unsigned int z; + int res = ioctl(fd, FIOGETOWN, &z); + assert(res == 0); + close(fd); + if (z) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/ioctl_custom.cc b/projects/compiler-rt/lib/msan/lit_tests/ioctl_custom.cc new file mode 100644 index 00000000..94ed528c --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/ioctl_custom.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t + +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 -g %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -DPOSITIVE -m64 -O3 -g %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + + struct ifreq ifreqs[20]; + struct ifconf ifc; + ifc.ifc_ifcu.ifcu_req = ifreqs; +#ifndef POSITIVE + ifc.ifc_len = sizeof(ifreqs); +#endif + int res = ioctl(fd, SIOCGIFCONF, (void *)&ifc); + // CHECK: UMR in ioctl{{.*}} at offset 0 + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #{{.*}} in main {{.*}}ioctl_custom.cc:[[@LINE-3]] + assert(res == 0); + for (int i = 0; i < ifc.ifc_len / sizeof(*ifc.ifc_ifcu.ifcu_req); ++i) + printf("%d %zu %s\n", i, strlen(ifreqs[i].ifr_name), ifreqs[i].ifr_name); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/keep-going-dso.cc b/projects/compiler-rt/lib/msan/lit_tests/keep-going-dso.cc new file mode 100644 index 00000000..6d006756 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/keep-going-dso.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// Test how -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going affect reports +// from interceptors. +// -mllvm -msan-keep-going provides the default value of keep_going flag, but is +// always overwritten by MSAN_OPTIONS + +#include +#include +#include + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + x[4] = 0; + if (strlen(x) < 3) + exit(0); + fprintf(stderr, "Done\n"); + // CHECK-NOT: Done + // CHECK-KEEP-GOING: Done + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/keep-going.cc b/projects/compiler-rt/lib/msan/lit_tests/keep-going.cc new file mode 100644 index 00000000..e33b137c --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/keep-going.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck %s <%t.out + +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=1 not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=0 not %t >%t.out 2>&1 +// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out + +// Test behaviour of -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going. +// -mllvm -msan-keep-going provides the default value of keep_going flag; value +// of 1 can be overwritten by MSAN_OPTIONS, value of 0 can not. + +#include +#include + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + if (x[0]) + exit(0); + fprintf(stderr, "Done\n"); + // CHECK-NOT: Done + // CHECK-KEEP-GOING: Done + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/lit.cfg b/projects/compiler-rt/lib/msan/lit_tests/lit.cfg new file mode 100644 index 00000000..da1bde6d --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/lit.cfg @@ -0,0 +1,74 @@ +# -*- Python -*- + +import os + +import lit.util + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'MemorySanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +def DisplayNoConfigMessage(): + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-msan") + +# Figure out LLVM source root. +llvm_src_root = getattr(config, 'llvm_src_root', None) +if llvm_src_root is None: + # We probably haven't loaded the site-specific configuration: the user + # is likely trying to run a test file directly, and the site configuration + # wasn't created by the build system. + msan_site_cfg = lit_config.params.get('msan_site_config', None) + if (msan_site_cfg) and (os.path.exists(msan_site_cfg)): + lit_config.load_config(config, msan_site_cfg) + raise SystemExit + + # Try to guess the location of site-specific configuration using llvm-config + # util that can point where the build tree is. + llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) + if not llvm_config: + DisplayNoConfigMessage() + + # Find out the presumed location of generated site config. + llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() + msan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", + "lib", "msan", "lit_tests", "lit.site.cfg") + if (not msan_site_cfg) or (not os.path.exists(msan_site_cfg)): + DisplayNoConfigMessage() + + lit_config.load_config(config, msan_site_cfg) + raise SystemExit + +# Setup default compiler flags used with -fsanitize=memory option. +clang_msan_cflags = ["-fsanitize=memory", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls", + "-g", + "-m64"] +clang_msan_cxxflags = ["--driver-mode=g++ "] + clang_msan_cflags +config.substitutions.append( ("%clang_msan ", + " ".join([config.clang] + clang_msan_cflags) + + " ") ) +config.substitutions.append( ("%clangxx_msan ", + " ".join([config.clang] + clang_msan_cxxflags) + + " ") ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# MemorySanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/msan/lit_tests/lit.site.cfg.in b/projects/compiler-rt/lib/msan/lit_tests/lit.site.cfg.in new file mode 100644 index 00000000..946df778 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/lit.site.cfg.in @@ -0,0 +1,5 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@MSAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/projects/compiler-rt/lib/msan/lit_tests/malloc_hook.cc b/projects/compiler-rt/lib/msan/lit_tests/malloc_hook.cc new file mode 100644 index 00000000..fc68fbc3 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/malloc_hook.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_msan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include +#include + +extern "C" { +int __msan_get_ownership(const void *p); + +void *global_ptr; + +// Note: avoid calling functions that allocate memory in malloc/free +// to avoid infinite recursion. +void __msan_malloc_hook(void *ptr, size_t sz) { + if (__msan_get_ownership(ptr)) { + write(1, "MallocHook\n", sizeof("MallocHook\n")); + global_ptr = ptr; + } +} +void __msan_free_hook(void *ptr) { + if (__msan_get_ownership(ptr) && ptr == global_ptr) + write(1, "FreeHook\n", sizeof("FreeHook\n")); +} +} // extern "C" + +int main() { + volatile int *x = new int; + // CHECK: MallocHook + // Check that malloc hook was called with correct argument. + if (global_ptr != (void*)x) { + _exit(1); + } + *x = 0; + delete x; + // CHECK: FreeHook + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/no_sanitize_memory.cc b/projects/compiler-rt/lib/msan/lit_tests/no_sanitize_memory.cc new file mode 100644 index 00000000..48afc17e --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/no_sanitize_memory.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t >%t.out 2>&1 + +// RUN: %clangxx_msan -m64 -O0 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 + +// Test that (no_sanitize_memory) functions +// * don't check shadow values (-DCHECK_IN_F) +// * treat all values loaded from memory as fully initialized (-UCHECK_IN_F) + +#include +#include + +__attribute__((noinline)) +__attribute__((no_sanitize_memory)) +int f(void) { + int x; + int * volatile p = &x; +#ifdef CHECK_IN_F + if (*p) + exit(0); +#endif + return *p; +} + +int main(void) { + if (f()) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/no_sanitize_memory_prop.cc b/projects/compiler-rt/lib/msan/lit_tests/no_sanitize_memory_prop.cc new file mode 100644 index 00000000..35515247 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/no_sanitize_memory_prop.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// Test that (no_sanitize_memory) functions propagate shadow. + +// Note that at -O0 there is no report, because 'x' in 'f' is spilled to the +// stack, and then loaded back as a fully initialiazed value (due to +// no_sanitize_memory attribute). + +#include +#include + +__attribute__((noinline)) +__attribute__((no_sanitize_memory)) +int f(int x) { + return x; +} + +int main(void) { + int x; + int * volatile p = &x; + int y = f(*p); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*no_sanitize_memory_prop.cc:}}[[@LINE+1]] + if (y) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/poison_in_free.cc b/projects/compiler-rt/lib/msan/lit_tests/poison_in_free.cc new file mode 100644 index 00000000..f134d05a --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/poison_in_free.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -O0 %s -o %t && not %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=poison_in_free=0 %t >%t.out 2>&1 + +#include +#include +#include + +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(50 * sizeof(char)); + memset(x, 0, 50); + free(x); + return x[25]; + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main{{.*}}poison_in_free.cc:[[@LINE-2]] +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/ptrace.cc b/projects/compiler-rt/lib/msan/lit_tests/ptrace.cc new file mode 100644 index 00000000..d0e83eab --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/ptrace.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + pid_t pid; + pid = fork(); + if (pid == 0) { // child + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execl("/bin/true", "true", NULL); + } else { + wait(NULL); + user_regs_struct regs; + int res; + res = ptrace(PTRACE_GETREGS, pid, NULL, ®s); + assert(!res); + if (regs.rip) + printf("%zx\n", regs.rip); + + user_fpregs_struct fpregs; + res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + if (fpregs.mxcsr) + printf("%x\n", fpregs.mxcsr); + + ptrace(PTRACE_CONT, pid, NULL, NULL); + wait(NULL); + } + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/readdir64.cc b/projects/compiler-rt/lib/msan/lit_tests/readdir64.cc new file mode 100644 index 00000000..0ec106c7 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/readdir64.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t + +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O1 -D_FILE_OFFSET_BITS=64 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O2 -D_FILE_OFFSET_BITS=64 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 -D_FILE_OFFSET_BITS=64 %s -o %t && %t + +// Test that readdir64 is intercepted as well as readdir. + +#include +#include +#include + + +int main(void) { + DIR *dir = opendir("."); + struct dirent *d = readdir(dir); + if (d->d_name[0]) { + closedir(dir); + exit(0); + } + closedir(dir); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/scandir.cc b/projects/compiler-rt/lib/msan/lit_tests/scandir.cc new file mode 100644 index 00000000..94672e1a --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/scandir.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +static int my_filter(const struct dirent *a) { + assert(__msan_test_shadow(&a, sizeof(a)) == (size_t)-1); + printf("%s\n", a->d_name); + __msan_print_shadow(a, a->d_reclen); + assert(__msan_test_shadow(a, a->d_reclen) == (size_t)-1); + printf("%s\n", a->d_name); + return strlen(a->d_name) == 3 && a->d_name[2] == 'b'; +} + +static int my_compar(const struct dirent **a, const struct dirent **b) { + assert(__msan_test_shadow(a, sizeof(*a)) == (size_t)-1); + assert(__msan_test_shadow(*a, (*a)->d_reclen) == (size_t)-1); + assert(__msan_test_shadow(b, sizeof(*b)) == (size_t)-1); + assert(__msan_test_shadow(*b, (*b)->d_reclen) == (size_t)-1); + if ((*a)->d_name[1] == (*b)->d_name[1]) + return 0; + return ((*a)->d_name[1] < (*b)->d_name[1]) ? 1 : -1; +} + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); + + struct dirent **d; + int res = scandir(buf, &d, my_filter, my_compar); + assert(res == 2); + assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); + for (int i = 0; i < res; ++i) { + assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); + assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); + } + + assert(strcmp(d[0]->d_name, "bbb") == 0); + assert(strcmp(d[1]->d_name, "aab") == 0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/scandir_null.cc b/projects/compiler-rt/lib/msan/lit_tests/scandir_null.cc new file mode 100644 index 00000000..84af7f41 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/scandir_null.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); + + struct dirent **d; + int res = scandir(buf, &d, NULL, NULL); + assert(res >= 3); + assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); + for (int i = 0; i < res; ++i) { + assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); + assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); + } + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/scandir_test_root/aaa b/projects/compiler-rt/lib/msan/lit_tests/scandir_test_root/aaa new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/msan/lit_tests/scandir_test_root/aab b/projects/compiler-rt/lib/msan/lit_tests/scandir_test_root/aab new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/msan/lit_tests/scandir_test_root/bbb b/projects/compiler-rt/lib/msan/lit_tests/scandir_test_root/bbb new file mode 100644 index 00000000..e69de29b diff --git a/projects/compiler-rt/lib/msan/lit_tests/select.cc b/projects/compiler-rt/lib/msan/lit_tests/select.cc new file mode 100644 index 00000000..a169a2dd --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/select.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + int z = *p ? 1 : 0; + if (z) + exit(0); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*select.cc:}}[[@LINE-3]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*select.cc:.* main}} + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/setlocale.cc b/projects/compiler-rt/lib/msan/lit_tests/setlocale.cc new file mode 100644 index 00000000..a22b744d --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/setlocale.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include +#include + +int main(void) { + char *locale = setlocale (LC_ALL, ""); + assert(locale); + if (locale[0]) + exit(0); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/signal_stress_test.cc b/projects/compiler-rt/lib/msan/lit_tests/signal_stress_test.cc new file mode 100644 index 00000000..ea75eae1 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/signal_stress_test.cc @@ -0,0 +1,71 @@ +// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %t + +// Test that va_arg shadow from a signal handler does not leak outside. + +#include +#include +#include +#include +#include +#include + +const int kSigCnt = 200; + +void f(bool poisoned, int n, ...) { + va_list vl; + va_start(vl, n); + for (int i = 0; i < n; ++i) { + void *p = va_arg(vl, void *); + if (!poisoned) + assert(__msan_test_shadow(&p, sizeof(p)) == -1); + } + va_end(vl); +} + +int sigcnt; + +void SignalHandler(int signo) { + assert(signo == SIGPROF); + void *p; + void **volatile q = &p; + f(true, 10, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q); + ++sigcnt; +} + +int main() { + signal(SIGPROF, SignalHandler); + + itimerval itv; + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 100; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 100; + setitimer(ITIMER_PROF, &itv, NULL); + + void *p; + void **volatile q = &p; + + do { + f(false, 20, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + f(true, 20, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q, + *q, *q, *q, *q, *q); + } while (sigcnt < kSigCnt); + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 0; + setitimer(ITIMER_PROF, &itv, NULL); + + signal(SIGPROF, SIG_DFL); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/sigwait.cc b/projects/compiler-rt/lib/msan/lit_tests/sigwait.cc new file mode 100644 index 00000000..29aa86c9 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/sigwait.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %t + +#include +#include +#include +#include +#include + +void test_sigwait() { + sigset_t s; + sigemptyset(&s); + sigaddset(&s, SIGUSR1); + sigprocmask(SIG_BLOCK, &s, 0); + + if (pid_t pid = fork()) { + kill(pid, SIGUSR1); + _exit(0); + } else { + int sig; + int res = sigwait(&s, &sig); + assert(!res); + // The following checks that sig is initialized. + assert(sig == SIGUSR1); + } +} + +int main(void) { + test_sigwait(); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/sigwaitinfo.cc b/projects/compiler-rt/lib/msan/lit_tests/sigwaitinfo.cc new file mode 100644 index 00000000..d4f00459 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/sigwaitinfo.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %t + +#include +#include +#include +#include +#include + +void test_sigwaitinfo() { + sigset_t s; + sigemptyset(&s); + sigaddset(&s, SIGUSR1); + sigprocmask(SIG_BLOCK, &s, 0); + + if (pid_t pid = fork()) { + kill(pid, SIGUSR1); + _exit(0); + } else { + siginfo_t info; + int res = sigwaitinfo(&s, &info); + assert(!res); + // The following checks that sig is initialized. + assert(info.si_signo == SIGUSR1); + assert(-1 == __msan_test_shadow(&info, sizeof(info))); + } +} + +int main(void) { + test_sigwaitinfo(); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/stack-origin.cc b/projects/compiler-rt/lib/msan/lit_tests/stack-origin.cc new file mode 100644 index 00000000..b0b05d96 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/stack-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + return *p; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + // CHECK-ORIGINS: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-8]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*stack-origin.cc:.* main}} +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/sync_lock_set_and_test.cc b/projects/compiler-rt/lib/msan/lit_tests/sync_lock_set_and_test.cc new file mode 100644 index 00000000..1023b3e5 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/sync_lock_set_and_test.cc @@ -0,0 +1,7 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +int main(void) { + int i; + __sync_lock_test_and_set(&i, 0); + return i; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/tzset.cc b/projects/compiler-rt/lib/msan/lit_tests/tzset.cc new file mode 100644 index 00000000..7e1c2cfa --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/tzset.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include +#include +#include + +extern char *tzname[2]; + +int main(void) { + if (!strlen(tzname[0]) || !strlen(tzname[1])) + exit(1); + tzset(); + if (!strlen(tzname[0]) || !strlen(tzname[1])) + exit(1); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/unaligned_read_origin.cc b/projects/compiler-rt/lib/msan/lit_tests/unaligned_read_origin.cc new file mode 100644 index 00000000..fa29ab69 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/unaligned_read_origin.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +#include + +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + return __sanitizer_unaligned_load32(p); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-2]] + // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-7]] +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/use-after-free.cc b/projects/compiler-rt/lib/msan/lit_tests/use-after-free.cc new file mode 100644 index 00000000..ac47c023 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/use-after-free.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + *p = 42; + free(p); + + if (*p) + exit(0); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*use-after-free.cc:}}[[@LINE-3]] + + // CHECK-ORIGINS: Uninitialized value was created by a heap allocation + // CHECK-ORIGINS: {{#0 0x.* in .*free}} + // CHECK-ORIGINS: {{#1 0x.* in main .*use-after-free.cc:}}[[@LINE-9]] + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/vector_cvt.cc b/projects/compiler-rt/lib/msan/lit_tests/vector_cvt.cc new file mode 100644 index 00000000..c200c77d --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/vector_cvt.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include + +int to_int(double v) { + __m128d t = _mm_set_sd(v); + int x = _mm_cvtsd_si32(t); + return x; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #{{.*}} in to_int{{.*}}vector_cvt.cc:[[@LINE-4]] +} + +int main() { +#ifdef POSITIVE + double v; +#else + double v = 1.1; +#endif + double* volatile p = &v; + int x = to_int(*p); + return !x; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/vector_select.cc b/projects/compiler-rt/lib/msan/lit_tests/vector_select.cc new file mode 100644 index 00000000..e8d55423 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/vector_select.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_msan -m64 -O0 %s -c -o %t +// RUN: %clangxx_msan -m64 -O3 %s -c -o %t + +// Regression test for MemorySanitizer instrumentation of a select instruction +// with vector arguments. + +#include + +__m128d select(bool b, __m128d c, __m128d d) +{ + return b ? c : d; +} + diff --git a/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls.cc b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls.cc new file mode 100644 index 00000000..b4bac1ec --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls.cc @@ -0,0 +1,64 @@ +// Test indirect call wrapping in MemorySanitizer. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/two.cc -fPIC -shared -o %t-two-so.so +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/wrapper.cc -fPIC -shared -o %t-wrapper-so.so + +// Disable fast path. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=0 \ +// RUN: -DSLOW=1 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t +// RUN: %t + +// Enable fast path, call from executable, -O0. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t +// RUN: %t + +// Enable fast path, call from executable, -O3. + +// RUN: %clangxx_msan -O3 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t +// RUN: %t + +// Enable fast path, call from DSO, -O0. + +// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc -shared \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t-caller-so.so +// RUN: %clangxx_msan -O0 %s %t-caller-so.so %t-two-so.so %t-wrapper-so.so -o %t +// RUN: %t + +// Enable fast path, call from DSO, -O3. + +// RUN: %clangxx_msan -O3 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc -shared \ +// RUN: %t-two-so.so %t-wrapper-so.so \ +// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ +// RUN: -DSLOW=0 \ +// RUN: -Wl,--defsym=__executable_start=0 -o %t-caller-so.so +// RUN: %clangxx_msan -O3 %s %t-caller-so.so %t-two-so.so %t-wrapper-so.so -o %t +// RUN: %t + +// The actual test is in multiple files in wrap_indirect_calls/ directory. +void run_test(); + +int main() { + run_test(); + return 0; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/caller.cc b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/caller.cc new file mode 100644 index 00000000..a0af8b7b --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/caller.cc @@ -0,0 +1,51 @@ +// Indirectly call a bunch of functions. + +#include + +extern int cnt; + +typedef int (*F)(int, int); + +// A function in the same object. +int f_local(int x, int y) { + return x + y; +} + +// A function in another object. +int f_other_object(int x, int y); + +// A function in another DSO. +int f_dso(int x, int y); + +// A function in another DSO that is replaced by the wrapper. +int f_replaced(int x, int y); + +void run_test(void) { + int x; + int expected_cnt = 0; + volatile F f; + + if (SLOW) ++expected_cnt; + f = &f_local; + x = f(1, 2); + assert(x == 3); + assert(cnt == expected_cnt); + + if (SLOW) ++expected_cnt; + f = &f_other_object; + x = f(2, 3); + assert(x == 6); + assert(cnt == expected_cnt); + + ++expected_cnt; + f = &f_dso; + x = f(2, 3); + assert(x == 7); + assert(cnt == expected_cnt); + + ++expected_cnt; + f = &f_replaced; + x = f(2, 3); + assert(x == 11); + assert(cnt == expected_cnt); +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg new file mode 100644 index 00000000..5e01230c --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg @@ -0,0 +1,3 @@ +# Sources in this directory are used by tests in parent directory. + +config.suffixes = [] diff --git a/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/one.cc b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/one.cc new file mode 100644 index 00000000..ab7bf412 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/one.cc @@ -0,0 +1,3 @@ +int f_other_object(int x, int y) { + return x * y; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/two.cc b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/two.cc new file mode 100644 index 00000000..c939a993 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/two.cc @@ -0,0 +1,11 @@ +int f_dso(int x, int y) { + return 2 * x + y; +} + +int f_replaced(int x, int y) { + return x + y + 5; +} + +int f_replacement(int x, int y) { + return x + y + 6; +} diff --git a/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc new file mode 100644 index 00000000..8fcd0c63 --- /dev/null +++ b/projects/compiler-rt/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc @@ -0,0 +1,11 @@ +int f_replaced(int x, int y); +int f_replacement(int x, int y); + +int cnt; + +extern "C" void *wrapper(void *p) { + ++cnt; + if (p == (void *)f_replaced) + return (void *)f_replacement; + return p; +} diff --git a/projects/compiler-rt/lib/msan/msan.cc b/projects/compiler-rt/lib/msan/msan.cc new file mode 100644 index 00000000..83b11e5c --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan.cc @@ -0,0 +1,564 @@ +//===-- msan.cc -----------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer runtime. +//===----------------------------------------------------------------------===// + +#include "msan.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +#include "interception/interception.h" + +// ACHTUNG! No system header includes in this file. + +using namespace __sanitizer; + +// Globals. +static THREADLOCAL int msan_expect_umr = 0; +static THREADLOCAL int msan_expected_umr_found = 0; + +static int msan_running_under_dr = 0; + +SANITIZER_INTERFACE_ATTRIBUTE +THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSizeInWords]; + +SANITIZER_INTERFACE_ATTRIBUTE +THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSizeInWords]; + +SANITIZER_INTERFACE_ATTRIBUTE +THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSizeInWords]; + +SANITIZER_INTERFACE_ATTRIBUTE +THREADLOCAL u32 __msan_retval_origin_tls; + +SANITIZER_INTERFACE_ATTRIBUTE +THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSizeInWords]; + +SANITIZER_INTERFACE_ATTRIBUTE +THREADLOCAL u64 __msan_va_arg_overflow_size_tls; + +SANITIZER_INTERFACE_ATTRIBUTE +THREADLOCAL u32 __msan_origin_tls; + +static THREADLOCAL struct { + uptr stack_top, stack_bottom; +} __msan_stack_bounds; + +static THREADLOCAL int is_in_symbolizer; +static THREADLOCAL int is_in_loader; + +extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_track_origins; + +int __msan_get_track_origins() { + return &__msan_track_origins ? __msan_track_origins : 0; +} + +extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_keep_going; + +namespace __msan { + +static bool IsRunningUnderDr() { + bool result = false; + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + const sptr kBufSize = 4095; + char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__); + while (proc_maps.Next(/* start */0, /* end */0, /* file_offset */0, + filename, kBufSize, /* protection */0)) { + if (internal_strstr(filename, "libdynamorio") != 0) { + result = true; + break; + } + } + UnmapOrDie(filename, kBufSize); + return result; +} + +void EnterSymbolizer() { ++is_in_symbolizer; } +void ExitSymbolizer() { --is_in_symbolizer; } +bool IsInSymbolizer() { return is_in_symbolizer; } + +void EnterLoader() { ++is_in_loader; } +void ExitLoader() { --is_in_loader; } + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +bool __msan_is_in_loader() { return is_in_loader; } +} + +static Flags msan_flags; + +Flags *flags() { + return &msan_flags; +} + +int msan_inited = 0; +bool msan_init_is_running; + +int msan_report_count = 0; + +// Array of stack origins. +// FIXME: make it resizable. +static const uptr kNumStackOriginDescrs = 1024 * 1024; +static const char *StackOriginDescr[kNumStackOriginDescrs]; +static uptr StackOriginPC[kNumStackOriginDescrs]; +static atomic_uint32_t NumStackOriginDescrs; + +static void ParseFlagsFromString(Flags *f, const char *str) { + ParseCommonFlagsFromString(str); + ParseFlag(str, &f->poison_heap_with_zeroes, "poison_heap_with_zeroes"); + ParseFlag(str, &f->poison_stack_with_zeroes, "poison_stack_with_zeroes"); + ParseFlag(str, &f->poison_in_malloc, "poison_in_malloc"); + ParseFlag(str, &f->poison_in_free, "poison_in_free"); + ParseFlag(str, &f->exit_code, "exit_code"); + if (f->exit_code < 0 || f->exit_code > 127) { + Printf("Exit code not in [0, 128) range: %d\n", f->exit_code); + Die(); + } + ParseFlag(str, &f->report_umrs, "report_umrs"); + ParseFlag(str, &f->wrap_signals, "wrap_signals"); + + // keep_going is an old name for halt_on_error, + // and it has inverse meaning. + f->halt_on_error = !f->halt_on_error; + ParseFlag(str, &f->halt_on_error, "keep_going"); + f->halt_on_error = !f->halt_on_error; + ParseFlag(str, &f->halt_on_error, "halt_on_error"); +} + +static void InitializeFlags(Flags *f, const char *options) { + CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); + cf->external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); + cf->malloc_context_size = 20; + cf->handle_ioctl = true; + + internal_memset(f, 0, sizeof(*f)); + f->poison_heap_with_zeroes = false; + f->poison_stack_with_zeroes = false; + f->poison_in_malloc = true; + f->poison_in_free = true; + f->exit_code = 77; + f->report_umrs = true; + f->wrap_signals = true; + f->halt_on_error = !&__msan_keep_going; + + // Override from user-specified string. + if (__msan_default_options) + ParseFlagsFromString(f, __msan_default_options()); + ParseFlagsFromString(f, options); +} + +static void GetCurrentStackBounds(uptr *stack_top, uptr *stack_bottom) { + if (__msan_stack_bounds.stack_top == 0) { + // Break recursion (GetStackTrace -> GetThreadStackTopAndBottom -> + // realloc -> GetStackTrace). + __msan_stack_bounds.stack_top = __msan_stack_bounds.stack_bottom = 1; + GetThreadStackTopAndBottom(/* at_initialization */false, + &__msan_stack_bounds.stack_top, + &__msan_stack_bounds.stack_bottom); + } + *stack_top = __msan_stack_bounds.stack_top; + *stack_bottom = __msan_stack_bounds.stack_bottom; +} + +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, + bool request_fast_unwind) { + if (!StackTrace::WillUseFastUnwind(request_fast_unwind)) { + // Block reports from our interceptors during _Unwind_Backtrace. + SymbolizerScope sym_scope; + return stack->Unwind(max_s, pc, bp, 0, 0, request_fast_unwind); + } + uptr stack_top, stack_bottom; + GetCurrentStackBounds(&stack_top, &stack_bottom); + stack->Unwind(max_s, pc, bp, stack_top, stack_bottom, request_fast_unwind); +} + +void PrintWarning(uptr pc, uptr bp) { + PrintWarningWithOrigin(pc, bp, __msan_origin_tls); +} + +bool OriginIsValid(u32 origin) { + return origin != 0 && origin != (u32)-1; +} + +void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin) { + if (msan_expect_umr) { + // Printf("Expected UMR\n"); + __msan_origin_tls = origin; + msan_expected_umr_found = 1; + return; + } + + ++msan_report_count; + + StackTrace stack; + GetStackTrace(&stack, kStackTraceMax, pc, bp, + common_flags()->fast_unwind_on_fatal); + + u32 report_origin = + (__msan_get_track_origins() && OriginIsValid(origin)) ? origin : 0; + ReportUMR(&stack, report_origin); + + if (__msan_get_track_origins() && !OriginIsValid(origin)) { + Printf( + " ORIGIN: invalid (%x). Might be a bug in MemorySanitizer origin " + "tracking.\n This could still be a bug in your code, too!\n", + origin); + } +} + +void UnpoisonParam(uptr n) { + internal_memset(__msan_param_tls, 0, n * sizeof(*__msan_param_tls)); +} + +// Backup MSan runtime TLS state. +// Implementation must be async-signal-safe. +// Instances of this class may live on the signal handler stack, and data size +// may be an issue. +void ScopedThreadLocalStateBackup::Backup() { + va_arg_overflow_size_tls = __msan_va_arg_overflow_size_tls; +} + +void ScopedThreadLocalStateBackup::Restore() { + // A lame implementation that only keeps essential state and resets the rest. + __msan_va_arg_overflow_size_tls = va_arg_overflow_size_tls; + + internal_memset(__msan_param_tls, 0, sizeof(__msan_param_tls)); + internal_memset(__msan_retval_tls, 0, sizeof(__msan_retval_tls)); + internal_memset(__msan_va_arg_tls, 0, sizeof(__msan_va_arg_tls)); + + if (__msan_get_track_origins()) { + internal_memset(&__msan_retval_origin_tls, 0, sizeof(__msan_retval_tls)); + internal_memset(__msan_param_origin_tls, 0, + sizeof(__msan_param_origin_tls)); + } +} + +void UnpoisonThreadLocalState() { +} + +const char *GetOriginDescrIfStack(u32 id, uptr *pc) { + if ((id >> 31) == 0) return 0; + id &= (1U << 31) - 1; + CHECK_LT(id, kNumStackOriginDescrs); + if (pc) *pc = StackOriginPC[id]; + return StackOriginDescr[id]; +} + +} // namespace __msan + +// Interface. + +using namespace __msan; + +void __msan_warning() { + GET_CALLER_PC_BP_SP; + (void)sp; + PrintWarning(pc, bp); + if (__msan::flags()->halt_on_error) { + Printf("Exiting\n"); + Die(); + } +} + +void __msan_warning_noreturn() { + GET_CALLER_PC_BP_SP; + (void)sp; + PrintWarning(pc, bp); + Printf("Exiting\n"); + Die(); +} + +void __msan_init() { + if (msan_inited) return; + msan_init_is_running = 1; + SanitizerToolName = "MemorySanitizer"; + + SetDieCallback(MsanDie); + InitTlsSize(); + + const char *msan_options = GetEnv("MSAN_OPTIONS"); + InitializeFlags(&msan_flags, msan_options); + __sanitizer_set_report_path(common_flags()->log_path); + + InitializeInterceptors(); + InstallAtExitHandler(); // Needs __cxa_atexit interceptor. + + if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE) + ReplaceOperatorsNewAndDelete(); + if (StackSizeIsUnlimited()) { + if (common_flags()->verbosity) + Printf("Unlimited stack, doing reexec\n"); + // A reasonably large stack size. It is bigger than the usual 8Mb, because, + // well, the program could have been run with unlimited stack for a reason. + SetStackSizeLimitInBytes(32 * 1024 * 1024); + ReExec(); + } + + if (common_flags()->verbosity) + Printf("MSAN_OPTIONS: %s\n", msan_options ? msan_options : ""); + + msan_running_under_dr = IsRunningUnderDr(); + __msan_clear_on_return(); + if (__msan_get_track_origins() && common_flags()->verbosity > 0) + Printf("msan_track_origins\n"); + if (!InitShadow(/* prot1 */ false, /* prot2 */ true, /* map_shadow */ true, + __msan_get_track_origins())) { + // FIXME: prot1 = false is only required when running under DR. + Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n"); + Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n"); + Printf("FATAL: Disabling ASLR is known to cause this error.\n"); + Printf("FATAL: If running under GDB, try " + "'set disable-randomization off'.\n"); + DumpProcessMap(); + Die(); + } + + const char *external_symbolizer = common_flags()->external_symbolizer_path; + bool external_symbolizer_started = + Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); + if (external_symbolizer && external_symbolizer[0]) { + CHECK(external_symbolizer_started); + } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); + + GetThreadStackTopAndBottom(/* at_initialization */true, + &__msan_stack_bounds.stack_top, + &__msan_stack_bounds.stack_bottom); + if (common_flags()->verbosity) + Printf("MemorySanitizer init done\n"); + msan_init_is_running = 0; + msan_inited = 1; +} + +void __msan_set_exit_code(int exit_code) { + flags()->exit_code = exit_code; +} + +void __msan_set_keep_going(int keep_going) { + flags()->halt_on_error = !keep_going; +} + +void __msan_set_expect_umr(int expect_umr) { + if (expect_umr) { + msan_expected_umr_found = 0; + } else if (!msan_expected_umr_found) { + GET_CALLER_PC_BP_SP; + (void)sp; + StackTrace stack; + GetStackTrace(&stack, kStackTraceMax, pc, bp, + common_flags()->fast_unwind_on_fatal); + ReportExpectedUMRNotFound(&stack); + Die(); + } + msan_expect_umr = expect_umr; +} + +void __msan_print_shadow(const void *x, uptr size) { + if (!MEM_IS_APP(x)) { + Printf("Not a valid application address: %p\n", x); + return; + } + unsigned char *s = (unsigned char*)MEM_TO_SHADOW(x); + u32 *o = (u32*)MEM_TO_ORIGIN(x); + for (uptr i = 0; i < size; i++) { + Printf("%x%x ", s[i] >> 4, s[i] & 0xf); + } + Printf("\n"); + if (__msan_get_track_origins()) { + for (uptr i = 0; i < size / 4; i++) { + Printf(" o: %x ", o[i]); + } + Printf("\n"); + } +} + +void __msan_print_param_shadow() { + for (int i = 0; i < 16; i++) { + Printf("#%d:%zx ", i, __msan_param_tls[i]); + } + Printf("\n"); +} + +sptr __msan_test_shadow(const void *x, uptr size) { + unsigned char *s = (unsigned char*)MEM_TO_SHADOW((uptr)x); + for (uptr i = 0; i < size; ++i) + if (s[i]) + return i; + return -1; +} + +int __msan_set_poison_in_malloc(int do_poison) { + int old = flags()->poison_in_malloc; + flags()->poison_in_malloc = do_poison; + return old; +} + +int __msan_has_dynamic_component() { + return msan_running_under_dr; +} + +NOINLINE +void __msan_clear_on_return() { + __msan_param_tls[0] = 0; +} + +static void* get_tls_base() { + u64 p; + asm("mov %%fs:0, %0" + : "=r"(p) ::); + return (void*)p; +} + +int __msan_get_retval_tls_offset() { + // volatile here is needed to avoid UB, because the compiler thinks that we + // are doing address arithmetics on unrelated pointers, and takes some + // shortcuts + volatile sptr retval_tls_p = (sptr)&__msan_retval_tls; + volatile sptr tls_base_p = (sptr)get_tls_base(); + return retval_tls_p - tls_base_p; +} + +int __msan_get_param_tls_offset() { + // volatile here is needed to avoid UB, because the compiler thinks that we + // are doing address arithmetics on unrelated pointers, and takes some + // shortcuts + volatile sptr param_tls_p = (sptr)&__msan_param_tls; + volatile sptr tls_base_p = (sptr)get_tls_base(); + return param_tls_p - tls_base_p; +} + +void __msan_partial_poison(const void* data, void* shadow, uptr size) { + internal_memcpy((void*)MEM_TO_SHADOW((uptr)data), shadow, size); +} + +void __msan_load_unpoisoned(void *src, uptr size, void *dst) { + internal_memcpy(dst, src, size); + __msan_unpoison(dst, size); +} + +void __msan_set_origin(const void *a, uptr size, u32 origin) { + // Origin mapping is 4 bytes per 4 bytes of application memory. + // Here we extend the range such that its left and right bounds are both + // 4 byte aligned. + if (!__msan_get_track_origins()) return; + uptr x = MEM_TO_ORIGIN((uptr)a); + uptr beg = x & ~3UL; // align down. + uptr end = (x + size + 3) & ~3UL; // align up. + u64 origin64 = ((u64)origin << 32) | origin; + // This is like memset, but the value is 32-bit. We unroll by 2 two write + // 64-bits at once. May want to unroll further to get 128-bit stores. + if (beg & 7ULL) { + *(u32*)beg = origin; + beg += 4; + } + for (uptr addr = beg; addr < (end & ~7UL); addr += 8) + *(u64*)addr = origin64; + if (end & 7ULL) + *(u32*)(end - 4) = origin; +} + +// 'descr' is created at compile time and contains '----' in the beginning. +// When we see descr for the first time we replace '----' with a uniq id +// and set the origin to (id | (31-th bit)). +void __msan_set_alloca_origin(void *a, uptr size, const char *descr) { + __msan_set_alloca_origin4(a, size, descr, 0); +} + +void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc) { + static const u32 dash = '-'; + static const u32 first_timer = + dash + (dash << 8) + (dash << 16) + (dash << 24); + u32 *id_ptr = (u32*)descr; + bool print = false; // internal_strstr(descr + 4, "AllocaTOTest") != 0; + u32 id = *id_ptr; + if (id == first_timer) { + id = atomic_fetch_add(&NumStackOriginDescrs, + 1, memory_order_relaxed); + *id_ptr = id; + CHECK_LT(id, kNumStackOriginDescrs); + StackOriginDescr[id] = descr + 4; + StackOriginPC[id] = pc; + if (print) + Printf("First time: id=%d %s %p \n", id, descr + 4, pc); + } + id |= 1U << 31; + if (print) + Printf("__msan_set_alloca_origin: descr=%s id=%x\n", descr + 4, id); + __msan_set_origin(a, size, id); +} + +const char *__msan_get_origin_descr_if_stack(u32 id) { + return GetOriginDescrIfStack(id, 0); +} + +u32 __msan_get_origin(const void *a) { + if (!__msan_get_track_origins()) return 0; + uptr x = (uptr)a; + uptr aligned = x & ~3ULL; + uptr origin_ptr = MEM_TO_ORIGIN(aligned); + return *(u32*)origin_ptr; +} + +u32 __msan_get_umr_origin() { + return __msan_origin_tls; +} + +u16 __sanitizer_unaligned_load16(const uu16 *p) { + __msan_retval_tls[0] = *(uu16 *)MEM_TO_SHADOW((uptr)p); + if (__msan_get_track_origins()) + __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + return *p; +} +u32 __sanitizer_unaligned_load32(const uu32 *p) { + __msan_retval_tls[0] = *(uu32 *)MEM_TO_SHADOW((uptr)p); + if (__msan_get_track_origins()) + __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + return *p; +} +u64 __sanitizer_unaligned_load64(const uu64 *p) { + __msan_retval_tls[0] = *(uu64 *)MEM_TO_SHADOW((uptr)p); + if (__msan_get_track_origins()) + __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + return *p; +} +void __sanitizer_unaligned_store16(uu16 *p, u16 x) { + *(uu16 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; + if (__msan_get_track_origins()) + *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + *p = x; +} +void __sanitizer_unaligned_store32(uu32 *p, u32 x) { + *(uu32 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; + if (__msan_get_track_origins()) + *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + *p = x; +} +void __sanitizer_unaligned_store64(uu64 *p, u64 x) { + *(uu64 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; + if (__msan_get_track_origins()) + *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + *p = x; +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char* __msan_default_options() { return ""; } +} // extern "C" +#endif + diff --git a/projects/compiler-rt/lib/msan/msan.h b/projects/compiler-rt/lib/msan/msan.h new file mode 100644 index 00000000..4e6c6194 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan.h @@ -0,0 +1,109 @@ +//===-- msan.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Private MSan header. +//===----------------------------------------------------------------------===// + +#ifndef MSAN_H +#define MSAN_H + +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "msan_interface_internal.h" +#include "msan_flags.h" + +#ifndef MSAN_REPLACE_OPERATORS_NEW_AND_DELETE +# define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1 +#endif + +#define MEM_TO_SHADOW(mem) (((uptr)mem) & ~0x400000000000ULL) +#define SHADOW_TO_ORIGIN(shadow) (((uptr)shadow) + 0x200000000000ULL) +#define MEM_TO_ORIGIN(mem) (SHADOW_TO_ORIGIN(MEM_TO_SHADOW(mem))) +#define MEM_IS_APP(mem) ((uptr)mem >= 0x600000000000ULL) +#define MEM_IS_SHADOW(mem) \ + ((uptr)mem >= 0x200000000000ULL && (uptr)mem <= 0x400000000000ULL) + +const int kMsanParamTlsSizeInWords = 100; +const int kMsanRetvalTlsSizeInWords = 100; + +namespace __msan { +extern int msan_inited; +extern bool msan_init_is_running; +extern int msan_report_count; + +bool ProtectRange(uptr beg, uptr end); +bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins); +char *GetProcSelfMaps(); +void InitializeInterceptors(); + +void MsanAllocatorThreadFinish(); +void *MsanReallocate(StackTrace *stack, void *oldp, uptr size, + uptr alignment, bool zeroise); +void MsanDeallocate(StackTrace *stack, void *ptr); +void InstallTrapHandler(); +void InstallAtExitHandler(); +void ReplaceOperatorsNewAndDelete(); + +const char *GetOriginDescrIfStack(u32 id, uptr *pc); + +void EnterSymbolizer(); +void ExitSymbolizer(); +bool IsInSymbolizer(); + +struct SymbolizerScope { + SymbolizerScope() { EnterSymbolizer(); } + ~SymbolizerScope() { ExitSymbolizer(); } +}; + +void EnterLoader(); +void ExitLoader(); + +void MsanDie(); +void PrintWarning(uptr pc, uptr bp); +void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin); + +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, + bool request_fast_unwind); + +void ReportUMR(StackTrace *stack, u32 origin); +void ReportExpectedUMRNotFound(StackTrace *stack); +void ReportAtExitStatistics(); + +// Unpoison first n function arguments. +void UnpoisonParam(uptr n); +void UnpoisonThreadLocalState(); + +#define GET_MALLOC_STACK_TRACE \ + StackTrace stack; \ + stack.size = 0; \ + if (__msan_get_track_origins() && msan_inited) \ + GetStackTrace(&stack, common_flags()->malloc_context_size, \ + StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ + common_flags()->fast_unwind_on_malloc) + +class ScopedThreadLocalStateBackup { + public: + ScopedThreadLocalStateBackup() { Backup(); } + ~ScopedThreadLocalStateBackup() { Restore(); } + void Backup(); + void Restore(); + private: + u64 va_arg_overflow_size_tls; +}; +} // namespace __msan + +#define MSAN_MALLOC_HOOK(ptr, size) \ + if (&__msan_malloc_hook) __msan_malloc_hook(ptr, size) +#define MSAN_FREE_HOOK(ptr) \ + if (&__msan_free_hook) __msan_free_hook(ptr) + +#endif // MSAN_H diff --git a/projects/compiler-rt/lib/msan/msan.syms.extra b/projects/compiler-rt/lib/msan/msan.syms.extra new file mode 100644 index 00000000..aad41cf1 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan.syms.extra @@ -0,0 +1 @@ +__msan_* diff --git a/projects/compiler-rt/lib/msan/msan_allocator.cc b/projects/compiler-rt/lib/msan/msan_allocator.cc new file mode 100644 index 00000000..2badf712 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_allocator.cc @@ -0,0 +1,178 @@ +//===-- msan_allocator.cc --------------------------- ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer allocator. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "msan.h" + +namespace __msan { + +struct Metadata { + uptr requested_size; +}; + +static const uptr kAllocatorSpace = 0x600000000000ULL; +static const uptr kAllocatorSize = 0x80000000000; // 8T. +static const uptr kMetadataSize = sizeof(Metadata); +static const uptr kMaxAllowedMallocSize = 8UL << 30; + +typedef SizeClassAllocator64 PrimaryAllocator; +typedef SizeClassAllocatorLocalCache AllocatorCache; +typedef LargeMmapAllocator<> SecondaryAllocator; +typedef CombinedAllocator Allocator; + +static THREADLOCAL AllocatorCache cache; +static Allocator allocator; + +static int inited = 0; + +static inline void Init() { + if (inited) return; + __msan_init(); + inited = true; // this must happen before any threads are created. + allocator.Init(); +} + +void MsanAllocatorThreadFinish() { + allocator.SwallowCache(&cache); +} + +static void *MsanAllocate(StackTrace *stack, uptr size, + uptr alignment, bool zeroise) { + Init(); + if (size > kMaxAllowedMallocSize) { + Report("WARNING: MemorySanitizer failed to allocate %p bytes\n", + (void *)size); + return AllocatorReturnNull(); + } + void *res = allocator.Allocate(&cache, size, alignment, false); + Metadata *meta = reinterpret_cast(allocator.GetMetaData(res)); + meta->requested_size = size; + if (zeroise) { + __msan_clear_and_unpoison(res, size); + } else if (flags()->poison_in_malloc) { + __msan_poison(res, size); + if (__msan_get_track_origins()) { + u32 stack_id = StackDepotPut(stack->trace, stack->size); + CHECK(stack_id); + CHECK_EQ((stack_id >> 31), + 0); // Higher bit is occupied by stack origins. + __msan_set_origin(res, size, stack_id); + } + } + MSAN_MALLOC_HOOK(res, size); + return res; +} + +void MsanDeallocate(StackTrace *stack, void *p) { + CHECK(p); + Init(); + MSAN_FREE_HOOK(p); + Metadata *meta = reinterpret_cast(allocator.GetMetaData(p)); + uptr size = meta->requested_size; + meta->requested_size = 0; + // This memory will not be reused by anyone else, so we are free to keep it + // poisoned. + if (flags()->poison_in_free) { + __msan_poison(p, size); + if (__msan_get_track_origins()) { + u32 stack_id = StackDepotPut(stack->trace, stack->size); + CHECK(stack_id); + CHECK_EQ((stack_id >> 31), + 0); // Higher bit is occupied by stack origins. + __msan_set_origin(p, size, stack_id); + } + } + allocator.Deallocate(&cache, p); +} + +void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, + uptr alignment, bool zeroise) { + if (!old_p) + return MsanAllocate(stack, new_size, alignment, zeroise); + if (!new_size) { + MsanDeallocate(stack, old_p); + return 0; + } + Metadata *meta = reinterpret_cast(allocator.GetMetaData(old_p)); + uptr old_size = meta->requested_size; + uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p); + if (new_size <= actually_allocated_size) { + // We are not reallocating here. + meta->requested_size = new_size; + if (new_size > old_size) + __msan_poison((char*)old_p + old_size, new_size - old_size); + return old_p; + } + uptr memcpy_size = Min(new_size, old_size); + void *new_p = MsanAllocate(stack, new_size, alignment, zeroise); + // Printf("realloc: old_size %zd new_size %zd\n", old_size, new_size); + if (new_p) { + __msan_memcpy(new_p, old_p, memcpy_size); + MsanDeallocate(stack, old_p); + } + return new_p; +} + +static uptr AllocationSize(const void *p) { + if (p == 0) + return 0; + const void *beg = allocator.GetBlockBegin(p); + if (beg != p) + return 0; + Metadata *b = (Metadata*)allocator.GetMetaData(p); + return b->requested_size; +} + +} // namespace __msan + +using namespace __msan; + +uptr __msan_get_current_allocated_bytes() { + u64 stats[AllocatorStatCount]; + allocator.GetStats(stats); + u64 m = stats[AllocatorStatMalloced]; + u64 f = stats[AllocatorStatFreed]; + return m >= f ? m - f : 1; +} + +uptr __msan_get_heap_size() { + u64 stats[AllocatorStatCount]; + allocator.GetStats(stats); + u64 m = stats[AllocatorStatMmapped]; + u64 f = stats[AllocatorStatUnmapped]; + return m >= f ? m - f : 1; +} + +uptr __msan_get_free_bytes() { + return 1; +} + +uptr __msan_get_unmapped_bytes() { + return 1; +} + +uptr __msan_get_estimated_allocated_size(uptr size) { + return size; +} + +int __msan_get_ownership(const void *p) { + return AllocationSize(p) != 0; +} + +uptr __msan_get_allocated_size(const void *p) { + return AllocationSize(p); +} diff --git a/projects/compiler-rt/lib/msan/msan_blacklist.txt b/projects/compiler-rt/lib/msan/msan_blacklist.txt new file mode 100644 index 00000000..44a5680d --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_blacklist.txt @@ -0,0 +1,7 @@ +# Blacklist for MemorySanitizer. Turns off instrumentation of particular +# functions or sources. Use with care. You may set location of blacklist +# at compile-time using -fsanitize-blacklist= flag. + +# Example usage: +# fun:*bad_function_name* +# src:file_with_tricky_code.cc diff --git a/projects/compiler-rt/lib/msan/msan_flags.h b/projects/compiler-rt/lib/msan/msan_flags.h new file mode 100644 index 00000000..93fa8a60 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_flags.h @@ -0,0 +1,35 @@ +//===-- msan_flags.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer allocator. +//===----------------------------------------------------------------------===// +#ifndef MSAN_FLAGS_H +#define MSAN_FLAGS_H + +namespace __msan { + +// Flags. +struct Flags { + int exit_code; + bool poison_heap_with_zeroes; // default: false + bool poison_stack_with_zeroes; // default: false + bool poison_in_malloc; // default: true + bool poison_in_free; // default: true + bool report_umrs; + bool wrap_signals; + bool halt_on_error; +}; + +Flags *flags(); + +} // namespace __msan + +#endif // MSAN_FLAGS_H diff --git a/projects/compiler-rt/lib/msan/msan_interceptors.cc b/projects/compiler-rt/lib/msan/msan_interceptors.cc new file mode 100644 index 00000000..15a8bec1 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_interceptors.cc @@ -0,0 +1,1546 @@ +//===-- msan_interceptors.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Interceptors for standard library functions. +// +// FIXME: move as many interceptors as possible into +// sanitizer_common/sanitizer_common_interceptors.h +//===----------------------------------------------------------------------===// + +#include "interception/interception.h" +#include "msan.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_linux.h" + +#include +// ACHTUNG! No other system header includes in this file. +// Ideally, we should get rid of stdarg.h as well. + +using namespace __msan; + +using __sanitizer::memory_order; +using __sanitizer::atomic_load; +using __sanitizer::atomic_store; +using __sanitizer::atomic_uintptr_t; + +static unsigned g_thread_finalize_key; + +// True if this is a nested interceptor. +static THREADLOCAL int in_interceptor_scope; + +struct InterceptorScope { + InterceptorScope() { ++in_interceptor_scope; } + ~InterceptorScope() { --in_interceptor_scope; } +}; + +bool IsInInterceptorScope() { + return in_interceptor_scope; +} + +#define ENSURE_MSAN_INITED() do { \ + CHECK(!msan_init_is_running); \ + if (!msan_inited) { \ + __msan_init(); \ + } \ +} while (0) + +// Check that [x, x+n) range is unpoisoned. +#define CHECK_UNPOISONED_0(x, n) \ + do { \ + sptr offset = __msan_test_shadow(x, n); \ + if (__msan::IsInSymbolizer()) break; \ + if (offset >= 0 && __msan::flags()->report_umrs) { \ + GET_CALLER_PC_BP_SP; \ + (void) sp; \ + Printf("UMR in %s at offset %d inside [%p, +%d) \n", __FUNCTION__, \ + offset, x, n); \ + __msan::PrintWarningWithOrigin(pc, bp, \ + __msan_get_origin((char *)x + offset)); \ + if (__msan::flags()->halt_on_error) { \ + Printf("Exiting\n"); \ + Die(); \ + } \ + } \ + } while (0) + +// Check that [x, x+n) range is unpoisoned unless we are in a nested +// interceptor. +#define CHECK_UNPOISONED(x, n) \ + do { \ + if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ + } while (0); + +static void *fast_memset(void *ptr, int c, SIZE_T n); +static void *fast_memcpy(void *dst, const void *src, SIZE_T n); + +INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(fread)(ptr, size, nmemb, file); + if (res > 0) + __msan_unpoison(ptr, res *size); + return res; +} + +INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb, + void *file) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(fread_unlocked)(ptr, size, nmemb, file); + if (res > 0) + __msan_unpoison(ptr, res *size); + return res; +} + +INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { + ENSURE_MSAN_INITED(); + SSIZE_T res = REAL(readlink)(path, buf, bufsiz); + if (res > 0) + __msan_unpoison(buf, res); + return res; +} + +INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { + return __msan_memcpy(dest, src, n); +} + +INTERCEPTOR(void *, mempcpy, void *dest, const void *src, SIZE_T n) { + return (char *)__msan_memcpy(dest, src, n) + n; +} + +INTERCEPTOR(void *, memccpy, void *dest, const void *src, int c, SIZE_T n) { + ENSURE_MSAN_INITED(); + void *res = REAL(memccpy)(dest, src, c, n); + CHECK(!res || (res >= dest && res <= (char *)dest + n)); + SIZE_T sz = res ? (char *)res - (char *)dest : n; + CHECK_UNPOISONED(src, sz); + __msan_unpoison(dest, sz); + return res; +} + +INTERCEPTOR(void *, memmove, void *dest, const void *src, SIZE_T n) { + return __msan_memmove(dest, src, n); +} + +INTERCEPTOR(void *, memset, void *s, int c, SIZE_T n) { + return __msan_memset(s, c, n); +} + +INTERCEPTOR(void *, bcopy, const void *src, void *dest, SIZE_T n) { + return __msan_memmove(dest, src, n); +} + +INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + CHECK_EQ(alignment & (alignment - 1), 0); + CHECK_NE(memptr, 0); + *memptr = MsanReallocate(&stack, 0, size, alignment, false); + CHECK_NE(*memptr, 0); + __msan_unpoison(memptr, sizeof(*memptr)); + return 0; +} + +INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + CHECK_EQ(boundary & (boundary - 1), 0); + void *ptr = MsanReallocate(&stack, 0, size, boundary, false); + return ptr; +} + +INTERCEPTOR(void *, valloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + void *ptr = MsanReallocate(&stack, 0, size, GetPageSizeCached(), false); + return ptr; +} + +INTERCEPTOR(void *, pvalloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + void *ptr = MsanReallocate(&stack, 0, size, PageSize, false); + return ptr; +} + +INTERCEPTOR(void, free, void *ptr) { + GET_MALLOC_STACK_TRACE; + if (ptr == 0) return; + MsanDeallocate(&stack, ptr); +} + +INTERCEPTOR(SIZE_T, strlen, const char *s) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(strlen)(s); + CHECK_UNPOISONED(s, res + 1); + return res; +} + +INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(strnlen)(s, n); + SIZE_T scan_size = (res == n) ? res : res + 1; + CHECK_UNPOISONED(s, scan_size); + return res; +} + +// FIXME: Add stricter shadow checks in str* interceptors (ex.: strcpy should +// check the shadow of the terminating \0 byte). + +INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T n = REAL(strlen)(src); + char *res = REAL(strcpy)(dest, src); // NOLINT + __msan_copy_poison(dest, src, n + 1); + return res; +} + +INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T copy_size = REAL(strnlen)(src, n); + if (copy_size < n) + copy_size++; // trailing \0 + char *res = REAL(strncpy)(dest, src, n); // NOLINT + __msan_copy_poison(dest, src, copy_size); + return res; +} + +INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T n = REAL(strlen)(src); + char *res = REAL(stpcpy)(dest, src); // NOLINT + __msan_copy_poison(dest, src, n + 1); + return res; +} + +INTERCEPTOR(char *, strdup, char *src) { + ENSURE_MSAN_INITED(); + SIZE_T n = REAL(strlen)(src); + char *res = REAL(strdup)(src); + __msan_copy_poison(res, src, n + 1); + return res; +} + +INTERCEPTOR(char *, __strdup, char *src) { + ENSURE_MSAN_INITED(); + SIZE_T n = REAL(strlen)(src); + char *res = REAL(__strdup)(src); + __msan_copy_poison(res, src, n + 1); + return res; +} + +INTERCEPTOR(char *, strndup, char *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + SIZE_T copy_size = REAL(strnlen)(src, n); + char *res = REAL(strndup)(src, n); + __msan_copy_poison(res, src, copy_size); + __msan_unpoison(res + copy_size, 1); // \0 + return res; +} + +INTERCEPTOR(char *, __strndup, char *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + SIZE_T copy_size = REAL(strnlen)(src, n); + char *res = REAL(__strndup)(src, n); + __msan_copy_poison(res, src, copy_size); + __msan_unpoison(res + copy_size, 1); // \0 + return res; +} + +INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) { + ENSURE_MSAN_INITED(); + char *res = REAL(gcvt)(number, ndigit, buf); + // DynamoRio tool will take care of unpoisoning gcvt result for us. + if (!__msan_has_dynamic_component()) { + SIZE_T n = REAL(strlen)(buf); + __msan_unpoison(buf, n + 1); + } + return res; +} + +INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T src_size = REAL(strlen)(src); + SIZE_T dest_size = REAL(strlen)(dest); + char *res = REAL(strcat)(dest, src); // NOLINT + __msan_copy_poison(dest + dest_size, src, src_size + 1); + return res; +} + +INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T dest_size = REAL(strlen)(dest); + SIZE_T copy_size = REAL(strlen)(src); + if (copy_size < n) + copy_size++; // trailing \0 + char *res = REAL(strncat)(dest, src, n); // NOLINT + __msan_copy_poison(dest + dest_size, src, copy_size); + return res; +} + +INTERCEPTOR(long, strtol, const char *nptr, char **endptr, // NOLINT + int base) { + ENSURE_MSAN_INITED(); + long res = REAL(strtol)(nptr, endptr, base); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(long long, strtoll, const char *nptr, char **endptr, // NOLINT + int base) { + ENSURE_MSAN_INITED(); + long res = REAL(strtoll)(nptr, endptr, base); //NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(unsigned long, strtoul, const char *nptr, char **endptr, // NOLINT + int base) { + ENSURE_MSAN_INITED(); + unsigned long res = REAL(strtoul)(nptr, endptr, base); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(unsigned long long, strtoull, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_MSAN_INITED(); + unsigned long res = REAL(strtoull)(nptr, endptr, base); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(double, strtod, const char *nptr, char **endptr) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(strtod)(nptr, endptr); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(double, strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(double, __strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(__strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(strtof)(nptr, endptr); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(float, strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(float, __strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(__strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(strtold)(nptr, endptr); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(long double, strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(long double, __strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(__strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) { + ENSURE_MSAN_INITED(); + int res = REAL(vasprintf)(strp, format, ap); + if (res >= 0 && !__msan_has_dynamic_component()) { + __msan_unpoison(strp, sizeof(*strp)); + __msan_unpoison(*strp, res + 1); + } + return res; +} + +INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) { // NOLINT + ENSURE_MSAN_INITED(); + va_list ap; + va_start(ap, format); + int res = vasprintf(strp, format, ap); // NOLINT + va_end(ap); + return res; +} + +INTERCEPTOR(int, vsnprintf, char *str, uptr size, + const char *format, va_list ap) { + ENSURE_MSAN_INITED(); + int res = REAL(vsnprintf)(str, size, format, ap); + if (res >= 0 && !__msan_has_dynamic_component()) { + __msan_unpoison(str, res + 1); + } + return res; +} + +INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { + ENSURE_MSAN_INITED(); + int res = REAL(vsprintf)(str, format, ap); + if (res >= 0 && !__msan_has_dynamic_component()) { + __msan_unpoison(str, res + 1); + } + return res; +} + +INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) { + ENSURE_MSAN_INITED(); + int res = REAL(vswprintf)(str, size, format, ap); + if (res >= 0 && !__msan_has_dynamic_component()) { + __msan_unpoison(str, 4 * (res + 1)); + } + return res; +} + +INTERCEPTOR(int, sprintf, char *str, const char *format, ...) { // NOLINT + ENSURE_MSAN_INITED(); + va_list ap; + va_start(ap, format); + int res = vsprintf(str, format, ap); // NOLINT + va_end(ap); + return res; +} + +INTERCEPTOR(int, snprintf, char *str, uptr size, const char *format, ...) { + ENSURE_MSAN_INITED(); + va_list ap; + va_start(ap, format); + int res = vsnprintf(str, size, format, ap); + va_end(ap); + return res; +} + +INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { + ENSURE_MSAN_INITED(); + va_list ap; + va_start(ap, format); + int res = vswprintf(str, size, format, ap); + va_end(ap); + return res; +} + +// SIZE_T strftime(char *s, SIZE_T max, const char *format,const struct tm *tm); +INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format, + __sanitizer_tm *tm) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(strftime)(s, max, format, tm); + if (res) __msan_unpoison(s, res + 1); + return res; +} + +INTERCEPTOR(int, mbtowc, wchar_t *dest, const char *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + int res = REAL(mbtowc)(dest, src, n); + if (res != -1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); + return res; +} + +INTERCEPTOR(int, mbrtowc, wchar_t *dest, const char *src, SIZE_T n, void *ps) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(mbrtowc)(dest, src, n, ps); + if (res != (SIZE_T)-1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); + return res; +} + +INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { + ENSURE_MSAN_INITED(); + SIZE_T res = REAL(wcslen)(s); + CHECK_UNPOISONED(s, sizeof(wchar_t) * (res + 1)); + return res; +} + +// wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); +INTERCEPTOR(wchar_t *, wcschr, void *s, wchar_t wc, void *ps) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wcschr)(s, wc, ps); + return res; +} + +// wchar_t *wcscpy(wchar_t *dest, const wchar_t *src); +INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wcscpy)(dest, src); + __msan_copy_poison(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1)); + return res; +} + +// wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, SIZE_T n); +INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wmemcpy)(dest, src, n); + __msan_copy_poison(dest, src, n * sizeof(wchar_t)); + return res; +} + +INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wmempcpy)(dest, src, n); + __msan_copy_poison(dest, src, n * sizeof(wchar_t)); + return res; +} + +INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) { + CHECK(MEM_IS_APP(s)); + ENSURE_MSAN_INITED(); + wchar_t *res = (wchar_t *)fast_memset(s, c, n * sizeof(wchar_t)); + __msan_unpoison(s, n * sizeof(wchar_t)); + return res; +} + +INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dest, const wchar_t *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wmemmove)(dest, src, n); + __msan_move_poison(dest, src, n * sizeof(wchar_t)); + return res; +} + +INTERCEPTOR(int, wcscmp, const wchar_t *s1, const wchar_t *s2) { + ENSURE_MSAN_INITED(); + int res = REAL(wcscmp)(s1, s2); + return res; +} + +INTERCEPTOR(double, wcstod, const wchar_t *nptr, wchar_t **endptr) { + ENSURE_MSAN_INITED(); + double res = REAL(wcstod)(nptr, endptr); + __msan_unpoison(endptr, sizeof(*endptr)); + return res; +} + +INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { + ENSURE_MSAN_INITED(); + int res = REAL(gettimeofday)(tv, tz); + if (tv) + __msan_unpoison(tv, 16); + if (tz) + __msan_unpoison(tz, 8); + return res; +} + +INTERCEPTOR(char *, fcvt, double x, int a, int *b, int *c) { + ENSURE_MSAN_INITED(); + char *res = REAL(fcvt)(x, a, b, c); + if (!__msan_has_dynamic_component()) { + __msan_unpoison(b, sizeof(*b)); + __msan_unpoison(c, sizeof(*c)); + } + return res; +} + +INTERCEPTOR(char *, getenv, char *name) { + ENSURE_MSAN_INITED(); + char *res = REAL(getenv)(name); + if (!__msan_has_dynamic_component()) { + if (res) + __msan_unpoison(res, REAL(strlen)(res) + 1); + } + return res; +} + +extern char **environ; + +static void UnpoisonEnviron() { + char **envp = environ; + for (; *envp; ++envp) { + __msan_unpoison(envp, sizeof(*envp)); + __msan_unpoison(*envp, REAL(strlen)(*envp) + 1); + } + // Trailing NULL pointer. + __msan_unpoison(envp, sizeof(*envp)); +} + +INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) { + ENSURE_MSAN_INITED(); + int res = REAL(setenv)(name, value, overwrite); + if (!res) UnpoisonEnviron(); + return res; +} + +INTERCEPTOR(int, putenv, char *string) { + ENSURE_MSAN_INITED(); + int res = REAL(putenv)(string); + if (!res) UnpoisonEnviron(); + return res; +} + +INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstat)(magic, fd, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} + +INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstat64)(magic, fd, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat64_sz); + return res; +} + +INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} + +INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat64)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); + return res; +} + +INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(__xstat)(magic, path, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} + +INTERCEPTOR(int, __xstat64, int magic, char *path, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(__xstat64)(magic, path, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat64_sz); + return res; +} + +INTERCEPTOR(int, __lxstat, int magic, char *path, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(__lxstat)(magic, path, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} + +INTERCEPTOR(int, __lxstat64, int magic, char *path, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(__lxstat64)(magic, path, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat64_sz); + return res; +} + +INTERCEPTOR(int, pipe, int pipefd[2]) { + if (msan_init_is_running) + return REAL(pipe)(pipefd); + ENSURE_MSAN_INITED(); + int res = REAL(pipe)(pipefd); + if (!res) + __msan_unpoison(pipefd, sizeof(int[2])); + return res; +} + +INTERCEPTOR(int, pipe2, int pipefd[2], int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(pipe2)(pipefd, flags); + if (!res) + __msan_unpoison(pipefd, sizeof(int[2])); + return res; +} + +INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int sv[2]) { + ENSURE_MSAN_INITED(); + int res = REAL(socketpair)(domain, type, protocol, sv); + if (!res) + __msan_unpoison(sv, sizeof(int[2])); + return res; +} + +INTERCEPTOR(char *, fgets, char *s, int size, void *stream) { + ENSURE_MSAN_INITED(); + char *res = REAL(fgets)(s, size, stream); + if (res) + __msan_unpoison(s, REAL(strlen)(s) + 1); + return res; +} + +INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { + ENSURE_MSAN_INITED(); + char *res = REAL(fgets_unlocked)(s, size, stream); + if (res) + __msan_unpoison(s, REAL(strlen)(s) + 1); + return res; +} + +INTERCEPTOR(int, getrlimit, int resource, void *rlim) { + if (msan_init_is_running) + return REAL(getrlimit)(resource, rlim); + ENSURE_MSAN_INITED(); + int res = REAL(getrlimit)(resource, rlim); + if (!res) + __msan_unpoison(rlim, __sanitizer::struct_rlimit_sz); + return res; +} + +INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { + if (msan_init_is_running) + return REAL(getrlimit64)(resource, rlim); + ENSURE_MSAN_INITED(); + int res = REAL(getrlimit64)(resource, rlim); + if (!res) + __msan_unpoison(rlim, __sanitizer::struct_rlimit64_sz); + return res; +} + +INTERCEPTOR(int, uname, void *utsname) { + ENSURE_MSAN_INITED(); + int res = REAL(uname)(utsname); + if (!res) { + __msan_unpoison(utsname, __sanitizer::struct_utsname_sz); + } + return res; +} + +INTERCEPTOR(int, gethostname, char *name, SIZE_T len) { + ENSURE_MSAN_INITED(); + int res = REAL(gethostname)(name, len); + if (!res) { + SIZE_T real_len = REAL(strnlen)(name, len); + if (real_len < len) + ++real_len; + __msan_unpoison(name, real_len); + } + return res; +} + +INTERCEPTOR(int, epoll_wait, int epfd, void *events, int maxevents, + int timeout) { + ENSURE_MSAN_INITED(); + int res = REAL(epoll_wait)(epfd, events, maxevents, timeout); + if (res > 0) { + __msan_unpoison(events, __sanitizer::struct_epoll_event_sz * res); + } + return res; +} + +INTERCEPTOR(int, epoll_pwait, int epfd, void *events, int maxevents, + int timeout, void *sigmask) { + ENSURE_MSAN_INITED(); + int res = REAL(epoll_pwait)(epfd, events, maxevents, timeout, sigmask); + if (res > 0) { + __msan_unpoison(events, __sanitizer::struct_epoll_event_sz * res); + } + return res; +} + +INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { + ENSURE_MSAN_INITED(); + SSIZE_T res = REAL(recv)(fd, buf, len, flags); + if (res > 0) + __msan_unpoison(buf, res); + return res; +} + +INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, + void *srcaddr, int *addrlen) { + ENSURE_MSAN_INITED(); + SIZE_T srcaddr_sz; + if (srcaddr) srcaddr_sz = *addrlen; + SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); + if (res > 0) { + __msan_unpoison(buf, res); + if (srcaddr) { + SIZE_T sz = *addrlen; + __msan_unpoison(srcaddr, (sz < srcaddr_sz) ? sz : srcaddr_sz); + } + } + return res; +} + +INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return AllocatorReturnNull(); + GET_MALLOC_STACK_TRACE; + if (!msan_inited) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const SIZE_T kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static SIZE_T allocated; + SIZE_T size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + return MsanReallocate(&stack, 0, nmemb * size, sizeof(u64), true); +} + +INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + return MsanReallocate(&stack, ptr, size, sizeof(u64), false); +} + +INTERCEPTOR(void *, malloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + return MsanReallocate(&stack, 0, size, sizeof(u64), false); +} + +void __msan_allocated_memory(const void* data, uptr size) { + GET_MALLOC_STACK_TRACE; + if (flags()->poison_in_malloc) + __msan_poison(data, size); + if (__msan_get_track_origins()) { + u32 stack_id = StackDepotPut(stack.trace, stack.size); + CHECK(stack_id); + CHECK_EQ((stack_id >> 31), 0); // Higher bit is occupied by stack origins. + __msan_set_origin(data, size, stack_id); + } +} + +INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF_T offset) { + ENSURE_MSAN_INITED(); + void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + __msan_unpoison(res, RoundUpTo(length, GetPageSize())); + return res; +} + +INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, + int fd, OFF64_T offset) { + ENSURE_MSAN_INITED(); + void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); + if (res != (void*)-1) + __msan_unpoison(res, RoundUpTo(length, GetPageSize())); + return res; +} + +struct dlinfo { + char *dli_fname; + void *dli_fbase; + char *dli_sname; + void *dli_saddr; +}; + +INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { + ENSURE_MSAN_INITED(); + int res = REAL(dladdr)(addr, info); + if (res != 0) { + __msan_unpoison(info, sizeof(*info)); + if (info->dli_fname) + __msan_unpoison(info->dli_fname, REAL(strlen)(info->dli_fname) + 1); + if (info->dli_sname) + __msan_unpoison(info->dli_sname, REAL(strlen)(info->dli_sname) + 1); + } + return res; +} + +INTERCEPTOR(char *, dlerror) { + ENSURE_MSAN_INITED(); + char *res = REAL(dlerror)(); + if (res != 0) __msan_unpoison(res, REAL(strlen)(res) + 1); + return res; +} + +// dlopen() ultimately calls mmap() down inside the loader, which generally +// doesn't participate in dynamic symbol resolution. Therefore we won't +// intercept its calls to mmap, and we have to hook it here. The loader +// initializes the module before returning, so without the dynamic component, we +// won't be able to clear the shadow before the initializers. Fixing this would +// require putting our own initializer first to clear the shadow. +INTERCEPTOR(void *, dlopen, const char *filename, int flag) { + ENSURE_MSAN_INITED(); + EnterLoader(); + link_map *map = (link_map *)REAL(dlopen)(filename, flag); + ExitLoader(); + if (!__msan_has_dynamic_component() && map) { + // If msandr didn't clear the shadow before the initializers ran, we do it + // ourselves afterwards. + ForEachMappedRegion(map, __msan_unpoison); + } + return (void *)map; +} + +typedef int (*dl_iterate_phdr_cb)(__sanitizer_dl_phdr_info *info, SIZE_T size, + void *data); +struct dl_iterate_phdr_data { + dl_iterate_phdr_cb callback; + void *data; +}; + +static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, + void *data) { + if (info) { + __msan_unpoison(info, size); + if (info->dlpi_name) + __msan_unpoison(info->dlpi_name, REAL(strlen)(info->dlpi_name) + 1); + } + dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; + UnpoisonParam(3); + return cbdata->callback(info, size, cbdata->data); +} + +INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb callback, void *data) { + ENSURE_MSAN_INITED(); + EnterLoader(); + dl_iterate_phdr_data cbdata; + cbdata.callback = callback; + cbdata.data = data; + int res = REAL(dl_iterate_phdr)(msan_dl_iterate_phdr_cb, (void *)&cbdata); + ExitLoader(); + return res; +} + +INTERCEPTOR(int, getrusage, int who, void *usage) { + ENSURE_MSAN_INITED(); + int res = REAL(getrusage)(who, usage); + if (res == 0) { + __msan_unpoison(usage, __sanitizer::struct_rusage_sz); + } + return res; +} + +// sigactions_mu guarantees atomicity of sigaction() and signal() calls. +// Access to sigactions[] is gone with relaxed atomics to avoid data race with +// the signal handler. +const int kMaxSignals = 1024; +static atomic_uintptr_t sigactions[kMaxSignals]; +static StaticSpinMutex sigactions_mu; + +static void SignalHandler(int signo) { + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(1); + + typedef void (*signal_cb)(int x); + signal_cb cb = + (signal_cb)atomic_load(&sigactions[signo], memory_order_relaxed); + cb(signo); +} + +static void SignalAction(int signo, void *si, void *uc) { + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(3); + __msan_unpoison(si, sizeof(__sanitizer_sigaction)); + __msan_unpoison(uc, __sanitizer::ucontext_t_sz); + + typedef void (*sigaction_cb)(int, void *, void *); + sigaction_cb cb = + (sigaction_cb)atomic_load(&sigactions[signo], memory_order_relaxed); + cb(signo, si, uc); +} + +INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, + __sanitizer_sigaction *oldact) { + ENSURE_MSAN_INITED(); + // FIXME: check that *act is unpoisoned. + // That requires intercepting all of sigemptyset, sigfillset, etc. + int res; + if (flags()->wrap_signals) { + SpinMutexLock lock(&sigactions_mu); + CHECK_LT(signo, kMaxSignals); + uptr old_cb = atomic_load(&sigactions[signo], memory_order_relaxed); + __sanitizer_sigaction new_act; + __sanitizer_sigaction *pnew_act = act ? &new_act : 0; + if (act) { + internal_memcpy(pnew_act, act, sizeof(__sanitizer_sigaction)); + uptr cb = (uptr)pnew_act->sa_sigaction; + uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo) + ? (uptr)SignalAction + : (uptr)SignalHandler; + if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { + atomic_store(&sigactions[signo], cb, memory_order_relaxed); + pnew_act->sa_sigaction = (void (*)(int, void *, void *))new_cb; + } + } + res = REAL(sigaction)(signo, pnew_act, oldact); + if (res == 0 && oldact) { + uptr cb = (uptr)oldact->sa_sigaction; + if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { + oldact->sa_sigaction = (void (*)(int, void *, void *))old_cb; + } + } + } else { + res = REAL(sigaction)(signo, act, oldact); + } + + if (res == 0 && oldact) { + __msan_unpoison(oldact, sizeof(__sanitizer_sigaction)); + } + return res; +} + +INTERCEPTOR(int, signal, int signo, uptr cb) { + ENSURE_MSAN_INITED(); + if (flags()->wrap_signals) { + CHECK_LT(signo, kMaxSignals); + SpinMutexLock lock(&sigactions_mu); + if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { + atomic_store(&sigactions[signo], cb, memory_order_relaxed); + cb = (uptr) SignalHandler; + } + return REAL(signal)(signo, cb); + } else { + return REAL(signal)(signo, cb); + } +} + +extern "C" int pthread_attr_init(void *attr); +extern "C" int pthread_attr_destroy(void *attr); +extern "C" int pthread_setspecific(unsigned key, const void *v); +extern "C" int pthread_yield(); + +static void thread_finalize(void *v) { + uptr iter = (uptr)v; + if (iter > 1) { + if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + return; + } + MsanAllocatorThreadFinish(); +} + +struct ThreadParam { + void* (*callback)(void *arg); + void *param; + atomic_uintptr_t done; +}; + +static void *MsanThreadStartFunc(void *arg) { + ThreadParam *p = (ThreadParam *)arg; + void* (*callback)(void *arg) = p->callback; + void *param = p->param; + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + atomic_store(&p->done, 1, memory_order_release); + return callback(param); +} + +INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), + void * param) { + ENSURE_MSAN_INITED(); // for GetTlsSize() + __sanitizer_pthread_attr_t myattr; + if (attr == 0) { + pthread_attr_init(&myattr); + attr = &myattr; + } + + AdjustStackSizeLinux(attr); + + ThreadParam p; + p.callback = callback; + p.param = param; + atomic_store(&p.done, 0, memory_order_relaxed); + + int res = REAL(pthread_create)(th, attr, MsanThreadStartFunc, (void *)&p); + if (res == 0) { + while (atomic_load(&p.done, memory_order_acquire) != 1) + pthread_yield(); + } + + if (attr == &myattr) + pthread_attr_destroy(&myattr); + if (!res) { + __msan_unpoison(th, __sanitizer::pthread_t_sz); + } + return res; +} + +INTERCEPTOR(int, pthread_key_create, __sanitizer_pthread_key_t *key, + void (*dtor)(void *value)) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_key_create)(key, dtor); + if (!res && key) + __msan_unpoison(key, sizeof(*key)); + return res; +} + +INTERCEPTOR(int, pthread_join, void *th, void **retval) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_join)(th, retval); + if (!res && retval) + __msan_unpoison(retval, sizeof(*retval)); + return res; +} + +extern char *tzname[2]; + +INTERCEPTOR(void, tzset) { + ENSURE_MSAN_INITED(); + REAL(tzset)(); + if (tzname[0]) + __msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1); + if (tzname[1]) + __msan_unpoison(tzname[1], REAL(strlen)(tzname[1]) + 1); + return; +} + +struct MSanAtExitRecord { + void (*func)(void *arg); + void *arg; +}; + +void MSanAtExitWrapper(void *arg) { + UnpoisonParam(1); + MSanAtExitRecord *r = (MSanAtExitRecord *)arg; + r->func(r->arg); + InternalFree(r); +} + +// Unpoison argument shadow for C++ module destructors. +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + if (msan_init_is_running) return REAL(__cxa_atexit)(func, arg, dso_handle); + ENSURE_MSAN_INITED(); + MSanAtExitRecord *r = + (MSanAtExitRecord *)InternalAlloc(sizeof(MSanAtExitRecord)); + r->func = func; + r->arg = arg; + return REAL(__cxa_atexit)(MSanAtExitWrapper, r, dso_handle); +} + +DECLARE_REAL(int, shmctl, int shmid, int cmd, void *buf) + +INTERCEPTOR(void *, shmat, int shmid, const void *shmaddr, int shmflg) { + ENSURE_MSAN_INITED(); + void *p = REAL(shmat)(shmid, shmaddr, shmflg); + if (p != (void *)-1) { + __sanitizer_shmid_ds ds; + int res = REAL(shmctl)(shmid, shmctl_ipc_stat, &ds); + if (!res) { + __msan_unpoison(p, ds.shm_segsz); + } + } + return p; +} + +// Linux kernel has a bug that leads to kernel deadlock if a process +// maps TBs of memory and then calls mlock(). +static void MlockIsUnsupported() { + static atomic_uint8_t printed; + if (atomic_exchange(&printed, 1, memory_order_relaxed)) + return; + if (common_flags()->verbosity > 0) + Printf("INFO: MemorySanitizer ignores mlock/mlockall/munlock/munlockall\n"); +} + +INTERCEPTOR(int, mlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, mlockall, int flags) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlockall, void) { + MlockIsUnsupported(); + return 0; +} + +struct MSanInterceptorContext { + bool in_interceptor_scope; +}; + +namespace __msan { + +int OnExit() { + // FIXME: ask frontend whether we need to return failure. + return 0; +} + +} // namespace __msan + +extern "C" int *__errno_location(void); + +// A version of CHECK_UNPOISONED using a saved scope value. Used in common +// interceptors. +#define CHECK_UNPOISONED_CTX(ctx, x, n) \ + do { \ + if (!((MSanInterceptorContext *)ctx)->in_interceptor_scope) \ + CHECK_UNPOISONED_0(x, n); \ + } while (0) + +#define MSAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \ + common_flags()->verbosity > 0) \ + Report("MemorySanitizer: failed to intercept '" #name "'\n"); \ + } while (0) + +#define COMMON_INTERCEPT_FUNCTION(name) MSAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + UnpoisonParam(count) +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + __msan_unpoison(ptr, size) +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + CHECK_UNPOISONED_CTX(ctx, ptr, size) +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, ptr, size) \ + __msan_unpoison(ptr, size) +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ + MSanInterceptorContext msan_ctx = {IsInInterceptorScope()}; \ + ctx = (void *)&msan_ctx; \ + (void)ctx; \ + InterceptorScope interceptor_scope; \ + __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */ \ + ENSURE_MSAN_INITED(); +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() +#include "sanitizer_common/sanitizer_common_interceptors.inc" + +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) +#include "sanitizer_common/sanitizer_common_syscalls.inc" + +// static +void *fast_memset(void *ptr, int c, SIZE_T n) { + // hack until we have a really fast internal_memset + if (sizeof(uptr) == 8 && + (n % 8) == 0 && + ((uptr)ptr % 8) == 0 && + (c == 0 || c == -1)) { + // Printf("memset %p %zd %x\n", ptr, n, c); + uptr to_store = c ? -1L : 0L; + uptr *p = (uptr*)ptr; + for (SIZE_T i = 0; i < n / 8; i++) + p[i] = to_store; + return ptr; + } + return internal_memset(ptr, c, n); +} + +// static +void *fast_memcpy(void *dst, const void *src, SIZE_T n) { + // Same hack as in fast_memset above. + if (sizeof(uptr) == 8 && + (n % 8) == 0 && + ((uptr)dst % 8) == 0 && + ((uptr)src % 8) == 0) { + uptr *d = (uptr*)dst; + uptr *s = (uptr*)src; + for (SIZE_T i = 0; i < n / 8; i++) + d[i] = s[i]; + return dst; + } + return internal_memcpy(dst, src, n); +} + +// These interface functions reside here so that they can use +// fast_memset, etc. +void __msan_unpoison(const void *a, uptr size) { + if (!MEM_IS_APP(a)) return; + fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size); +} + +void __msan_poison(const void *a, uptr size) { + if (!MEM_IS_APP(a)) return; + fast_memset((void*)MEM_TO_SHADOW((uptr)a), + __msan::flags()->poison_heap_with_zeroes ? 0 : -1, size); +} + +void __msan_poison_stack(void *a, uptr size) { + if (!MEM_IS_APP(a)) return; + fast_memset((void*)MEM_TO_SHADOW((uptr)a), + __msan::flags()->poison_stack_with_zeroes ? 0 : -1, size); +} + +void __msan_clear_and_unpoison(void *a, uptr size) { + fast_memset(a, 0, size); + fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size); +} + +u32 get_origin_if_poisoned(uptr a, uptr size) { + unsigned char *s = (unsigned char *)MEM_TO_SHADOW(a); + for (uptr i = 0; i < size; ++i) + if (s[i]) + return *(u32 *)SHADOW_TO_ORIGIN((s + i) & ~3UL); + return 0; +} + +void __msan_copy_origin(void *dst, const void *src, uptr size) { + if (!__msan_get_track_origins()) return; + if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; + uptr d = (uptr)dst; + uptr beg = d & ~3UL; + // Copy left unaligned origin if that memory is poisoned. + if (beg < d) { + u32 o = get_origin_if_poisoned(beg, d - beg); + if (o) + *(u32 *)MEM_TO_ORIGIN(beg) = o; + beg += 4; + } + + uptr end = (d + size + 3) & ~3UL; + // Copy right unaligned origin if that memory is poisoned. + if (end > d + size) { + u32 o = get_origin_if_poisoned(d + size, end - d - size); + if (o) + *(u32 *)MEM_TO_ORIGIN(end - 4) = o; + end -= 4; + } + + if (beg < end) { + // Align src up. + uptr s = ((uptr)src + 3) & ~3UL; + fast_memcpy((void*)MEM_TO_ORIGIN(beg), (void*)MEM_TO_ORIGIN(s), end - beg); + } +} + +void __msan_copy_poison(void *dst, const void *src, uptr size) { + if (!MEM_IS_APP(dst)) return; + if (!MEM_IS_APP(src)) return; + fast_memcpy((void*)MEM_TO_SHADOW((uptr)dst), + (void*)MEM_TO_SHADOW((uptr)src), size); + __msan_copy_origin(dst, src, size); +} + +void __msan_move_poison(void *dst, const void *src, uptr size) { + if (!MEM_IS_APP(dst)) return; + if (!MEM_IS_APP(src)) return; + internal_memmove((void*)MEM_TO_SHADOW((uptr)dst), + (void*)MEM_TO_SHADOW((uptr)src), size); + __msan_copy_origin(dst, src, size); +} + +void *__msan_memcpy(void *dest, const void *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + void *res = fast_memcpy(dest, src, n); + __msan_copy_poison(dest, src, n); + return res; +} + +void *__msan_memset(void *s, int c, SIZE_T n) { + ENSURE_MSAN_INITED(); + void *res = fast_memset(s, c, n); + __msan_unpoison(s, n); + return res; +} + +void *__msan_memmove(void *dest, const void *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + void *res = REAL(memmove)(dest, src, n); + __msan_move_poison(dest, src, n); + return res; +} + +namespace __msan { +void InitializeInterceptors() { + static int inited = 0; + CHECK_EQ(inited, 0); + SANITIZER_COMMON_INTERCEPTORS_INIT; + + INTERCEPT_FUNCTION(mmap); + INTERCEPT_FUNCTION(mmap64); + INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(pvalloc); + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(realloc); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(fread); + INTERCEPT_FUNCTION(fread_unlocked); + INTERCEPT_FUNCTION(readlink); + INTERCEPT_FUNCTION(memcpy); + INTERCEPT_FUNCTION(memccpy); + INTERCEPT_FUNCTION(mempcpy); + INTERCEPT_FUNCTION(memset); + INTERCEPT_FUNCTION(memmove); + INTERCEPT_FUNCTION(bcopy); + INTERCEPT_FUNCTION(wmemset); + INTERCEPT_FUNCTION(wmemcpy); + INTERCEPT_FUNCTION(wmempcpy); + INTERCEPT_FUNCTION(wmemmove); + INTERCEPT_FUNCTION(strcpy); // NOLINT + INTERCEPT_FUNCTION(stpcpy); // NOLINT + INTERCEPT_FUNCTION(strdup); + INTERCEPT_FUNCTION(__strdup); + INTERCEPT_FUNCTION(strndup); + INTERCEPT_FUNCTION(__strndup); + INTERCEPT_FUNCTION(strncpy); // NOLINT + INTERCEPT_FUNCTION(strlen); + INTERCEPT_FUNCTION(strnlen); + INTERCEPT_FUNCTION(gcvt); + INTERCEPT_FUNCTION(strcat); // NOLINT + INTERCEPT_FUNCTION(strncat); // NOLINT + INTERCEPT_FUNCTION(strtol); + INTERCEPT_FUNCTION(strtoll); + INTERCEPT_FUNCTION(strtoul); + INTERCEPT_FUNCTION(strtoull); + INTERCEPT_FUNCTION(strtod); + INTERCEPT_FUNCTION(strtod_l); + INTERCEPT_FUNCTION(__strtod_l); + INTERCEPT_FUNCTION(strtof); + INTERCEPT_FUNCTION(strtof_l); + INTERCEPT_FUNCTION(__strtof_l); + INTERCEPT_FUNCTION(strtold); + INTERCEPT_FUNCTION(strtold_l); + INTERCEPT_FUNCTION(__strtold_l); + INTERCEPT_FUNCTION(vasprintf); + INTERCEPT_FUNCTION(asprintf); + INTERCEPT_FUNCTION(vsprintf); + INTERCEPT_FUNCTION(vsnprintf); + INTERCEPT_FUNCTION(vswprintf); + INTERCEPT_FUNCTION(sprintf); // NOLINT + INTERCEPT_FUNCTION(snprintf); + INTERCEPT_FUNCTION(swprintf); + INTERCEPT_FUNCTION(strftime); + INTERCEPT_FUNCTION(mbtowc); + INTERCEPT_FUNCTION(mbrtowc); + INTERCEPT_FUNCTION(wcslen); + INTERCEPT_FUNCTION(wcschr); + INTERCEPT_FUNCTION(wcscpy); + INTERCEPT_FUNCTION(wcscmp); + INTERCEPT_FUNCTION(wcstod); + INTERCEPT_FUNCTION(getenv); + INTERCEPT_FUNCTION(setenv); + INTERCEPT_FUNCTION(putenv); + INTERCEPT_FUNCTION(gettimeofday); + INTERCEPT_FUNCTION(fcvt); + INTERCEPT_FUNCTION(__fxstat); + INTERCEPT_FUNCTION(__fxstatat); + INTERCEPT_FUNCTION(__xstat); + INTERCEPT_FUNCTION(__lxstat); + INTERCEPT_FUNCTION(__fxstat64); + INTERCEPT_FUNCTION(__fxstatat64); + INTERCEPT_FUNCTION(__xstat64); + INTERCEPT_FUNCTION(__lxstat64); + INTERCEPT_FUNCTION(pipe); + INTERCEPT_FUNCTION(pipe2); + INTERCEPT_FUNCTION(socketpair); + INTERCEPT_FUNCTION(fgets); + INTERCEPT_FUNCTION(fgets_unlocked); + INTERCEPT_FUNCTION(getrlimit); + INTERCEPT_FUNCTION(getrlimit64); + INTERCEPT_FUNCTION(uname); + INTERCEPT_FUNCTION(gethostname); + INTERCEPT_FUNCTION(epoll_wait); + INTERCEPT_FUNCTION(epoll_pwait); + INTERCEPT_FUNCTION(recv); + INTERCEPT_FUNCTION(recvfrom); + INTERCEPT_FUNCTION(dladdr); + INTERCEPT_FUNCTION(dlerror); + INTERCEPT_FUNCTION(dlopen); + INTERCEPT_FUNCTION(dl_iterate_phdr); + INTERCEPT_FUNCTION(getrusage); + INTERCEPT_FUNCTION(sigaction); + INTERCEPT_FUNCTION(signal); + INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_key_create); + INTERCEPT_FUNCTION(pthread_join); + INTERCEPT_FUNCTION(tzset); + INTERCEPT_FUNCTION(__cxa_atexit); + INTERCEPT_FUNCTION(shmat); + + if (REAL(pthread_key_create)(&g_thread_finalize_key, &thread_finalize)) { + Printf("MemorySanitizer: failed to create thread key\n"); + Die(); + } + + inited = 1; +} +} // namespace __msan diff --git a/projects/compiler-rt/lib/msan/msan_interface_internal.h b/projects/compiler-rt/lib/msan/msan_interface_internal.h new file mode 100644 index 00000000..794b3540 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_interface_internal.h @@ -0,0 +1,175 @@ +//===-- msan_interface_internal.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Private MSan interface header. +//===----------------------------------------------------------------------===// + +#ifndef MSAN_INTERFACE_INTERNAL_H +#define MSAN_INTERFACE_INTERNAL_H + +#include "sanitizer_common/sanitizer_internal_defs.h" + +extern "C" { +// FIXME: document all interface functions. + +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_get_track_origins(); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_init(); + +// Print a warning and maybe return. +// This function can die based on flags()->exit_code. +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_warning(); + +// Print a warning and die. +// Intrumentation inserts calls to this function when building in "fast" mode +// (i.e. -mllvm -msan-keep-going) +SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) +void __msan_warning_noreturn(); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_unpoison(const void *a, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_clear_and_unpoison(void *a, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void* __msan_memcpy(void *dst, const void *src, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void* __msan_memset(void *s, int c, uptr n); +SANITIZER_INTERFACE_ATTRIBUTE +void* __msan_memmove(void* dest, const void* src, uptr n); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_copy_poison(void *dst, const void *src, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_copy_origin(void *dst, const void *src, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_move_poison(void *dst, const void *src, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_poison(const void *a, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_poison_stack(void *a, uptr size); + +// Copy size bytes from src to dst and unpoison the result. +// Useful to implement unsafe loads. +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_load_unpoisoned(void *src, uptr size, void *dst); + +// Returns the offset of the first (at least partially) poisoned byte, +// or -1 if the whole range is good. +SANITIZER_INTERFACE_ATTRIBUTE +sptr __msan_test_shadow(const void *x, uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_origin(const void *a, uptr size, u32 origin); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_alloca_origin(void *a, uptr size, const char *descr); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc); +SANITIZER_INTERFACE_ATTRIBUTE +u32 __msan_get_origin(const void *a); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_clear_on_return(); + +// Default: -1 (don't exit on error). +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_exit_code(int exit_code); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_keep_going(int keep_going); + +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_set_poison_in_malloc(int do_poison); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +/* OPTIONAL */ const char* __msan_default_options(); + +// For testing. +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_expect_umr(int expect_umr); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_print_shadow(const void *x, uptr size); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_print_param_shadow(); +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_has_dynamic_component(); + +// Returns x such that %fs:x is the first byte of __msan_retval_tls. +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_get_retval_tls_offset(); +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_get_param_tls_offset(); + +// For intercepting mmap from ld.so in msandr. +SANITIZER_INTERFACE_ATTRIBUTE +bool __msan_is_in_loader(); + +// For testing. +SANITIZER_INTERFACE_ATTRIBUTE +u32 __msan_get_umr_origin(); +SANITIZER_INTERFACE_ATTRIBUTE +const char *__msan_get_origin_descr_if_stack(u32 id); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_partial_poison(const void* data, void* shadow, uptr size); + +// Tell MSan about newly allocated memory (ex.: custom allocator). +// Memory will be marked uninitialized, with origin at the call site. +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_allocated_memory(const void* data, uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const uu16 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *p, u16 x); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *p, u32 x); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(uu64 *p, u64 x); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_estimated_allocated_size(uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_get_ownership(const void *p); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_allocated_size(const void *p); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_current_allocated_bytes(); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_heap_size(); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_free_bytes(); + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __msan_get_unmapped_bytes(); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +/* OPTIONAL */ void __msan_malloc_hook(void *ptr, uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +/* OPTIONAL */ void __msan_free_hook(void *ptr); +} // extern "C" + +#endif // MSAN_INTERFACE_INTERNAL_H diff --git a/projects/compiler-rt/lib/msan/msan_linux.cc b/projects/compiler-rt/lib/msan/msan_linux.cc new file mode 100644 index 00000000..46f501e4 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_linux.cc @@ -0,0 +1,102 @@ +//===-- msan_linux.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Linux-specific code. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "msan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +namespace __msan { + +static const uptr kMemBeg = 0x600000000000; +static const uptr kMemEnd = 0x7fffffffffff; +static const uptr kShadowBeg = MEM_TO_SHADOW(kMemBeg); +static const uptr kShadowEnd = MEM_TO_SHADOW(kMemEnd); +static const uptr kBad1Beg = 0x100000000; // 4G +static const uptr kBad1End = kShadowBeg - 1; +static const uptr kBad2Beg = kShadowEnd + 1; +static const uptr kBad2End = kMemBeg - 1; +static const uptr kOriginsBeg = kBad2Beg; +static const uptr kOriginsEnd = kBad2End; + +bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins) { + if ((uptr) & InitShadow < kMemBeg) { + Printf("FATAL: Code below application range: %p < %p. Non-PIE build?\n", + &InitShadow, (void *)kMemBeg); + return false; + } + + if (common_flags()->verbosity) { + Printf("__msan_init %p\n", &__msan_init); + Printf("Memory : %p %p\n", kMemBeg, kMemEnd); + Printf("Bad2 : %p %p\n", kBad2Beg, kBad2End); + Printf("Origins : %p %p\n", kOriginsBeg, kOriginsEnd); + Printf("Shadow : %p %p\n", kShadowBeg, kShadowEnd); + Printf("Bad1 : %p %p\n", kBad1Beg, kBad1End); + } + + if (!MemoryRangeIsAvailable(kShadowBeg, + init_origins ? kOriginsEnd : kShadowEnd)) { + Printf("FATAL: Shadow memory range is not available.\n"); + return false; + } + + if (prot1 && !Mprotect(kBad1Beg, kBad1End - kBad1Beg)) + return false; + if (prot2 && !Mprotect(kBad2Beg, kBad2End - kBad2Beg)) + return false; + if (map_shadow) { + void *shadow = MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg); + if (shadow != (void*)kShadowBeg) return false; + } + if (init_origins) { + void *origins = MmapFixedNoReserve(kOriginsBeg, kOriginsEnd - kOriginsBeg); + if (origins != (void*)kOriginsBeg) return false; + } + return true; +} + +void MsanDie() { + _exit(flags()->exit_code); +} + +static void MsanAtExit(void) { + if (msan_report_count > 0) { + ReportAtExitStatistics(); + if (flags()->exit_code) + _exit(flags()->exit_code); + } +} + +void InstallAtExitHandler() { + atexit(MsanAtExit); +} + +} // namespace __msan + +#endif // __linux__ diff --git a/projects/compiler-rt/lib/msan/msan_new_delete.cc b/projects/compiler-rt/lib/msan/msan_new_delete.cc new file mode 100644 index 00000000..17687ddf --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_new_delete.cc @@ -0,0 +1,56 @@ +//===-- msan_new_delete.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Interceptors for operators new and delete. +//===----------------------------------------------------------------------===// + +#include "msan.h" + +#if MSAN_REPLACE_OPERATORS_NEW_AND_DELETE + +#include + +namespace __msan { +// This function is a no-op. We need it to make sure that object file +// with our replacements will actually be loaded from static MSan +// run-time library at link-time. +void ReplaceOperatorsNewAndDelete() { } +} + +using namespace __msan; // NOLINT + +// Fake std::nothrow_t to avoid including . +namespace std { + struct nothrow_t {}; +} // namespace std + + +#define OPERATOR_NEW_BODY \ + GET_MALLOC_STACK_TRACE; \ + return MsanReallocate(&stack, 0, size, sizeof(u64), false) + +void *operator new(size_t size) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size) { OPERATOR_NEW_BODY; } +void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } + +#define OPERATOR_DELETE_BODY \ + GET_MALLOC_STACK_TRACE; \ + if (ptr) MsanDeallocate(&stack, ptr) + +void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } +void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY; +} + +#endif // MSAN_REPLACE_OPERATORS_NEW_AND_DELETE diff --git a/projects/compiler-rt/lib/msan/msan_report.cc b/projects/compiler-rt/lib/msan/msan_report.cc new file mode 100644 index 00000000..e3ef9930 --- /dev/null +++ b/projects/compiler-rt/lib/msan/msan_report.cc @@ -0,0 +1,101 @@ +//===-- msan_report.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Error reporting. +//===----------------------------------------------------------------------===// + +#include "msan.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +using namespace __sanitizer; + +namespace __msan { + +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *Origin() { return Magenta(); } + const char *Name() { return Green(); } + const char *End() { return Default(); } +}; + +static void DescribeOrigin(u32 origin) { + Decorator d; + if (common_flags()->verbosity) + Printf(" raw origin id: %d\n", origin); + uptr pc; + if (const char *so = GetOriginDescrIfStack(origin, &pc)) { + char* s = internal_strdup(so); + char* sep = internal_strchr(s, '@'); + CHECK(sep); + *sep = '\0'; + Printf("%s", d.Origin()); + Printf(" %sUninitialized value was created by an allocation of '%s%s%s'" + " in the stack frame of function '%s%s%s'%s\n", + d.Origin(), d.Name(), s, d.Origin(), d.Name(), + Symbolizer::Get()->Demangle(sep + 1), d.Origin(), d.End()); + InternalFree(s); + + if (pc) { + // For some reason function address in LLVM IR is 1 less then the address + // of the first instruction. + pc += 1; + StackTrace::PrintStack(&pc, 1); + } + } else { + uptr size = 0; + const uptr *trace = StackDepotGet(origin, &size); + Printf(" %sUninitialized value was created by a heap allocation%s\n", + d.Origin(), d.End()); + StackTrace::PrintStack(trace, size); + } +} + +void ReportUMR(StackTrace *stack, u32 origin) { + if (!__msan::flags()->report_umrs) return; + + SpinMutexLock l(&CommonSanitizerReportMutex); + + Decorator d; + Printf("%s", d.Warning()); + Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n"); + Printf("%s", d.End()); + StackTrace::PrintStack(stack->trace, stack->size); + if (origin) { + DescribeOrigin(origin); + } + ReportErrorSummary("use-of-uninitialized-value", stack); +} + +void ReportExpectedUMRNotFound(StackTrace *stack) { + SpinMutexLock l(&CommonSanitizerReportMutex); + + Printf(" WARNING: Expected use of uninitialized value not found\n"); + StackTrace::PrintStack(stack->trace, stack->size); +} + +void ReportAtExitStatistics() { + SpinMutexLock l(&CommonSanitizerReportMutex); + + Decorator d; + Printf("%s", d.Warning()); + Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count); + Printf("%s", d.End()); +} + +} // namespace __msan diff --git a/projects/compiler-rt/lib/msan/tests/CMakeLists.txt b/projects/compiler-rt/lib/msan/tests/CMakeLists.txt new file mode 100644 index 00000000..9c49f167 --- /dev/null +++ b/projects/compiler-rt/lib/msan/tests/CMakeLists.txt @@ -0,0 +1,172 @@ +include(CheckCXXCompilerFlag) +include(CompilerRTCompile) +include(CompilerRTLink) + +include_directories(..) +include_directories(../..) + +# Instrumented libcxx sources and build flags. +file(GLOB MSAN_LIBCXX_SOURCES ${MSAN_LIBCXX_PATH}/src/*.cpp) +set(MSAN_LIBCXX_CFLAGS + -I${MSAN_LIBCXX_PATH}/include + -fsanitize=memory + -fsanitize-memory-track-origins + -fPIC + -Wno-\#warnings + -g + -O2 + -std=c++0x + -fstrict-aliasing + -fno-exceptions + -nostdinc++ + -fno-omit-frame-pointer + -mno-omit-leaf-frame-pointer) +set(MSAN_LIBCXX_LINK_FLAGS + -nodefaultlibs + -lpthread + -lrt + -lc + -lstdc++ + -fsanitize=memory) + +# Unittest sources and build flags. +set(MSAN_UNITTEST_SOURCES msan_test.cc msan_test_main.cc) +set(MSAN_LOADABLE_SOURCE msan_loadable.cc) +set(MSAN_UNITTEST_HEADERS + msan_test_config.h + msandr_test_so.h + ../../../include/sanitizer/msan_interface.h +) +set(MSANDR_UNITTEST_SOURCE msandr_test_so.cc) +set(MSAN_UNITTEST_COMMON_CFLAGS + -I${MSAN_LIBCXX_PATH}/include + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/msan + -std=c++0x + -stdlib=libc++ + -g + -O2 + -fno-exceptions + -fno-omit-frame-pointer + -mno-omit-leaf-frame-pointer + -Wno-deprecated-declarations +) +set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS + ${MSAN_UNITTEST_COMMON_CFLAGS} + -fsanitize=memory + -fsanitize-memory-track-origins + -mllvm -msan-keep-going=1 +) +set(MSAN_UNITTEST_LINK_FLAGS + -fsanitize=memory + -ldl + # FIXME: we build libcxx without cxxabi and need libstdc++ to provide it. + -lstdc++ +) +set(MSAN_LOADABLE_LINK_FLAGS + -fsanitize=memory + -shared +) + +# Compile source for the given architecture, using compiler +# options in ${ARGN}, and add it to the object list. +macro(msan_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + clang_compile(${output_obj} ${source} + CFLAGS ${ARGN} ${TARGET_CFLAGS} + DEPS gtest ${MSAN_RUNTIME_LIBRARIES} ${MSAN_UNITTEST_HEADERS}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +macro(msan_link_shared so_list so_name arch) + parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN}) + set(output_so "${CMAKE_CURRENT_BINARY_DIR}/${so_name}.${arch}.so") + get_target_flags_for_arch(${arch} TARGET_LINKFLAGS) + clang_link_shared(${output_so} + OBJECTS ${SOURCE_OBJECTS} + LINKFLAGS ${TARGET_LINKFLAGS} ${SOURCE_LINKFLAGS} + DEPS ${SOURCE_DEPS}) + list(APPEND ${so_list} ${output_so}) +endmacro() + +# Link MSan unit test for a given architecture from a set +# of objects in ${ARGN}. +macro(add_msan_test test_suite test_name arch) + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(${test_suite} ${test_name} + OBJECTS ${ARGN} + DEPS ${MSAN_RUNTIME_LIBRARIES} ${ARGN} + ${MSAN_LOADABLE_SO} + LINK_FLAGS ${MSAN_UNITTEST_LINK_FLAGS} + ${TARGET_LINK_FLAGS} + "-Wl,-rpath=${CMAKE_CURRENT_BINARY_DIR}") +endmacro() + +# Main MemorySanitizer unit tests. +add_custom_target(MsanUnitTests) +set_target_properties(MsanUnitTests PROPERTIES FOLDER "MSan unit tests") + +# Adds MSan unit tests and benchmarks for architecture. +macro(add_msan_tests_for_arch arch) + # Build gtest instrumented with MSan. + set(MSAN_INST_GTEST) + msan_compile(MSAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${arch} + ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS}) + + # Build libcxx instrumented with MSan. + set(MSAN_INST_LIBCXX_OBJECTS) + foreach(SOURCE ${MSAN_LIBCXX_SOURCES}) + msan_compile(MSAN_INST_LIBCXX_OBJECTS ${SOURCE} ${arch} + ${MSAN_LIBCXX_CFLAGS}) + endforeach(SOURCE) + + set(MSAN_INST_LIBCXX) + msan_link_shared(MSAN_INST_LIBCXX "libcxx" ${arch} + OBJECTS ${MSAN_INST_LIBCXX_OBJECTS} + LINKFLAGS ${MSAN_LIBCXX_LINK_FLAGS} + DEPS ${MSAN_INST_LIBCXX_OBJECTS} ${MSAN_RUNTIME_LIBRARIES}) + + # Instrumented tests. + set(MSAN_INST_TEST_OBJECTS) + foreach (SOURCE ${MSAN_UNITTEST_SOURCES}) + msan_compile(MSAN_INST_TEST_OBJECTS ${SOURCE} ${arch} + ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS}) + endforeach(SOURCE) + + # Instrumented loadable module objects. + set(MSAN_INST_LOADABLE_OBJECTS) + msan_compile(MSAN_INST_LOADABLE_OBJECTS ${MSAN_LOADABLE_SOURCE} ${arch} + ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS}) + + # Uninstrumented shared object for MSanDR tests. + set(MSANDR_TEST_OBJECTS) + msan_compile(MSANDR_TEST_OBJECTS ${MSANDR_UNITTEST_SOURCE} ${arch} + ${MSAN_UNITTEST_COMMON_CFLAGS}) + + # Instrumented loadable library tests. + set(MSAN_LOADABLE_SO) + msan_link_shared(MSAN_LOADABLE_SO "libmsan_loadable" ${arch} + OBJECTS ${MSAN_INST_LOADABLE_OBJECTS} + DEPS ${MSAN_INST_LOADABLE_OBJECTS} ${MSAN_RUNTIME_LIBRARIES}) + + # Uninstrumented shared library tests. + set(MSANDR_TEST_SO) + msan_link_shared(MSANDR_TEST_SO "libmsandr_test" ${arch} + OBJECTS ${MSANDR_TEST_OBJECTS} + DEPS ${MSANDR_TEST_OBJECTS} ${MSAN_RUNTIME_LIBRARIES}) + + # Link everything together. + add_msan_test(MsanUnitTests "Msan-${arch}-Test" ${arch} + ${MSAN_INST_TEST_OBJECTS} ${MSAN_INST_GTEST} + ${MSAN_INST_LIBCXX} ${MSANDR_TEST_SO}) +endmacro() + +if(COMPILER_RT_CAN_EXECUTE_TESTS AND MSAN_CAN_INSTRUMENT_LIBCXX) + if(CAN_TARGET_x86_64) + add_msan_tests_for_arch(x86_64) + endif() +endif() diff --git a/projects/compiler-rt/lib/msan/tests/msan_loadable.cc b/projects/compiler-rt/lib/msan/tests/msan_loadable.cc new file mode 100644 index 00000000..db3bf489 --- /dev/null +++ b/projects/compiler-rt/lib/msan/tests/msan_loadable.cc @@ -0,0 +1,45 @@ +//===-- msan_loadable.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer unit tests. +//===----------------------------------------------------------------------===// + +#include "msan/msan_interface_internal.h" +#include + +static void *dso_global; + +// No name mangling. +extern "C" { + +__attribute__((constructor)) +void loadable_module_init(void) { + if (!__msan_has_dynamic_component()) + return; + // The real test is that this compare should not make an uninit. + if (dso_global == NULL) + dso_global = malloc(4); +} + +__attribute__((destructor)) +void loadable_module_fini(void) { + if (!__msan_has_dynamic_component()) + return; + free(dso_global); + // *Don't* overwrite it with NULL! That would unpoison it, but our test + // relies on reloading at the same address and keeping the poison. +} + +void **get_dso_global() { + return &dso_global; +} + +} diff --git a/projects/compiler-rt/lib/msan/tests/msan_test.cc b/projects/compiler-rt/lib/msan/tests/msan_test.cc new file mode 100644 index 00000000..f95bb4e7 --- /dev/null +++ b/projects/compiler-rt/lib/msan/tests/msan_test.cc @@ -0,0 +1,3561 @@ +//===-- msan_test.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer unit tests. +//===----------------------------------------------------------------------===// + +#ifndef MSAN_EXTERNAL_TEST_CONFIG +#include "msan_test_config.h" +#endif // MSAN_EXTERNAL_TEST_CONFIG + +#include "sanitizer/msan_interface.h" +#include "msandr_test_so.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__i386__) || defined(__x86_64__) +# include +# define MSAN_HAS_M128 1 +#else +# define MSAN_HAS_M128 0 +#endif + +static const int kPageSize = 4096; + +typedef unsigned char U1; +typedef unsigned short U2; // NOLINT +typedef unsigned int U4; +typedef unsigned long long U8; // NOLINT +typedef signed char S1; +typedef signed short S2; // NOLINT +typedef signed int S4; +typedef signed long long S8; // NOLINT +#define NOINLINE __attribute__((noinline)) +#define INLINE __attribute__((always_inline)) + +static bool TrackingOrigins() { + S8 x; + __msan_set_origin(&x, sizeof(x), 0x1234); + U4 origin = __msan_get_origin(&x); + __msan_set_origin(&x, sizeof(x), 0); + return origin == 0x1234; +} + +#define EXPECT_UMR(action) \ + do { \ + __msan_set_expect_umr(1); \ + action; \ + __msan_set_expect_umr(0); \ + } while (0) + +#define EXPECT_UMR_O(action, origin) \ + do { \ + __msan_set_expect_umr(1); \ + action; \ + __msan_set_expect_umr(0); \ + if (TrackingOrigins()) \ + EXPECT_EQ(origin, __msan_get_umr_origin()); \ + } while (0) + +#define EXPECT_UMR_S(action, stack_origin) \ + do { \ + __msan_set_expect_umr(1); \ + action; \ + __msan_set_expect_umr(0); \ + U4 id = __msan_get_umr_origin(); \ + const char *str = __msan_get_origin_descr_if_stack(id); \ + if (!str || strcmp(str, stack_origin)) { \ + fprintf(stderr, "EXPECT_POISONED_S: id=%u %s, %s", \ + id, stack_origin, str); \ + EXPECT_EQ(1, 0); \ + } \ + } while (0) + +#define EXPECT_POISONED(x) ExpectPoisoned(x) + +template +void ExpectPoisoned(const T& t) { + EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); +} + +#define EXPECT_POISONED_O(x, origin) \ + ExpectPoisonedWithOrigin(x, origin) + +template +void ExpectPoisonedWithOrigin(const T& t, unsigned origin) { + EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); + if (TrackingOrigins()) + EXPECT_EQ(origin, __msan_get_origin((void*)&t)); +} + +#define EXPECT_POISONED_S(x, stack_origin) \ + ExpectPoisonedWithStackOrigin(x, stack_origin) + +template +void ExpectPoisonedWithStackOrigin(const T& t, const char *stack_origin) { + EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); + U4 id = __msan_get_origin((void*)&t); + const char *str = __msan_get_origin_descr_if_stack(id); + if (!str || strcmp(str, stack_origin)) { + fprintf(stderr, "EXPECT_POISONED_S: id=%u %s, %s", + id, stack_origin, str); + EXPECT_EQ(1, 0); + } +} + +#define EXPECT_NOT_POISONED(x) ExpectNotPoisoned(x) + +template +void ExpectNotPoisoned(const T& t) { + EXPECT_EQ(-1, __msan_test_shadow((void*)&t, sizeof(t))); +} + +static U8 poisoned_array[100]; +template +T *GetPoisoned(int i = 0, T val = 0) { + T *res = (T*)&poisoned_array[i]; + *res = val; + __msan_poison(&poisoned_array[i], sizeof(T)); + return res; +} + +template +T *GetPoisonedO(int i, U4 origin, T val = 0) { + T *res = (T*)&poisoned_array[i]; + *res = val; + __msan_poison(&poisoned_array[i], sizeof(T)); + __msan_set_origin(&poisoned_array[i], sizeof(T), origin); + return res; +} + +// This function returns its parameter but in such a way that compiler +// can not prove it. +template +NOINLINE +static T Ident(T t) { + volatile T ret = t; + return ret; +} + +template NOINLINE T ReturnPoisoned() { return *GetPoisoned(); } + +static volatile int g_one = 1; +static volatile int g_zero = 0; +static volatile int g_0 = 0; +static volatile int g_1 = 1; + +S4 a_s4[100]; +S8 a_s8[100]; + +// Check that malloc poisons memory. +// A lot of tests below depend on this. +TEST(MemorySanitizerSanity, PoisonInMalloc) { + int *x = (int*)malloc(sizeof(int)); + EXPECT_POISONED(*x); + free(x); +} + +TEST(MemorySanitizer, NegativeTest1) { + S4 *x = GetPoisoned(); + if (g_one) + *x = 0; + EXPECT_NOT_POISONED(*x); +} + +TEST(MemorySanitizer, PositiveTest1) { + // Load to store. + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + + // S->S conversions. + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + + // ZExt + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(*GetPoisoned()); + + // Unary ops. + EXPECT_POISONED(- *GetPoisoned()); + + EXPECT_UMR(a_s4[g_zero] = 100 / *GetPoisoned(0, 1)); + + + a_s4[g_zero] = 1 - *GetPoisoned(); + a_s4[g_zero] = 1 + *GetPoisoned(); +} + +TEST(MemorySanitizer, Phi1) { + S4 c; + if (g_one) { + c = *GetPoisoned(); + } else { + break_optimization(0); + c = 0; + } + EXPECT_POISONED(c); +} + +TEST(MemorySanitizer, Phi2) { + S4 i = *GetPoisoned(); + S4 n = g_one; + EXPECT_UMR(for (; i < g_one; i++);); + EXPECT_POISONED(i); +} + +NOINLINE void Arg1ExpectUMR(S4 a1) { EXPECT_POISONED(a1); } +NOINLINE void Arg2ExpectUMR(S4 a1, S4 a2) { EXPECT_POISONED(a2); } +NOINLINE void Arg3ExpectUMR(S1 a1, S4 a2, S8 a3) { EXPECT_POISONED(a3); } + +TEST(MemorySanitizer, ArgTest) { + Arg1ExpectUMR(*GetPoisoned()); + Arg2ExpectUMR(0, *GetPoisoned()); + Arg3ExpectUMR(0, 1, *GetPoisoned()); +} + + +TEST(MemorySanitizer, CallAndRet) { + if (!__msan_has_dynamic_component()) return; + ReturnPoisoned(); + ReturnPoisoned(); + ReturnPoisoned(); + ReturnPoisoned(); + + EXPECT_POISONED(ReturnPoisoned()); + EXPECT_POISONED(ReturnPoisoned()); + EXPECT_POISONED(ReturnPoisoned()); + EXPECT_POISONED(ReturnPoisoned()); +} + +// malloc() in the following test may be optimized to produce a compile-time +// undef value. Check that we trap on the volatile assignment anyway. +TEST(MemorySanitizer, DISABLED_MallocNoIdent) { + S4 *x = (int*)malloc(sizeof(S4)); + EXPECT_POISONED(*x); + free(x); +} + +TEST(MemorySanitizer, Malloc) { + S4 *x = (int*)Ident(malloc(sizeof(S4))); + EXPECT_POISONED(*x); + free(x); +} + +TEST(MemorySanitizer, Realloc) { + S4 *x = (int*)Ident(realloc(0, sizeof(S4))); + EXPECT_POISONED(x[0]); + x[0] = 1; + x = (int*)Ident(realloc(x, 2 * sizeof(S4))); + EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. + EXPECT_POISONED(x[1]); + x = (int*)Ident(realloc(x, 3 * sizeof(S4))); + EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. + EXPECT_POISONED(x[2]); + EXPECT_POISONED(x[1]); + x[2] = 1; // Init this here. Check that after realloc it is poisoned again. + x = (int*)Ident(realloc(x, 2 * sizeof(S4))); + EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. + EXPECT_POISONED(x[1]); + x = (int*)Ident(realloc(x, 3 * sizeof(S4))); + EXPECT_POISONED(x[1]); + EXPECT_POISONED(x[2]); + free(x); +} + +TEST(MemorySanitizer, Calloc) { + S4 *x = (int*)Ident(calloc(1, sizeof(S4))); + EXPECT_NOT_POISONED(*x); // Should not be poisoned. + // EXPECT_EQ(0, *x); + free(x); +} + +TEST(MemorySanitizer, AndOr) { + U4 *p = GetPoisoned(); + // We poison two bytes in the midle of a 4-byte word to make the test + // correct regardless of endianness. + ((U1*)p)[1] = 0; + ((U1*)p)[2] = 0xff; + EXPECT_NOT_POISONED(*p & 0x00ffff00); + EXPECT_NOT_POISONED(*p & 0x00ff0000); + EXPECT_NOT_POISONED(*p & 0x0000ff00); + EXPECT_POISONED(*p & 0xff000000); + EXPECT_POISONED(*p & 0x000000ff); + EXPECT_POISONED(*p & 0x0000ffff); + EXPECT_POISONED(*p & 0xffff0000); + + EXPECT_NOT_POISONED(*p | 0xff0000ff); + EXPECT_NOT_POISONED(*p | 0xff00ffff); + EXPECT_NOT_POISONED(*p | 0xffff00ff); + EXPECT_POISONED(*p | 0xff000000); + EXPECT_POISONED(*p | 0x000000ff); + EXPECT_POISONED(*p | 0x0000ffff); + EXPECT_POISONED(*p | 0xffff0000); + + EXPECT_POISONED(*GetPoisoned() & *GetPoisoned()); +} + +template +static bool applyNot(T value, T shadow) { + __msan_partial_poison(&value, &shadow, sizeof(T)); + return !value; +} + +TEST(MemorySanitizer, Not) { + EXPECT_NOT_POISONED(applyNot(0x0, 0x0)); + EXPECT_NOT_POISONED(applyNot(0xFFFFFFFF, 0x0)); + EXPECT_POISONED(applyNot(0xFFFFFFFF, 0xFFFFFFFF)); + EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x0FFFFFFF)); + EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x00FFFFFF)); + EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x0000FFFF)); + EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x00000000)); + EXPECT_POISONED(applyNot(0xFF000000, 0xFF000000)); + EXPECT_NOT_POISONED(applyNot(0xFF800000, 0xFF000000)); + EXPECT_POISONED(applyNot(0x00008000, 0x00008000)); + + EXPECT_NOT_POISONED(applyNot(0x0, 0x0)); + EXPECT_NOT_POISONED(applyNot(0xFF, 0xFE)); + EXPECT_NOT_POISONED(applyNot(0xFF, 0x0)); + EXPECT_POISONED(applyNot(0xFF, 0xFF)); + + EXPECT_POISONED(applyNot((void*)0xFFFFFF, (void*)(-1))); + EXPECT_NOT_POISONED(applyNot((void*)0xFFFFFF, (void*)(-2))); +} + +TEST(MemorySanitizer, Shift) { + U4 *up = GetPoisoned(); + ((U1*)up)[0] = 0; + ((U1*)up)[3] = 0xff; + EXPECT_NOT_POISONED(*up >> 30); + EXPECT_NOT_POISONED(*up >> 24); + EXPECT_POISONED(*up >> 23); + EXPECT_POISONED(*up >> 10); + + EXPECT_NOT_POISONED(*up << 30); + EXPECT_NOT_POISONED(*up << 24); + EXPECT_POISONED(*up << 23); + EXPECT_POISONED(*up << 10); + + S4 *sp = (S4*)up; + EXPECT_NOT_POISONED(*sp >> 30); + EXPECT_NOT_POISONED(*sp >> 24); + EXPECT_POISONED(*sp >> 23); + EXPECT_POISONED(*sp >> 10); + + sp = GetPoisoned(); + ((S1*)sp)[1] = 0; + ((S1*)sp)[2] = 0; + EXPECT_POISONED(*sp >> 31); + + EXPECT_POISONED(100 >> *GetPoisoned()); + EXPECT_POISONED(100U >> *GetPoisoned()); +} + +NOINLINE static int GetPoisonedZero() { + int *zero = new int; + *zero = 0; + __msan_poison(zero, sizeof(*zero)); + int res = *zero; + delete zero; + return res; +} + +TEST(MemorySanitizer, LoadFromDirtyAddress) { + int *a = new int; + *a = 0; + EXPECT_UMR(break_optimization((void*)(U8)a[GetPoisonedZero()])); + delete a; +} + +TEST(MemorySanitizer, StoreToDirtyAddress) { + int *a = new int; + EXPECT_UMR(a[GetPoisonedZero()] = 0); + break_optimization(a); + delete a; +} + + +NOINLINE void StackTestFunc() { + S4 p4; + S4 ok4 = 1; + S2 p2; + S2 ok2 = 1; + S1 p1; + S1 ok1 = 1; + break_optimization(&p4); + break_optimization(&ok4); + break_optimization(&p2); + break_optimization(&ok2); + break_optimization(&p1); + break_optimization(&ok1); + + EXPECT_POISONED(p4); + EXPECT_POISONED(p2); + EXPECT_POISONED(p1); + EXPECT_NOT_POISONED(ok1); + EXPECT_NOT_POISONED(ok2); + EXPECT_NOT_POISONED(ok4); +} + +TEST(MemorySanitizer, StackTest) { + StackTestFunc(); +} + +NOINLINE void StackStressFunc() { + int foo[10000]; + break_optimization(foo); +} + +TEST(MemorySanitizer, DISABLED_StackStressTest) { + for (int i = 0; i < 1000000; i++) + StackStressFunc(); +} + +template +void TestFloatingPoint() { + static volatile T v; + static T g[100]; + break_optimization(&g); + T *x = GetPoisoned(); + T *y = GetPoisoned(1); + EXPECT_POISONED(*x); + EXPECT_POISONED((long long)*x); + EXPECT_POISONED((int)*x); + g[0] = *x; + g[1] = *x + *y; + g[2] = *x - *y; + g[3] = *x * *y; +} + +TEST(MemorySanitizer, FloatingPointTest) { + TestFloatingPoint(); + TestFloatingPoint(); +} + +TEST(MemorySanitizer, DynMem) { + S4 x = 0; + S4 *y = GetPoisoned(); + memcpy(y, &x, g_one * sizeof(S4)); + EXPECT_NOT_POISONED(*y); +} + +static char *DynRetTestStr; + +TEST(MemorySanitizer, DynRet) { + if (!__msan_has_dynamic_component()) return; + ReturnPoisoned(); + EXPECT_NOT_POISONED(clearenv()); +} + + +TEST(MemorySanitizer, DynRet1) { + if (!__msan_has_dynamic_component()) return; + ReturnPoisoned(); +} + +struct LargeStruct { + S4 x[10]; +}; + +NOINLINE +LargeStruct LargeRetTest() { + LargeStruct res; + res.x[0] = *GetPoisoned(); + res.x[1] = *GetPoisoned(); + res.x[2] = *GetPoisoned(); + res.x[3] = *GetPoisoned(); + res.x[4] = *GetPoisoned(); + res.x[5] = *GetPoisoned(); + res.x[6] = *GetPoisoned(); + res.x[7] = *GetPoisoned(); + res.x[8] = *GetPoisoned(); + res.x[9] = *GetPoisoned(); + return res; +} + +TEST(MemorySanitizer, strcmp) { + char s1[10]; + char s2[10]; + strncpy(s1, "foo", 10); + s2[0] = 'f'; + s2[1] = 'n'; + EXPECT_GT(strcmp(s1, s2), 0); + s2[1] = 'o'; + int res; + EXPECT_UMR(res = strcmp(s1, s2)); + EXPECT_NOT_POISONED(res); + EXPECT_EQ(strncmp(s1, s2, 1), 0); +} + +TEST(MemorySanitizer, LargeRet) { + LargeStruct a = LargeRetTest(); + EXPECT_POISONED(a.x[0]); + EXPECT_POISONED(a.x[9]); +} + +TEST(MemorySanitizer, strerror) { + char *buf = strerror(EINVAL); + EXPECT_NOT_POISONED(strlen(buf)); + buf = strerror(123456); + EXPECT_NOT_POISONED(strlen(buf)); +} + +TEST(MemorySanitizer, strerror_r) { + errno = 0; + char buf[1000]; + char *res = strerror_r(EINVAL, buf, sizeof(buf)); + ASSERT_EQ(0, errno); + if (!res) res = buf; // POSIX version success. + EXPECT_NOT_POISONED(strlen(res)); +} + +TEST(MemorySanitizer, fread) { + char *x = new char[32]; + FILE *f = fopen("/proc/self/stat", "r"); + assert(f); + fread(x, 1, 32, f); + EXPECT_NOT_POISONED(x[0]); + EXPECT_NOT_POISONED(x[16]); + EXPECT_NOT_POISONED(x[31]); + fclose(f); + delete x; +} + +TEST(MemorySanitizer, read) { + char *x = new char[32]; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = read(fd, x, 32); + assert(sz == 32); + EXPECT_NOT_POISONED(x[0]); + EXPECT_NOT_POISONED(x[16]); + EXPECT_NOT_POISONED(x[31]); + close(fd); + delete x; +} + +TEST(MemorySanitizer, pread) { + char *x = new char[32]; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = pread(fd, x, 32, 0); + assert(sz == 32); + EXPECT_NOT_POISONED(x[0]); + EXPECT_NOT_POISONED(x[16]); + EXPECT_NOT_POISONED(x[31]); + close(fd); + delete x; +} + +TEST(MemorySanitizer, readv) { + char buf[2011]; + struct iovec iov[2]; + iov[0].iov_base = buf + 1; + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = readv(fd, iov, 2); + ASSERT_LT(sz, 5 + 2000); + ASSERT_GT(sz, iov[0].iov_len); + EXPECT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[1]); + EXPECT_NOT_POISONED(buf[5]); + EXPECT_POISONED(buf[6]); + EXPECT_POISONED(buf[9]); + EXPECT_NOT_POISONED(buf[10]); + EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); + EXPECT_POISONED(buf[11 + (sz - 1) - 5]); + close(fd); +} + +TEST(MemorySanitizer, preadv) { + char buf[2011]; + struct iovec iov[2]; + iov[0].iov_base = buf + 1; + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = preadv(fd, iov, 2, 3); + ASSERT_LT(sz, 5 + 2000); + ASSERT_GT(sz, iov[0].iov_len); + EXPECT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[1]); + EXPECT_NOT_POISONED(buf[5]); + EXPECT_POISONED(buf[6]); + EXPECT_POISONED(buf[9]); + EXPECT_NOT_POISONED(buf[10]); + EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); + EXPECT_POISONED(buf[11 + (sz - 1) - 5]); + close(fd); +} + +// FIXME: fails now. +TEST(MemorySanitizer, DISABLED_ioctl) { + struct winsize ws; + EXPECT_EQ(ioctl(2, TIOCGWINSZ, &ws), 0); + EXPECT_NOT_POISONED(ws.ws_col); +} + +TEST(MemorySanitizer, readlink) { + char *x = new char[1000]; + readlink("/proc/self/exe", x, 1000); + EXPECT_NOT_POISONED(x[0]); + delete [] x; +} + + +TEST(MemorySanitizer, stat) { + struct stat* st = new struct stat; + int res = stat("/proc/self/stat", st); + assert(!res); + EXPECT_NOT_POISONED(st->st_dev); + EXPECT_NOT_POISONED(st->st_mode); + EXPECT_NOT_POISONED(st->st_size); +} + +TEST(MemorySanitizer, fstatat) { + struct stat* st = new struct stat; + int dirfd = open("/proc/self", O_RDONLY); + assert(dirfd > 0); + int res = fstatat(dirfd, "stat", st, 0); + assert(!res); + EXPECT_NOT_POISONED(st->st_dev); + EXPECT_NOT_POISONED(st->st_mode); + EXPECT_NOT_POISONED(st->st_size); + close(dirfd); +} + +TEST(MemorySanitizer, statfs) { + struct statfs st; + int res = statfs("/", &st); + assert(!res); + EXPECT_NOT_POISONED(st.f_type); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namelen); +} + +TEST(MemorySanitizer, statvfs) { + struct statvfs st; + int res = statvfs("/", &st); + assert(!res); + EXPECT_NOT_POISONED(st.f_bsize); + EXPECT_NOT_POISONED(st.f_blocks); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namemax); +} + +TEST(MemorySanitizer, fstatvfs) { + struct statvfs st; + int fd = open("/", O_RDONLY | O_DIRECTORY); + int res = fstatvfs(fd, &st); + assert(!res); + EXPECT_NOT_POISONED(st.f_bsize); + EXPECT_NOT_POISONED(st.f_blocks); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namemax); + close(fd); +} + +TEST(MemorySanitizer, pipe) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + assert(!res); + EXPECT_NOT_POISONED(pipefd[0]); + EXPECT_NOT_POISONED(pipefd[1]); + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, pipe2) { + int* pipefd = new int[2]; + int res = pipe2(pipefd, O_NONBLOCK); + assert(!res); + EXPECT_NOT_POISONED(pipefd[0]); + EXPECT_NOT_POISONED(pipefd[1]); + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, socketpair) { + int sv[2]; + int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); + assert(!res); + EXPECT_NOT_POISONED(sv[0]); + EXPECT_NOT_POISONED(sv[1]); + close(sv[0]); + close(sv[1]); +} + +TEST(MemorySanitizer, poll) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + char data = 42; + res = write(pipefd[1], &data, 1); + ASSERT_EQ(1, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + fds[1].fd = pipefd[1]; + fds[1].events = POLLIN; + res = poll(fds, 2, 500); + ASSERT_EQ(1, res); + EXPECT_NOT_POISONED(fds[0].revents); + EXPECT_NOT_POISONED(fds[1].revents); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, ppoll) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + char data = 42; + res = write(pipefd[1], &data, 1); + ASSERT_EQ(1, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + fds[1].fd = pipefd[1]; + fds[1].events = POLLIN; + sigset_t ss; + sigemptyset(&ss); + res = ppoll(fds, 2, NULL, &ss); + ASSERT_EQ(1, res); + EXPECT_NOT_POISONED(fds[0].revents); + EXPECT_NOT_POISONED(fds[1].revents); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, poll_positive) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + // fds[1].fd uninitialized + fds[1].events = POLLIN; + EXPECT_UMR(poll(fds, 2, 0)); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, bind_getsockname) { + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_UNIX; + int res = bind(sock, (struct sockaddr *)&sai, sizeof(sai)); + + assert(!res); + char buf[200]; + socklen_t addrlen; + EXPECT_UMR(getsockname(sock, (struct sockaddr *)&buf, &addrlen)); + + addrlen = sizeof(buf); + res = getsockname(sock, (struct sockaddr *)&buf, &addrlen); + EXPECT_NOT_POISONED(addrlen); + EXPECT_NOT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[addrlen - 1]); + EXPECT_POISONED(buf[addrlen]); + close(sock); +} + +TEST(MemorySanitizer, accept) { + int listen_socket = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LT(0, listen_socket); + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int res = bind(listen_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(0, res); + + res = listen(listen_socket, 1); + ASSERT_EQ(0, res); + + socklen_t sz = sizeof(sai); + res = getsockname(listen_socket, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + + int connect_socket = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LT(0, connect_socket); + res = fcntl(connect_socket, F_SETFL, O_NONBLOCK); + ASSERT_EQ(0, res); + res = connect(connect_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(-1, res); + ASSERT_EQ(EINPROGRESS, errno); + + __msan_poison(&sai, sizeof(sai)); + int new_sock = accept(listen_socket, (struct sockaddr *)&sai, &sz); + ASSERT_LT(0, new_sock); + ASSERT_EQ(sizeof(sai), sz); + EXPECT_NOT_POISONED(sai); + + __msan_poison(&sai, sizeof(sai)); + res = getpeername(new_sock, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + EXPECT_NOT_POISONED(sai); + + close(new_sock); + close(connect_socket); + close(listen_socket); +} + +TEST(MemorySanitizer, getaddrinfo) { + struct addrinfo *ai; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + int res = getaddrinfo("localhost", NULL, &hints, &ai); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(*ai); + ASSERT_EQ(sizeof(sockaddr_in), ai->ai_addrlen); + EXPECT_NOT_POISONED(*(sockaddr_in*)ai->ai_addr); +} + +TEST(MemorySanitizer, getnameinfo) { + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 80; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + char host[500]; + char serv[500]; + int res = getnameinfo((struct sockaddr *)&sai, sizeof(sai), host, + sizeof(host), serv, sizeof(serv), 0); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(host[0]); + EXPECT_POISONED(host[sizeof(host) - 1]); + + ASSERT_NE(0, strlen(host)); + EXPECT_NOT_POISONED(serv[0]); + EXPECT_POISONED(serv[sizeof(serv) - 1]); + ASSERT_NE(0, strlen(serv)); +} + +#define EXPECT_HOSTENT_NOT_POISONED(he) \ + do { \ + EXPECT_NOT_POISONED(*(he)); \ + ASSERT_NE((void *) 0, (he)->h_name); \ + ASSERT_NE((void *) 0, (he)->h_aliases); \ + ASSERT_NE((void *) 0, (he)->h_addr_list); \ + EXPECT_NOT_POISONED(strlen((he)->h_name)); \ + char **p = (he)->h_aliases; \ + while (*p) { \ + EXPECT_NOT_POISONED(strlen(*p)); \ + ++p; \ + } \ + char **q = (he)->h_addr_list; \ + while (*q) { \ + EXPECT_NOT_POISONED(*q[0]); \ + ++q; \ + } \ + EXPECT_NOT_POISONED(*q); \ + } while (0) + +TEST(MemorySanitizer, gethostent) { + struct hostent *he = gethostent(); + ASSERT_NE((void *)NULL, he); + EXPECT_HOSTENT_NOT_POISONED(he); +} + +#ifndef MSAN_TEST_DISABLE_GETHOSTBYNAME + +TEST(MemorySanitizer, gethostbyname) { + struct hostent *he = gethostbyname("localhost"); + ASSERT_NE((void *)NULL, he); + EXPECT_HOSTENT_NOT_POISONED(he); +} + +#endif // MSAN_TEST_DISABLE_GETHOSTBYNAME + +TEST(MemorySanitizer, recvmsg) { + int server_socket = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_LT(0, server_socket); + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int res = bind(server_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(0, res); + + socklen_t sz = sizeof(sai); + res = getsockname(server_socket, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + + + int client_socket = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_LT(0, client_socket); + + struct sockaddr_in client_sai; + memset(&client_sai, 0, sizeof(client_sai)); + client_sai.sin_family = AF_INET; + client_sai.sin_port = 0; + client_sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + res = bind(client_socket, (struct sockaddr *)&client_sai, sizeof(client_sai)); + ASSERT_EQ(0, res); + + sz = sizeof(client_sai); + res = getsockname(client_socket, (struct sockaddr *)&client_sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(client_sai), sz); + + + const char *s = "message text"; + struct iovec iov; + iov.iov_base = (void *)s; + iov.iov_len = strlen(s) + 1; + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sai; + msg.msg_namelen = sizeof(sai); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + res = sendmsg(client_socket, &msg, 0); + ASSERT_LT(0, res); + + + char buf[1000]; + struct iovec recv_iov; + recv_iov.iov_base = (void *)&buf; + recv_iov.iov_len = sizeof(buf); + struct sockaddr_in recv_sai; + struct msghdr recv_msg; + memset(&recv_msg, 0, sizeof(recv_msg)); + recv_msg.msg_name = &recv_sai; + recv_msg.msg_namelen = sizeof(recv_sai); + recv_msg.msg_iov = &recv_iov; + recv_msg.msg_iovlen = 1; + res = recvmsg(server_socket, &recv_msg, 0); + ASSERT_LT(0, res); + + ASSERT_EQ(sizeof(recv_sai), recv_msg.msg_namelen); + EXPECT_NOT_POISONED(*(struct sockaddr_in *)recv_msg.msg_name); + EXPECT_STREQ(s, buf); + + close(server_socket); + close(client_socket); +} + +TEST(MemorySanitizer, gethostbyname2) { + struct hostent *he = gethostbyname2("localhost", AF_INET); + ASSERT_NE((void *)NULL, he); + EXPECT_HOSTENT_NOT_POISONED(he); +} + +TEST(MemorySanitizer, gethostbyaddr) { + in_addr_t addr = inet_addr("127.0.0.1"); + EXPECT_NOT_POISONED(addr); + struct hostent *he = gethostbyaddr(&addr, sizeof(addr), AF_INET); + ASSERT_NE((void *)NULL, he); + EXPECT_HOSTENT_NOT_POISONED(he); +} + +TEST(MemorySanitizer, gethostent_r) { + char buf[2000]; + struct hostent he; + struct hostent *result; + int err; + int res = gethostent_r(&he, buf, sizeof(buf), &result, &err); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(result); + ASSERT_NE((void *)NULL, result); + EXPECT_HOSTENT_NOT_POISONED(result); + EXPECT_NOT_POISONED(err); +} + +TEST(MemorySanitizer, gethostbyname_r) { + char buf[2000]; + struct hostent he; + struct hostent *result; + int err; + int res = gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(result); + ASSERT_NE((void *)NULL, result); + EXPECT_HOSTENT_NOT_POISONED(result); + EXPECT_NOT_POISONED(err); +} + +TEST(MemorySanitizer, gethostbyname2_r) { + char buf[2000]; + struct hostent he; + struct hostent *result; + int err; + int res = gethostbyname2_r("localhost", AF_INET, &he, buf, sizeof(buf), + &result, &err); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(result); + ASSERT_NE((void *)NULL, result); + EXPECT_HOSTENT_NOT_POISONED(result); + EXPECT_NOT_POISONED(err); +} + +TEST(MemorySanitizer, gethostbyaddr_r) { + char buf[2000]; + struct hostent he; + struct hostent *result; + int err; + in_addr_t addr = inet_addr("127.0.0.1"); + EXPECT_NOT_POISONED(addr); + int res = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &he, buf, sizeof(buf), + &result, &err); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(result); + ASSERT_NE((void *)NULL, result); + EXPECT_HOSTENT_NOT_POISONED(result); + EXPECT_NOT_POISONED(err); +} + +TEST(MemorySanitizer, getsockopt) { + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + struct linger l[2]; + socklen_t sz = sizeof(l[0]); + int res = getsockopt(sock, SOL_SOCKET, SO_LINGER, &l[0], &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(l[0]), sz); + EXPECT_NOT_POISONED(l[0]); + EXPECT_POISONED(*(char *)(l + 1)); +} + +TEST(MemorySanitizer, getcwd) { + char path[PATH_MAX + 1]; + char* res = getcwd(path, sizeof(path)); + assert(res); + EXPECT_NOT_POISONED(path[0]); +} + +TEST(MemorySanitizer, getcwd_gnu) { + char* res = getcwd(NULL, 0); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +TEST(MemorySanitizer, get_current_dir_name) { + char* res = get_current_dir_name(); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +TEST(MemorySanitizer, shmctl) { + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + ASSERT_GT(id, -1); + + struct shmid_ds ds; + int res = shmctl(id, IPC_STAT, &ds); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(ds); + + struct shminfo si; + res = shmctl(id, IPC_INFO, (struct shmid_ds *)&si); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(si); + + struct shm_info s_i; + res = shmctl(id, SHM_INFO, (struct shmid_ds *)&s_i); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(s_i); + + res = shmctl(id, IPC_RMID, 0); + ASSERT_GT(res, -1); +} + +TEST(MemorySanitizer, shmat) { + void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + ASSERT_NE(MAP_FAILED, p); + + ((char *)p)[10] = *GetPoisoned(); + ((char *)p)[4095] = *GetPoisoned(); + + int res = munmap(p, 4096); + ASSERT_EQ(0, res); + + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + ASSERT_GT(id, -1); + + void *q = shmat(id, p, 0); + ASSERT_EQ(p, q); + + EXPECT_NOT_POISONED(((char *)q)[0]); + EXPECT_NOT_POISONED(((char *)q)[10]); + EXPECT_NOT_POISONED(((char *)q)[4095]); + + res = shmdt(q); + ASSERT_EQ(0, res); + + res = shmctl(id, IPC_RMID, 0); + ASSERT_GT(res, -1); +} + +TEST(MemorySanitizer, random_r) { + int32_t x; + char z[64]; + memset(z, 0, sizeof(z)); + + struct random_data buf; + memset(&buf, 0, sizeof(buf)); + + int res = initstate_r(0, z, sizeof(z), &buf); + ASSERT_EQ(0, res); + + res = random_r(&buf, &x); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(x); +} + +TEST(MemorySanitizer, confstr) { + char buf[3]; + size_t res = confstr(_CS_PATH, buf, sizeof(buf)); + ASSERT_GT(res, sizeof(buf)); + EXPECT_NOT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[sizeof(buf) - 1]); + + char buf2[1000]; + res = confstr(_CS_PATH, buf2, sizeof(buf2)); + ASSERT_LT(res, sizeof(buf2)); + EXPECT_NOT_POISONED(buf2[0]); + EXPECT_NOT_POISONED(buf2[res - 1]); + EXPECT_POISONED(buf2[res]); + ASSERT_EQ(res, strlen(buf2) + 1); +} + +TEST(MemorySanitizer, readdir) { + DIR *dir = opendir("."); + struct dirent *d = readdir(dir); + assert(d); + EXPECT_NOT_POISONED(d->d_name[0]); + closedir(dir); +} + +TEST(MemorySanitizer, readdir_r) { + DIR *dir = opendir("."); + struct dirent d; + struct dirent *pd; + int res = readdir_r(dir, &d, &pd); + assert(!res); + EXPECT_NOT_POISONED(pd); + EXPECT_NOT_POISONED(d.d_name[0]); + closedir(dir); +} + +TEST(MemorySanitizer, realpath) { + const char* relpath = "."; + char path[PATH_MAX + 1]; + char* res = realpath(relpath, path); + assert(res); + EXPECT_NOT_POISONED(path[0]); +} + +TEST(MemorySanitizer, realpath_null) { + const char* relpath = "."; + char* res = realpath(relpath, NULL); + printf("%d, %s\n", errno, strerror(errno)); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +TEST(MemorySanitizer, canonicalize_file_name) { + const char* relpath = "."; + char* res = canonicalize_file_name(relpath); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +extern char **environ; + +TEST(MemorySanitizer, setenv) { + setenv("AAA", "BBB", 1); + for (char **envp = environ; *envp; ++envp) { + EXPECT_NOT_POISONED(*envp); + EXPECT_NOT_POISONED(*envp[0]); + } +} + +TEST(MemorySanitizer, putenv) { + char s[] = "AAA=BBB"; + putenv(s); + for (char **envp = environ; *envp; ++envp) { + EXPECT_NOT_POISONED(*envp); + EXPECT_NOT_POISONED(*envp[0]); + } +} + +TEST(MemorySanitizer, memcpy) { + char* x = new char[2]; + char* y = new char[2]; + x[0] = 1; + x[1] = *GetPoisoned(); + memcpy(y, x, 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); +} + +void TestUnalignedMemcpy(int left, int right, bool src_is_aligned) { + const int sz = 20; + char *dst = (char *)malloc(sz); + U4 origin = __msan_get_origin(dst); + + char *src = (char *)malloc(sz); + memset(src, 0, sz); + + memcpy(dst + left, src_is_aligned ? src + left : src, sz - left - right); + for (int i = 0; i < left; ++i) + EXPECT_POISONED_O(dst[i], origin); + for (int i = 0; i < right; ++i) + EXPECT_POISONED_O(dst[sz - i - 1], origin); + EXPECT_NOT_POISONED(dst[left]); + EXPECT_NOT_POISONED(dst[sz - right - 1]); + + free(dst); + free(src); +} + +TEST(MemorySanitizer, memcpy_unaligned) { + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + TestUnalignedMemcpy(i, j, true); + TestUnalignedMemcpy(i, j, false); + } + } +} + +TEST(MemorySanitizer, memmove) { + char* x = new char[2]; + char* y = new char[2]; + x[0] = 1; + x[1] = *GetPoisoned(); + memmove(y, x, 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); +} + +TEST(MemorySanitizer, memccpy_nomatch) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + memccpy(y, x, 'd', 4); + EXPECT_NOT_POISONED(y[0]); + EXPECT_NOT_POISONED(y[1]); + EXPECT_NOT_POISONED(y[2]); + EXPECT_NOT_POISONED(y[3]); + EXPECT_POISONED(y[4]); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_match) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + memccpy(y, x, 'b', 4); + EXPECT_NOT_POISONED(y[0]); + EXPECT_NOT_POISONED(y[1]); + EXPECT_POISONED(y[2]); + EXPECT_POISONED(y[3]); + EXPECT_POISONED(y[4]); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_nomatch_positive) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + EXPECT_UMR(memccpy(y, x, 'd', 5)); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_match_positive) { + char* x = new char[5]; + char* y = new char[5]; + x[0] = 'a'; + x[2] = 'b'; + EXPECT_UMR(memccpy(y, x, 'b', 5)); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, bcopy) { + char* x = new char[2]; + char* y = new char[2]; + x[0] = 1; + x[1] = *GetPoisoned(); + bcopy(x, y, 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); +} + +TEST(MemorySanitizer, strdup) { + char buf[4] = "abc"; + __msan_poison(buf + 2, sizeof(*buf)); + char *x = strdup(buf); + EXPECT_NOT_POISONED(x[0]); + EXPECT_NOT_POISONED(x[1]); + EXPECT_POISONED(x[2]); + EXPECT_NOT_POISONED(x[3]); + free(x); +} + +TEST(MemorySanitizer, strndup) { + char buf[4] = "abc"; + __msan_poison(buf + 2, sizeof(*buf)); + char *x = strndup(buf, 3); + EXPECT_NOT_POISONED(x[0]); + EXPECT_NOT_POISONED(x[1]); + EXPECT_POISONED(x[2]); + EXPECT_NOT_POISONED(x[3]); + free(x); +} + +TEST(MemorySanitizer, strndup_short) { + char buf[4] = "abc"; + __msan_poison(buf + 1, sizeof(*buf)); + __msan_poison(buf + 2, sizeof(*buf)); + char *x = strndup(buf, 2); + EXPECT_NOT_POISONED(x[0]); + EXPECT_POISONED(x[1]); + EXPECT_NOT_POISONED(x[2]); + free(x); +} + + +template +void TestOverlapMemmove() { + T *x = new T[size]; + assert(size >= 3); + x[2] = 0; + memmove(x, x + 1, (size - 1) * sizeof(T)); + EXPECT_NOT_POISONED(x[1]); + if (!__msan_has_dynamic_component()) { + // FIXME: under DR we will lose this information + // because accesses in memmove will unpoisin the shadow. + // We need to use our own memove implementation instead of libc's. + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[2]); + } + delete [] x; +} + +TEST(MemorySanitizer, overlap_memmove) { + TestOverlapMemmove(); + TestOverlapMemmove(); + TestOverlapMemmove(); + TestOverlapMemmove(); +} + +TEST(MemorySanitizer, strcpy) { // NOLINT + char* x = new char[3]; + char* y = new char[3]; + x[0] = 'a'; + x[1] = *GetPoisoned(1, 1); + x[2] = 0; + strcpy(y, x); // NOLINT + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); + EXPECT_NOT_POISONED(y[2]); +} + +TEST(MemorySanitizer, strncpy) { // NOLINT + char* x = new char[3]; + char* y = new char[3]; + x[0] = 'a'; + x[1] = *GetPoisoned(1, 1); + x[2] = 0; + strncpy(y, x, 2); // NOLINT + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); + EXPECT_POISONED(y[2]); +} + +TEST(MemorySanitizer, stpcpy) { // NOLINT + char* x = new char[3]; + char* y = new char[3]; + x[0] = 'a'; + x[1] = *GetPoisoned(1, 1); + x[2] = 0; + char *res = stpcpy(y, x); // NOLINT + ASSERT_EQ(res, y + 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); + EXPECT_NOT_POISONED(y[2]); +} + +TEST(MemorySanitizer, strtol) { + char *e; + assert(1 == strtol("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtoll) { + char *e; + assert(1 == strtoll("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtoul) { + char *e; + assert(1 == strtoul("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtoull) { + char *e; + assert(1 == strtoull("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtoimax) { + char *e; + assert(1 == strtoimax("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtoumax) { + char *e; + assert(1 == strtoumax("1", &e, 10)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtod) { + char *e; + assert(0 != strtod("1.5", &e)); + EXPECT_NOT_POISONED((S8) e); +} + +#ifdef __GLIBC__ +extern "C" double __strtod_l(const char *nptr, char **endptr, locale_t loc); +TEST(MemorySanitizer, __strtod_l) { + locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); + char *e; + assert(0 != __strtod_l("1.5", &e, loc)); + EXPECT_NOT_POISONED((S8) e); + freelocale(loc); +} +#endif // __GLIBC__ + +TEST(MemorySanitizer, strtof) { + char *e; + assert(0 != strtof("1.5", &e)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, strtold) { + char *e; + assert(0 != strtold("1.5", &e)); + EXPECT_NOT_POISONED((S8) e); +} + +TEST(MemorySanitizer, modf) { + double x, y; + x = modf(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, modff) { + float x, y; + x = modff(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, modfl) { + long double x, y; + x = modfl(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, sincos) { + double s, c; + sincos(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, sincosf) { + float s, c; + sincosf(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, sincosl) { + long double s, c; + sincosl(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, remquo) { + int quo; + double res = remquo(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, remquof) { + int quo; + float res = remquof(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, remquol) { + int quo; + long double res = remquof(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, lgamma) { + double res = lgamma(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgammaf) { + float res = lgammaf(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgammal) { + long double res = lgammal(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgamma_r) { + int sgn; + double res = lgamma_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, lgammaf_r) { + int sgn; + float res = lgammaf_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, lgammal_r) { + int sgn; + long double res = lgammal_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, drand48_r) { + struct drand48_data buf; + srand48_r(0, &buf); + double d; + drand48_r(&buf, &d); + EXPECT_NOT_POISONED(d); +} + +TEST(MemorySanitizer, lrand48_r) { + struct drand48_data buf; + srand48_r(0, &buf); + long d; + lrand48_r(&buf, &d); + EXPECT_NOT_POISONED(d); +} + +TEST(MemorySanitizer, sprintf) { // NOLINT + char buff[10]; + break_optimization(buff); + EXPECT_POISONED(buff[0]); + int res = sprintf(buff, "%d", 1234567); // NOLINT + assert(res == 7); + assert(buff[0] == '1'); + assert(buff[1] == '2'); + assert(buff[2] == '3'); + assert(buff[6] == '7'); + assert(buff[7] == 0); + EXPECT_POISONED(buff[8]); +} + +TEST(MemorySanitizer, snprintf) { + char buff[10]; + break_optimization(buff); + EXPECT_POISONED(buff[0]); + int res = snprintf(buff, sizeof(buff), "%d", 1234567); + assert(res == 7); + assert(buff[0] == '1'); + assert(buff[1] == '2'); + assert(buff[2] == '3'); + assert(buff[6] == '7'); + assert(buff[7] == 0); + EXPECT_POISONED(buff[8]); +} + +TEST(MemorySanitizer, swprintf) { + wchar_t buff[10]; + assert(sizeof(wchar_t) == 4); + break_optimization(buff); + EXPECT_POISONED(buff[0]); + int res = swprintf(buff, 9, L"%d", 1234567); + assert(res == 7); + assert(buff[0] == '1'); + assert(buff[1] == '2'); + assert(buff[2] == '3'); + assert(buff[6] == '7'); + assert(buff[7] == 0); + EXPECT_POISONED(buff[8]); +} + +TEST(MemorySanitizer, asprintf) { // NOLINT + char *pbuf; + EXPECT_POISONED(pbuf); + int res = asprintf(&pbuf, "%d", 1234567); // NOLINT + assert(res == 7); + EXPECT_NOT_POISONED(pbuf); + assert(pbuf[0] == '1'); + assert(pbuf[1] == '2'); + assert(pbuf[2] == '3'); + assert(pbuf[6] == '7'); + assert(pbuf[7] == 0); + free(pbuf); +} + +TEST(MemorySanitizer, mbstowcs) { + const char *x = "abc"; + wchar_t buff[10]; + int res = mbstowcs(buff, x, 2); + EXPECT_EQ(2, res); + EXPECT_EQ(L'a', buff[0]); + EXPECT_EQ(L'b', buff[1]); + EXPECT_POISONED(buff[2]); + res = mbstowcs(buff, x, 10); + EXPECT_EQ(3, res); + EXPECT_NOT_POISONED(buff[3]); +} + +TEST(MemorySanitizer, wcstombs) { + const wchar_t *x = L"abc"; + char buff[10]; + int res = wcstombs(buff, x, 4); + EXPECT_EQ(res, 3); + EXPECT_EQ(buff[0], 'a'); + EXPECT_EQ(buff[1], 'b'); + EXPECT_EQ(buff[2], 'c'); +} + +TEST(MemorySanitizer, wcsrtombs) { + const wchar_t *x = L"abc"; + const wchar_t *p = x; + char buff[10]; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = wcsrtombs(buff, &p, 4, &mbs); + EXPECT_EQ(res, 3); + EXPECT_EQ(buff[0], 'a'); + EXPECT_EQ(buff[1], 'b'); + EXPECT_EQ(buff[2], 'c'); + EXPECT_EQ(buff[3], '\0'); + EXPECT_POISONED(buff[4]); +} + +TEST(MemorySanitizer, wcsnrtombs) { + const wchar_t *x = L"abc"; + const wchar_t *p = x; + char buff[10]; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = wcsnrtombs(buff, &p, 2, 4, &mbs); + EXPECT_EQ(res, 2); + EXPECT_EQ(buff[0], 'a'); + EXPECT_EQ(buff[1], 'b'); + EXPECT_POISONED(buff[2]); +} + +TEST(MemorySanitizer, mbtowc) { + const char *x = "abc"; + wchar_t wx; + int res = mbtowc(&wx, x, 3); + EXPECT_GT(res, 0); + EXPECT_NOT_POISONED(wx); +} + +TEST(MemorySanitizer, mbrtowc) { + const char *x = "abc"; + wchar_t wx; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = mbrtowc(&wx, x, 3, &mbs); + EXPECT_GT(res, 0); + EXPECT_NOT_POISONED(wx); +} + +TEST(MemorySanitizer, gettimeofday) { + struct timeval tv; + struct timezone tz; + break_optimization(&tv); + break_optimization(&tz); + assert(sizeof(tv) == 16); + assert(sizeof(tz) == 8); + EXPECT_POISONED(tv.tv_sec); + EXPECT_POISONED(tv.tv_usec); + EXPECT_POISONED(tz.tz_minuteswest); + EXPECT_POISONED(tz.tz_dsttime); + assert(0 == gettimeofday(&tv, &tz)); + EXPECT_NOT_POISONED(tv.tv_sec); + EXPECT_NOT_POISONED(tv.tv_usec); + EXPECT_NOT_POISONED(tz.tz_minuteswest); + EXPECT_NOT_POISONED(tz.tz_dsttime); +} + +TEST(MemorySanitizer, clock_gettime) { + struct timespec tp; + EXPECT_POISONED(tp.tv_sec); + EXPECT_POISONED(tp.tv_nsec); + assert(0 == clock_gettime(CLOCK_REALTIME, &tp)); + EXPECT_NOT_POISONED(tp.tv_sec); + EXPECT_NOT_POISONED(tp.tv_nsec); +} + +TEST(MemorySanitizer, clock_getres) { + struct timespec tp; + EXPECT_POISONED(tp.tv_sec); + EXPECT_POISONED(tp.tv_nsec); + assert(0 == clock_getres(CLOCK_REALTIME, 0)); + EXPECT_POISONED(tp.tv_sec); + EXPECT_POISONED(tp.tv_nsec); + assert(0 == clock_getres(CLOCK_REALTIME, &tp)); + EXPECT_NOT_POISONED(tp.tv_sec); + EXPECT_NOT_POISONED(tp.tv_nsec); +} + +TEST(MemorySanitizer, getitimer) { + struct itimerval it1, it2; + int res; + EXPECT_POISONED(it1.it_interval.tv_sec); + EXPECT_POISONED(it1.it_interval.tv_usec); + EXPECT_POISONED(it1.it_value.tv_sec); + EXPECT_POISONED(it1.it_value.tv_usec); + res = getitimer(ITIMER_VIRTUAL, &it1); + assert(!res); + EXPECT_NOT_POISONED(it1.it_interval.tv_sec); + EXPECT_NOT_POISONED(it1.it_interval.tv_usec); + EXPECT_NOT_POISONED(it1.it_value.tv_sec); + EXPECT_NOT_POISONED(it1.it_value.tv_usec); + + it1.it_interval.tv_sec = it1.it_value.tv_sec = 10000; + it1.it_interval.tv_usec = it1.it_value.tv_usec = 0; + + res = setitimer(ITIMER_VIRTUAL, &it1, &it2); + assert(!res); + EXPECT_NOT_POISONED(it2.it_interval.tv_sec); + EXPECT_NOT_POISONED(it2.it_interval.tv_usec); + EXPECT_NOT_POISONED(it2.it_value.tv_sec); + EXPECT_NOT_POISONED(it2.it_value.tv_usec); + + // Check that old_value can be 0, and disable the timer. + memset(&it1, 0, sizeof(it1)); + res = setitimer(ITIMER_VIRTUAL, &it1, 0); + assert(!res); +} + +TEST(MemorySanitizer, setitimer_null) { + setitimer(ITIMER_VIRTUAL, 0, 0); + // Not testing the return value, since it the behaviour seems to differ + // between libc implementations and POSIX. + // Should never crash, though. +} + +TEST(MemorySanitizer, time) { + time_t t; + EXPECT_POISONED(t); + time_t t2 = time(&t); + assert(t2 != (time_t)-1); + EXPECT_NOT_POISONED(t); +} + +TEST(MemorySanitizer, strptime) { + struct tm time; + char *p = strptime("11/1/2013-05:39", "%m/%d/%Y-%H:%M", &time); + assert(p != 0); + EXPECT_NOT_POISONED(time.tm_sec); + EXPECT_NOT_POISONED(time.tm_hour); + EXPECT_NOT_POISONED(time.tm_year); +} + +TEST(MemorySanitizer, localtime) { + time_t t = 123; + struct tm *time = localtime(&t); + assert(time != 0); + EXPECT_NOT_POISONED(time->tm_sec); + EXPECT_NOT_POISONED(time->tm_hour); + EXPECT_NOT_POISONED(time->tm_year); + EXPECT_NOT_POISONED(time->tm_isdst); + EXPECT_NE(0, strlen(time->tm_zone)); +} + +TEST(MemorySanitizer, localtime_r) { + time_t t = 123; + struct tm time; + struct tm *res = localtime_r(&t, &time); + assert(res != 0); + EXPECT_NOT_POISONED(time.tm_sec); + EXPECT_NOT_POISONED(time.tm_hour); + EXPECT_NOT_POISONED(time.tm_year); + EXPECT_NOT_POISONED(time.tm_isdst); + EXPECT_NE(0, strlen(time.tm_zone)); +} + +TEST(MemorySanitizer, getmntent) { + FILE *fp = setmntent("/etc/fstab", "r"); + struct mntent *mnt = getmntent(fp); + ASSERT_NE((void *)0, mnt); + ASSERT_NE(0, strlen(mnt->mnt_fsname)); + ASSERT_NE(0, strlen(mnt->mnt_dir)); + ASSERT_NE(0, strlen(mnt->mnt_type)); + ASSERT_NE(0, strlen(mnt->mnt_opts)); + EXPECT_NOT_POISONED(mnt->mnt_freq); + EXPECT_NOT_POISONED(mnt->mnt_passno); + fclose(fp); +} + +TEST(MemorySanitizer, getmntent_r) { + FILE *fp = setmntent("/etc/fstab", "r"); + struct mntent mntbuf; + char buf[1000]; + struct mntent *mnt = getmntent_r(fp, &mntbuf, buf, sizeof(buf)); + ASSERT_NE((void *)0, mnt); + ASSERT_NE(0, strlen(mnt->mnt_fsname)); + ASSERT_NE(0, strlen(mnt->mnt_dir)); + ASSERT_NE(0, strlen(mnt->mnt_type)); + ASSERT_NE(0, strlen(mnt->mnt_opts)); + EXPECT_NOT_POISONED(mnt->mnt_freq); + EXPECT_NOT_POISONED(mnt->mnt_passno); + fclose(fp); +} + +TEST(MemorySanitizer, ether) { + const char *asc = "11:22:33:44:55:66"; + struct ether_addr *paddr = ether_aton(asc); + EXPECT_NOT_POISONED(*paddr); + + struct ether_addr addr; + paddr = ether_aton_r(asc, &addr); + ASSERT_EQ(paddr, &addr); + EXPECT_NOT_POISONED(addr); + + char *s = ether_ntoa(&addr); + ASSERT_NE(0, strlen(s)); + + char buf[100]; + s = ether_ntoa_r(&addr, buf); + ASSERT_EQ(s, buf); + ASSERT_NE(0, strlen(buf)); +} + +TEST(MemorySanitizer, mmap) { + const int size = 4096; + void *p1, *p2; + p1 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + __msan_poison(p1, size); + munmap(p1, size); + for (int i = 0; i < 1000; i++) { + p2 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + if (p2 == p1) + break; + else + munmap(p2, size); + } + if (p1 == p2) { + EXPECT_NOT_POISONED(*(char*)p2); + munmap(p2, size); + } +} + +// FIXME: enable and add ecvt. +// FIXME: check why msandr does nt handle fcvt. +TEST(MemorySanitizer, fcvt) { + int a, b; + break_optimization(&a); + break_optimization(&b); + EXPECT_POISONED(a); + EXPECT_POISONED(b); + char *str = fcvt(12345.6789, 10, &a, &b); + EXPECT_NOT_POISONED(a); + EXPECT_NOT_POISONED(b); +} + +TEST(MemorySanitizer, frexp) { + int x; + x = *GetPoisoned(); + double r = frexp(1.1, &x); + EXPECT_NOT_POISONED(r); + EXPECT_NOT_POISONED(x); + + x = *GetPoisoned(); + float rf = frexpf(1.1, &x); + EXPECT_NOT_POISONED(rf); + EXPECT_NOT_POISONED(x); + + x = *GetPoisoned(); + double rl = frexpl(1.1, &x); + EXPECT_NOT_POISONED(rl); + EXPECT_NOT_POISONED(x); +} + +namespace { + +static int cnt; + +void SigactionHandler(int signo, siginfo_t* si, void* uc) { + assert(signo == SIGPROF); + assert(si); + EXPECT_NOT_POISONED(si->si_errno); + EXPECT_NOT_POISONED(si->si_pid); +#if __linux__ +# if defined(__x86_64__) + EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_RIP]); +# elif defined(__i386__) + EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP]); +# endif +#endif + ++cnt; +} + +TEST(MemorySanitizer, sigaction) { + struct sigaction act = {}; + struct sigaction oldact = {}; + struct sigaction origact = {}; + + sigaction(SIGPROF, 0, &origact); + + act.sa_flags |= SA_SIGINFO; + act.sa_sigaction = &SigactionHandler; + sigaction(SIGPROF, &act, 0); + + kill(getpid(), SIGPROF); + + act.sa_flags &= ~SA_SIGINFO; + act.sa_handler = SIG_DFL; + sigaction(SIGPROF, &act, 0); + + act.sa_flags &= ~SA_SIGINFO; + act.sa_handler = SIG_IGN; + sigaction(SIGPROF, &act, &oldact); + EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); + EXPECT_EQ(SIG_DFL, oldact.sa_handler); + kill(getpid(), SIGPROF); + + act.sa_flags |= SA_SIGINFO; + act.sa_sigaction = &SigactionHandler; + sigaction(SIGPROF, &act, &oldact); + EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); + EXPECT_EQ(SIG_IGN, oldact.sa_handler); + kill(getpid(), SIGPROF); + + act.sa_flags &= ~SA_SIGINFO; + act.sa_handler = SIG_DFL; + sigaction(SIGPROF, &act, &oldact); + EXPECT_TRUE(oldact.sa_flags & SA_SIGINFO); + EXPECT_EQ(&SigactionHandler, oldact.sa_sigaction); + EXPECT_EQ(2, cnt); + + sigaction(SIGPROF, &origact, 0); +} + +} // namespace + + +TEST(MemorySanitizer, sigemptyset) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigemptyset(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigfillset) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigfillset(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigpending) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigpending(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigprocmask) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigprocmask(SIG_BLOCK, 0, &s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +struct StructWithDtor { + ~StructWithDtor(); +}; + +NOINLINE StructWithDtor::~StructWithDtor() { + break_optimization(0); +} + +TEST(MemorySanitizer, Invoke) { + StructWithDtor s; // Will cause the calls to become invokes. + EXPECT_NOT_POISONED(0); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_NOT_POISONED(0); + EXPECT_POISONED(*GetPoisoned()); + EXPECT_POISONED(ReturnPoisoned()); +} + +TEST(MemorySanitizer, ptrtoint) { + // Test that shadow is propagated through pointer-to-integer conversion. + void* p = (void*)0xABCD; + __msan_poison(((char*)&p) + 1, sizeof(p)); + EXPECT_NOT_POISONED((((uintptr_t)p) & 0xFF) == 0); + + void* q = (void*)0xABCD; + __msan_poison(&q, sizeof(q) - 1); + EXPECT_POISONED((((uintptr_t)q) & 0xFF) == 0); +} + +static void vaargsfn2(int guard, ...) { + va_list vl; + va_start(vl, guard); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, double)); + va_end(vl); +} + +static void vaargsfn(int guard, ...) { + va_list vl; + va_start(vl, guard); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); + // The following call will overwrite __msan_param_tls. + // Checks after it test that arg shadow was somehow saved across the call. + vaargsfn2(1, 2, 3, 4, *GetPoisoned()); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); + va_end(vl); +} + +TEST(MemorySanitizer, VAArgTest) { + int* x = GetPoisoned(); + int* y = GetPoisoned(4); + vaargsfn(1, 13, *x, 42, *y); +} + +static void vaargsfn_many(int guard, ...) { + va_list vl; + va_start(vl, guard); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); + va_end(vl); +} + +TEST(MemorySanitizer, VAArgManyTest) { + int* x = GetPoisoned(); + int* y = GetPoisoned(4); + vaargsfn_many(1, 2, *x, 3, 4, 5, 6, 7, 8, 9, *y); +} + +static void vaargsfn_pass2(va_list vl) { + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); +} + +static void vaargsfn_pass(int guard, ...) { + va_list vl; + va_start(vl, guard); + EXPECT_POISONED(va_arg(vl, int)); + vaargsfn_pass2(vl); + va_end(vl); +} + +TEST(MemorySanitizer, VAArgPass) { + int* x = GetPoisoned(); + int* y = GetPoisoned(4); + vaargsfn_pass(1, *x, 2, 3, *y); +} + +static void vaargsfn_copy2(va_list vl) { + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); +} + +static void vaargsfn_copy(int guard, ...) { + va_list vl; + va_start(vl, guard); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); + va_list vl2; + va_copy(vl2, vl); + vaargsfn_copy2(vl2); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); + va_end(vl); +} + +TEST(MemorySanitizer, VAArgCopy) { + int* x = GetPoisoned(); + int* y = GetPoisoned(4); + vaargsfn_copy(1, 2, *x, 3, *y); +} + +static void vaargsfn_ptr(int guard, ...) { + va_list vl; + va_start(vl, guard); + EXPECT_NOT_POISONED(va_arg(vl, int*)); + EXPECT_POISONED(va_arg(vl, int*)); + EXPECT_NOT_POISONED(va_arg(vl, int*)); + EXPECT_POISONED(va_arg(vl, double*)); + va_end(vl); +} + +TEST(MemorySanitizer, VAArgPtr) { + int** x = GetPoisoned(); + double** y = GetPoisoned(8); + int z; + vaargsfn_ptr(1, &z, *x, &z, *y); +} + +static void vaargsfn_overflow(int guard, ...) { + va_list vl; + va_start(vl, guard); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, int)); + + EXPECT_NOT_POISONED(va_arg(vl, double)); + EXPECT_NOT_POISONED(va_arg(vl, double)); + EXPECT_NOT_POISONED(va_arg(vl, double)); + EXPECT_POISONED(va_arg(vl, double)); + EXPECT_NOT_POISONED(va_arg(vl, double)); + EXPECT_POISONED(va_arg(vl, int*)); + EXPECT_NOT_POISONED(va_arg(vl, double)); + EXPECT_NOT_POISONED(va_arg(vl, double)); + + EXPECT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, double)); + EXPECT_POISONED(va_arg(vl, int*)); + + EXPECT_NOT_POISONED(va_arg(vl, int)); + EXPECT_NOT_POISONED(va_arg(vl, double)); + EXPECT_NOT_POISONED(va_arg(vl, int*)); + + EXPECT_POISONED(va_arg(vl, int)); + EXPECT_POISONED(va_arg(vl, double)); + EXPECT_POISONED(va_arg(vl, int*)); + + va_end(vl); +} + +TEST(MemorySanitizer, VAArgOverflow) { + int* x = GetPoisoned(); + double* y = GetPoisoned(8); + int** p = GetPoisoned(16); + int z; + vaargsfn_overflow(1, + 1, 2, *x, 4, 5, 6, + 1.1, 2.2, 3.3, *y, 5.5, *p, 7.7, 8.8, + // the following args will overflow for sure + *x, *y, *p, + 7, 9.9, &z, + *x, *y, *p); +} + +static void vaargsfn_tlsoverwrite2(int guard, ...) { + va_list vl; + va_start(vl, guard); + for (int i = 0; i < 20; ++i) + EXPECT_NOT_POISONED(va_arg(vl, int)); + va_end(vl); +} + +static void vaargsfn_tlsoverwrite(int guard, ...) { + // This call will overwrite TLS contents unless it's backed up somewhere. + vaargsfn_tlsoverwrite2(2, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42); // 20x + va_list vl; + va_start(vl, guard); + for (int i = 0; i < 20; ++i) + EXPECT_POISONED(va_arg(vl, int)); + va_end(vl); +} + +TEST(MemorySanitizer, VAArgTLSOverwrite) { + int* x = GetPoisoned(); + vaargsfn_tlsoverwrite(1, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x); // 20x + +} + +struct StructByVal { + int a, b, c, d, e, f; +}; + +NOINLINE void StructByValTestFunc(struct StructByVal s) { + EXPECT_NOT_POISONED(s.a); + EXPECT_POISONED(s.b); + EXPECT_NOT_POISONED(s.c); + EXPECT_POISONED(s.d); + EXPECT_NOT_POISONED(s.e); + EXPECT_POISONED(s.f); +} + +NOINLINE void StructByValTestFunc1(struct StructByVal s) { + StructByValTestFunc(s); +} + +NOINLINE void StructByValTestFunc2(int z, struct StructByVal s) { + StructByValTestFunc(s); +} + +TEST(MemorySanitizer, StructByVal) { + // Large aggregates are passed as "byval" pointer argument in LLVM. + struct StructByVal s; + s.a = 1; + s.b = *GetPoisoned(); + s.c = 2; + s.d = *GetPoisoned(); + s.e = 3; + s.f = *GetPoisoned(); + StructByValTestFunc(s); + StructByValTestFunc1(s); + StructByValTestFunc2(0, s); +} + + +#if MSAN_HAS_M128 +NOINLINE __m128i m128Eq(__m128i *a, __m128i *b) { return _mm_cmpeq_epi16(*a, *b); } +NOINLINE __m128i m128Lt(__m128i *a, __m128i *b) { return _mm_cmplt_epi16(*a, *b); } +TEST(MemorySanitizer, m128) { + __m128i a = _mm_set1_epi16(0x1234); + __m128i b = _mm_set1_epi16(0x7890); + EXPECT_NOT_POISONED(m128Eq(&a, &b)); + EXPECT_NOT_POISONED(m128Lt(&a, &b)); +} +// FIXME: add more tests for __m128i. +#endif // MSAN_HAS_M128 + +// We should not complain when copying this poisoned hole. +struct StructWithHole { + U4 a; + // 4-byte hole. + U8 b; +}; + +NOINLINE StructWithHole ReturnStructWithHole() { + StructWithHole res; + __msan_poison(&res, sizeof(res)); + res.a = 1; + res.b = 2; + return res; +} + +TEST(MemorySanitizer, StructWithHole) { + StructWithHole a = ReturnStructWithHole(); + break_optimization(&a); +} + +template +NOINLINE T ReturnStruct() { + T res; + __msan_poison(&res, sizeof(res)); + res.a = 1; + return res; +} + +template +NOINLINE void TestReturnStruct() { + T s1 = ReturnStruct(); + EXPECT_NOT_POISONED(s1.a); + EXPECT_POISONED(s1.b); +} + +struct SSS1 { + int a, b, c; +}; +struct SSS2 { + int b, a, c; +}; +struct SSS3 { + int b, c, a; +}; +struct SSS4 { + int c, b, a; +}; + +struct SSS5 { + int a; + float b; +}; +struct SSS6 { + int a; + double b; +}; +struct SSS7 { + S8 b; + int a; +}; +struct SSS8 { + S2 b; + S8 a; +}; + +TEST(MemorySanitizer, IntStruct3) { + TestReturnStruct(); + TestReturnStruct(); + TestReturnStruct(); + TestReturnStruct(); + TestReturnStruct(); + TestReturnStruct(); + TestReturnStruct(); + TestReturnStruct(); +} + +struct LongStruct { + U1 a1, b1; + U2 a2, b2; + U4 a4, b4; + U8 a8, b8; +}; + +NOINLINE LongStruct ReturnLongStruct1() { + LongStruct res; + __msan_poison(&res, sizeof(res)); + res.a1 = res.a2 = res.a4 = res.a8 = 111; + // leaves b1, .., b8 poisoned. + return res; +} + +NOINLINE LongStruct ReturnLongStruct2() { + LongStruct res; + __msan_poison(&res, sizeof(res)); + res.b1 = res.b2 = res.b4 = res.b8 = 111; + // leaves a1, .., a8 poisoned. + return res; +} + +TEST(MemorySanitizer, LongStruct) { + LongStruct s1 = ReturnLongStruct1(); + __msan_print_shadow(&s1, sizeof(s1)); + EXPECT_NOT_POISONED(s1.a1); + EXPECT_NOT_POISONED(s1.a2); + EXPECT_NOT_POISONED(s1.a4); + EXPECT_NOT_POISONED(s1.a8); + + EXPECT_POISONED(s1.b1); + EXPECT_POISONED(s1.b2); + EXPECT_POISONED(s1.b4); + EXPECT_POISONED(s1.b8); + + LongStruct s2 = ReturnLongStruct2(); + __msan_print_shadow(&s2, sizeof(s2)); + EXPECT_NOT_POISONED(s2.b1); + EXPECT_NOT_POISONED(s2.b2); + EXPECT_NOT_POISONED(s2.b4); + EXPECT_NOT_POISONED(s2.b8); + + EXPECT_POISONED(s2.a1); + EXPECT_POISONED(s2.a2); + EXPECT_POISONED(s2.a4); + EXPECT_POISONED(s2.a8); +} + +TEST(MemorySanitizer, getrlimit) { + struct rlimit limit; + __msan_poison(&limit, sizeof(limit)); + int result = getrlimit(RLIMIT_DATA, &limit); + assert(result == 0); + EXPECT_NOT_POISONED(limit.rlim_cur); + EXPECT_NOT_POISONED(limit.rlim_max); +} + +TEST(MemorySanitizer, getrusage) { + struct rusage usage; + __msan_poison(&usage, sizeof(usage)); + int result = getrusage(RUSAGE_SELF, &usage); + assert(result == 0); + EXPECT_NOT_POISONED(usage.ru_utime.tv_sec); + EXPECT_NOT_POISONED(usage.ru_utime.tv_usec); + EXPECT_NOT_POISONED(usage.ru_stime.tv_sec); + EXPECT_NOT_POISONED(usage.ru_stime.tv_usec); + EXPECT_NOT_POISONED(usage.ru_maxrss); + EXPECT_NOT_POISONED(usage.ru_minflt); + EXPECT_NOT_POISONED(usage.ru_majflt); + EXPECT_NOT_POISONED(usage.ru_inblock); + EXPECT_NOT_POISONED(usage.ru_oublock); + EXPECT_NOT_POISONED(usage.ru_nvcsw); + EXPECT_NOT_POISONED(usage.ru_nivcsw); +} + +#ifdef __GLIBC__ +extern char *program_invocation_name; +#else // __GLIBC__ +# error "TODO: port this" +#endif + +static void dladdr_testfn() {} + +TEST(MemorySanitizer, dladdr) { + Dl_info info; + __msan_poison(&info, sizeof(info)); + int result = dladdr((const void*)dladdr_testfn, &info); + assert(result != 0); + EXPECT_NOT_POISONED((unsigned long)info.dli_fname); + if (info.dli_fname) + EXPECT_NOT_POISONED(strlen(info.dli_fname)); + EXPECT_NOT_POISONED((unsigned long)info.dli_fbase); + EXPECT_NOT_POISONED((unsigned long)info.dli_sname); + if (info.dli_sname) + EXPECT_NOT_POISONED(strlen(info.dli_sname)); + EXPECT_NOT_POISONED((unsigned long)info.dli_saddr); +} + +#ifndef MSAN_TEST_DISABLE_DLOPEN + +static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { + (*(int *)data)++; + EXPECT_NOT_POISONED(info->dlpi_addr); + EXPECT_NOT_POISONED(strlen(info->dlpi_name)); + EXPECT_NOT_POISONED(info->dlpi_phnum); + for (int i = 0; i < info->dlpi_phnum; ++i) + EXPECT_NOT_POISONED(info->dlpi_phdr[i]); + return 0; +} + +// Compute the path to our loadable DSO. We assume it's in the same +// directory. Only use string routines that we intercept so far to do this. +static int PathToLoadable(char *buf, size_t sz) { + const char *basename = "libmsan_loadable.x86_64.so"; + char *argv0 = program_invocation_name; + char *last_slash = strrchr(argv0, '/'); + assert(last_slash); + int res = + snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); + return res < sz ? 0 : res; +} + +TEST(MemorySanitizer, dl_iterate_phdr) { + char path[4096]; + int res = PathToLoadable(path, sizeof(path)); + assert(!res); + + // Having at least one dlopen'ed library in the process makes this more + // entertaining. + void *lib = dlopen(path, RTLD_LAZY); + ASSERT_NE((void*)0, lib); + + int count = 0; + int result = dl_iterate_phdr(dl_phdr_callback, &count); + assert(count > 0); + + dlclose(lib); +} + + +TEST(MemorySanitizer, dlopen) { + char path[4096]; + int res = PathToLoadable(path, sizeof(path)); + assert(!res); + + // We need to clear shadow for globals when doing dlopen. In order to test + // this, we have to poison the shadow for the DSO before we load it. In + // general this is difficult, but the loader tends to reload things in the + // same place, so we open, close, and then reopen. The global should always + // start out clean after dlopen. + for (int i = 0; i < 2; i++) { + void *lib = dlopen(path, RTLD_LAZY); + if (lib == NULL) { + printf("dlerror: %s\n", dlerror()); + assert(lib != NULL); + } + void **(*get_dso_global)() = (void **(*)())dlsym(lib, "get_dso_global"); + assert(get_dso_global); + void **dso_global = get_dso_global(); + EXPECT_NOT_POISONED(*dso_global); + __msan_poison(dso_global, sizeof(*dso_global)); + EXPECT_POISONED(*dso_global); + dlclose(lib); + } +} + +// Regression test for a crash in dlopen() interceptor. +TEST(MemorySanitizer, dlopenFailed) { + const char *path = "/libmsan_loadable_does_not_exist.x86_64.so"; + void *lib = dlopen(path, RTLD_LAZY); + ASSERT_EQ(0, lib); +} + +#endif // MSAN_TEST_DISABLE_DLOPEN + +TEST(MemorySanitizer, sched_getaffinity) { + cpu_set_t mask; + int res = sched_getaffinity(getpid(), sizeof(mask), &mask); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(mask); +} + +TEST(MemorySanitizer, scanf) { + const char *input = "42 hello"; + int* d = new int; + char* s = new char[7]; + int res = sscanf(input, "%d %5s", d, s); + printf("res %d\n", res); + assert(res == 2); + EXPECT_NOT_POISONED(*d); + EXPECT_NOT_POISONED(s[0]); + EXPECT_NOT_POISONED(s[1]); + EXPECT_NOT_POISONED(s[2]); + EXPECT_NOT_POISONED(s[3]); + EXPECT_NOT_POISONED(s[4]); + EXPECT_NOT_POISONED(s[5]); + EXPECT_POISONED(s[6]); + delete s; + delete d; +} + +static void *SimpleThread_threadfn(void* data) { + return new int; +} + +TEST(MemorySanitizer, SimpleThread) { + pthread_t t; + void *p; + int res = pthread_create(&t, NULL, SimpleThread_threadfn, NULL); + assert(!res); + EXPECT_NOT_POISONED(t); + res = pthread_join(t, &p); + assert(!res); + EXPECT_NOT_POISONED(p); + delete (int*)p; +} + +static void *SmallStackThread_threadfn(void* data) { + return 0; +} + +TEST(MemorySanitizer, SmallStackThread) { + pthread_attr_t attr; + pthread_t t; + void *p; + int res; + res = pthread_attr_init(&attr); + ASSERT_EQ(0, res); + res = pthread_attr_setstacksize(&attr, 64 * 1024); + ASSERT_EQ(0, res); + res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); + ASSERT_EQ(0, res); + res = pthread_join(t, &p); + ASSERT_EQ(0, res); + res = pthread_attr_destroy(&attr); + ASSERT_EQ(0, res); +} + +TEST(MemorySanitizer, PreAllocatedStackThread) { + pthread_attr_t attr; + pthread_t t; + int res; + res = pthread_attr_init(&attr); + ASSERT_EQ(0, res); + void *stack; + const size_t kStackSize = 64 * 1024; + res = posix_memalign(&stack, 4096, kStackSize); + ASSERT_EQ(0, res); + res = pthread_attr_setstack(&attr, stack, kStackSize); + ASSERT_EQ(0, res); + // A small self-allocated stack can not be extended by the tool. + // In this case pthread_create is expected to fail. + res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); + EXPECT_NE(0, res); + res = pthread_attr_destroy(&attr); + ASSERT_EQ(0, res); +} + +TEST(MemorySanitizer, pthread_attr_get) { + pthread_attr_t attr; + int res; + res = pthread_attr_init(&attr); + ASSERT_EQ(0, res); + { + int v; + res = pthread_attr_getdetachstate(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + size_t v; + res = pthread_attr_getguardsize(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + struct sched_param v; + res = pthread_attr_getschedparam(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getschedpolicy(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getinheritsched(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getscope(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + size_t v; + res = pthread_attr_getstacksize(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + void *v; + size_t w; + res = pthread_attr_getstack(&attr, &v, &w); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + EXPECT_NOT_POISONED(w); + } + { + cpu_set_t v; + res = pthread_attr_getaffinity_np(&attr, sizeof(v), &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + res = pthread_attr_destroy(&attr); + ASSERT_EQ(0, res); +} + +TEST(MemorySanitizer, pthread_getschedparam) { + int policy; + struct sched_param param; + int res = pthread_getschedparam(pthread_self(), &policy, ¶m); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(policy); + EXPECT_NOT_POISONED(param.sched_priority); +} + +TEST(MemorySanitizer, pthread_key_create) { + pthread_key_t key; + int res = pthread_key_create(&key, NULL); + assert(!res); + EXPECT_NOT_POISONED(key); + res = pthread_key_delete(key); + assert(!res); +} + +namespace { +struct SignalCondArg { + pthread_cond_t* cond; + pthread_mutex_t* mu; + bool broadcast; +}; + +void *SignalCond(void *param) { + SignalCondArg *arg = reinterpret_cast(param); + pthread_mutex_lock(arg->mu); + if (arg->broadcast) + pthread_cond_broadcast(arg->cond); + else + pthread_cond_signal(arg->cond); + pthread_mutex_unlock(arg->mu); + return 0; +} +} // namespace + +TEST(MemorySanitizer, pthread_cond_wait) { + pthread_cond_t cond; + pthread_mutex_t mu; + SignalCondArg args = {&cond, &mu, false}; + pthread_cond_init(&cond, 0); + pthread_mutex_init(&mu, 0); + pthread_mutex_lock(&mu); + + // signal + pthread_t thr; + pthread_create(&thr, 0, SignalCond, &args); + int res = pthread_cond_wait(&cond, &mu); + assert(!res); + pthread_join(thr, 0); + + // broadcast + args.broadcast = true; + pthread_create(&thr, 0, SignalCond, &args); + res = pthread_cond_wait(&cond, &mu); + assert(!res); + pthread_join(thr, 0); + + pthread_mutex_unlock(&mu); + pthread_mutex_destroy(&mu); + pthread_cond_destroy(&cond); +} + +TEST(MemorySanitizer, tmpnam) { + char s[L_tmpnam]; + char *res = tmpnam(s); + ASSERT_EQ(s, res); + EXPECT_NOT_POISONED(strlen(res)); +} + +TEST(MemorySanitizer, tempnam) { + char *res = tempnam(NULL, "zzz"); + EXPECT_NOT_POISONED(strlen(res)); + free(res); +} + +TEST(MemorySanitizer, posix_memalign) { + void *p; + EXPECT_POISONED(p); + int res = posix_memalign(&p, 4096, 13); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(p); + EXPECT_EQ(0U, (uintptr_t)p % 4096); + free(p); +} + +TEST(MemorySanitizer, memalign) { + void *p = memalign(4096, 13); + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + free(p); +} + +TEST(MemorySanitizer, valloc) { + void *a = valloc(100); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); + free(a); +} + +TEST(MemorySanitizer, pvalloc) { + void *p = pvalloc(kPageSize + 100); + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + EXPECT_EQ(2 * kPageSize, __msan_get_allocated_size(p)); + free(p); + + p = pvalloc(0); // pvalloc(0) should allocate at least one page. + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + EXPECT_EQ(kPageSize, __msan_get_allocated_size(p)); + free(p); +} + +TEST(MemorySanitizer, inet_pton) { + const char *s = "1:0:0:0:0:0:0:8"; + unsigned char buf[sizeof(struct in6_addr)]; + int res = inet_pton(AF_INET6, s, buf); + ASSERT_EQ(1, res); + EXPECT_NOT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[sizeof(struct in6_addr) - 1]); + + char s_out[INET6_ADDRSTRLEN]; + EXPECT_POISONED(s_out[3]); + const char *q = inet_ntop(AF_INET6, buf, s_out, INET6_ADDRSTRLEN); + ASSERT_NE((void*)0, q); + EXPECT_NOT_POISONED(s_out[3]); +} + +TEST(MemorySanitizer, inet_aton) { + const char *s = "127.0.0.1"; + struct in_addr in[2]; + int res = inet_aton(s, in); + ASSERT_NE(0, res); + EXPECT_NOT_POISONED(in[0]); + EXPECT_POISONED(*(char *)(in + 1)); +} + +TEST(MemorySanitizer, uname) { + struct utsname u; + int res = uname(&u); + assert(!res); + EXPECT_NOT_POISONED(strlen(u.sysname)); + EXPECT_NOT_POISONED(strlen(u.nodename)); + EXPECT_NOT_POISONED(strlen(u.release)); + EXPECT_NOT_POISONED(strlen(u.version)); + EXPECT_NOT_POISONED(strlen(u.machine)); +} + +TEST(MemorySanitizer, gethostname) { + char buf[100]; + int res = gethostname(buf, 100); + assert(!res); + EXPECT_NOT_POISONED(strlen(buf)); +} + +TEST(MemorySanitizer, sysinfo) { + struct sysinfo info; + int res = sysinfo(&info); + assert(!res); + EXPECT_NOT_POISONED(info); +} + +TEST(MemorySanitizer, getpwuid) { + struct passwd *p = getpwuid(0); // root + assert(p); + EXPECT_NOT_POISONED(p->pw_name); + assert(p->pw_name); + EXPECT_NOT_POISONED(p->pw_name[0]); + EXPECT_NOT_POISONED(p->pw_uid); + assert(p->pw_uid == 0); +} + +TEST(MemorySanitizer, getpwnam_r) { + struct passwd pwd; + struct passwd *pwdres; + char buf[10000]; + int res = getpwnam_r("root", &pwd, buf, sizeof(buf), &pwdres); + assert(!res); + EXPECT_NOT_POISONED(pwd.pw_name); + assert(pwd.pw_name); + EXPECT_NOT_POISONED(pwd.pw_name[0]); + EXPECT_NOT_POISONED(pwd.pw_uid); + assert(pwd.pw_uid == 0); +} + +TEST(MemorySanitizer, getpwnam_r_positive) { + struct passwd pwd; + struct passwd *pwdres; + char s[5]; + strncpy(s, "abcd", 5); + __msan_poison(s, 5); + char buf[10000]; + int res; + EXPECT_UMR(res = getpwnam_r(s, &pwd, buf, sizeof(buf), &pwdres)); +} + +TEST(MemorySanitizer, getgrnam_r) { + struct group grp; + struct group *grpres; + char buf[10000]; + int res = getgrnam_r("root", &grp, buf, sizeof(buf), &grpres); + assert(!res); + EXPECT_NOT_POISONED(grp.gr_name); + assert(grp.gr_name); + EXPECT_NOT_POISONED(grp.gr_name[0]); + EXPECT_NOT_POISONED(grp.gr_gid); +} + +TEST(MemorySanitizer, getgroups) { + int n = getgroups(0, 0); + gid_t *gids = new gid_t[n]; + int res = getgroups(n, gids); + ASSERT_EQ(n, res); + for (int i = 0; i < n; ++i) + EXPECT_NOT_POISONED(gids[i]); +} + +TEST(MemorySanitizer, wordexp) { + wordexp_t w; + int res = wordexp("a b c", &w, 0); + ASSERT_EQ(0, res); + ASSERT_EQ(3, w.we_wordc); + ASSERT_STREQ("a", w.we_wordv[0]); + ASSERT_STREQ("b", w.we_wordv[1]); + ASSERT_STREQ("c", w.we_wordv[2]); +} + +template +static bool applySlt(T value, T shadow) { + __msan_partial_poison(&value, &shadow, sizeof(T)); + volatile bool zzz = true; + // This "|| zzz" trick somehow makes LLVM emit "icmp slt" instead of + // a shift-and-trunc to get at the highest bit. + volatile bool v = value < 0 || zzz; + return v; +} + +TEST(MemorySanitizer, SignedCompareWithZero) { + EXPECT_NOT_POISONED(applySlt(0xF, 0xF)); + EXPECT_NOT_POISONED(applySlt(0xF, 0xFF)); + EXPECT_NOT_POISONED(applySlt(0xF, 0xFFFFFF)); + EXPECT_NOT_POISONED(applySlt(0xF, 0x7FFFFFF)); + EXPECT_UMR(applySlt(0xF, 0x80FFFFFF)); + EXPECT_UMR(applySlt(0xF, 0xFFFFFFFF)); +} + +template +static T poisoned(T Va, S Sa) { + char SIZE_CHECK1[(ssize_t)sizeof(T) - (ssize_t)sizeof(S)]; + char SIZE_CHECK2[(ssize_t)sizeof(S) - (ssize_t)sizeof(T)]; + T a; + a = Va; + __msan_partial_poison(&a, &Sa, sizeof(T)); + return a; +} + +TEST(MemorySanitizer, ICmpRelational) { + EXPECT_NOT_POISONED(poisoned(0, 0) < poisoned(0, 0)); + EXPECT_NOT_POISONED(poisoned(0U, 0) < poisoned(0U, 0)); + EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) < poisoned(0LL, 0LLU)); + EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) < poisoned(0LLU, 0LLU)); + EXPECT_POISONED(poisoned(0xFF, 0xFF) < poisoned(0xFF, 0xFF)); + EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) < + poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); + EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) < + poisoned(-1, 0xFFFFFFFFU)); + + EXPECT_NOT_POISONED(poisoned(0, 0) <= poisoned(0, 0)); + EXPECT_NOT_POISONED(poisoned(0U, 0) <= poisoned(0U, 0)); + EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) <= poisoned(0LL, 0LLU)); + EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) <= poisoned(0LLU, 0LLU)); + EXPECT_POISONED(poisoned(0xFF, 0xFF) <= poisoned(0xFF, 0xFF)); + EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) <= + poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); + EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) <= + poisoned(-1, 0xFFFFFFFFU)); + + EXPECT_NOT_POISONED(poisoned(0, 0) > poisoned(0, 0)); + EXPECT_NOT_POISONED(poisoned(0U, 0) > poisoned(0U, 0)); + EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) > poisoned(0LL, 0LLU)); + EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) > poisoned(0LLU, 0LLU)); + EXPECT_POISONED(poisoned(0xFF, 0xFF) > poisoned(0xFF, 0xFF)); + EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) > + poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); + EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) > + poisoned(-1, 0xFFFFFFFFU)); + + EXPECT_NOT_POISONED(poisoned(0, 0) >= poisoned(0, 0)); + EXPECT_NOT_POISONED(poisoned(0U, 0) >= poisoned(0U, 0)); + EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) >= poisoned(0LL, 0LLU)); + EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) >= poisoned(0LLU, 0LLU)); + EXPECT_POISONED(poisoned(0xFF, 0xFF) >= poisoned(0xFF, 0xFF)); + EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) >= + poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); + EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) >= + poisoned(-1, 0xFFFFFFFFU)); + + EXPECT_POISONED(poisoned(6, 0xF) > poisoned(7, 0)); + EXPECT_POISONED(poisoned(0xF, 0xF) > poisoned(7, 0)); + + EXPECT_NOT_POISONED(poisoned(-1, 0x80000000U) >= poisoned(-1, 0U)); +} + +#if MSAN_HAS_M128 +TEST(MemorySanitizer, ICmpVectorRelational) { + EXPECT_NOT_POISONED( + _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)), + poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)))); + EXPECT_NOT_POISONED( + _mm_cmplt_epi16(poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)), + poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)))); + EXPECT_POISONED( + _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)), + poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)))); + EXPECT_POISONED(_mm_cmpgt_epi16(poisoned(_mm_set1_epi16(6), _mm_set1_epi16(0xF)), + poisoned(_mm_set1_epi16(7), _mm_set1_epi16(0)))); +} +#endif + +// Volatile bitfield store is implemented as load-mask-store +// Test that we don't warn on the store of (uninitialized) padding. +struct VolatileBitfieldStruct { + volatile unsigned x : 1; + unsigned y : 1; +}; + +TEST(MemorySanitizer, VolatileBitfield) { + VolatileBitfieldStruct *S = new VolatileBitfieldStruct; + S->x = 1; + EXPECT_NOT_POISONED((unsigned)S->x); + EXPECT_POISONED((unsigned)S->y); +} + +TEST(MemorySanitizer, UnalignedLoad) { + char x[32]; + memset(x + 8, 0, 16); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+6)); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+7)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+22)); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+23)); + EXPECT_POISONED(__sanitizer_unaligned_load16(x+24)); + + EXPECT_POISONED(__sanitizer_unaligned_load32(x+4)); + EXPECT_POISONED(__sanitizer_unaligned_load32(x+7)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+20)); + EXPECT_POISONED(__sanitizer_unaligned_load32(x+21)); + EXPECT_POISONED(__sanitizer_unaligned_load32(x+24)); + + EXPECT_POISONED(__sanitizer_unaligned_load64(x)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+1)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+7)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+16)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+17)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+21)); + EXPECT_POISONED(__sanitizer_unaligned_load64(x+24)); +} + +TEST(MemorySanitizer, UnalignedStore16) { + char x[5]; + U2 y = 0; + __msan_poison(&y, 1); + __sanitizer_unaligned_store16(x + 1, y); + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[1]); + EXPECT_NOT_POISONED(x[2]); + EXPECT_POISONED(x[3]); + EXPECT_POISONED(x[4]); +} + +TEST(MemorySanitizer, UnalignedStore32) { + char x[8]; + U4 y4 = 0; + __msan_poison(&y4, 2); + __sanitizer_unaligned_store32(x+3, y4); + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[1]); + EXPECT_POISONED(x[2]); + EXPECT_POISONED(x[3]); + EXPECT_POISONED(x[4]); + EXPECT_NOT_POISONED(x[5]); + EXPECT_NOT_POISONED(x[6]); + EXPECT_POISONED(x[7]); +} + +TEST(MemorySanitizer, UnalignedStore64) { + char x[16]; + U8 y = 0; + __msan_poison(&y, 3); + __msan_poison(((char *)&y) + sizeof(y) - 2, 1); + __sanitizer_unaligned_store64(x+3, y); + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[1]); + EXPECT_POISONED(x[2]); + EXPECT_POISONED(x[3]); + EXPECT_POISONED(x[4]); + EXPECT_POISONED(x[5]); + EXPECT_NOT_POISONED(x[6]); + EXPECT_NOT_POISONED(x[7]); + EXPECT_NOT_POISONED(x[8]); + EXPECT_POISONED(x[9]); + EXPECT_NOT_POISONED(x[10]); + EXPECT_POISONED(x[11]); +} + +TEST(MemorySanitizerDr, StoreInDSOTest) { + if (!__msan_has_dynamic_component()) return; + char* s = new char[10]; + dso_memfill(s, 9); + EXPECT_NOT_POISONED(s[5]); + EXPECT_POISONED(s[9]); +} + +int return_poisoned_int() { + return ReturnPoisoned(); +} + +TEST(MemorySanitizerDr, ReturnFromDSOTest) { + if (!__msan_has_dynamic_component()) return; + EXPECT_NOT_POISONED(dso_callfn(return_poisoned_int)); +} + +NOINLINE int TrashParamTLS(long long x, long long y, long long z) { //NOLINT + EXPECT_POISONED(x); + EXPECT_POISONED(y); + EXPECT_POISONED(z); + return 0; +} + +static int CheckParamTLS(long long x, long long y, long long z) { //NOLINT + EXPECT_NOT_POISONED(x); + EXPECT_NOT_POISONED(y); + EXPECT_NOT_POISONED(z); + return 0; +} + +TEST(MemorySanitizerDr, CallFromDSOTest) { + if (!__msan_has_dynamic_component()) return; + S8* x = GetPoisoned(); + S8* y = GetPoisoned(); + S8* z = GetPoisoned(); + EXPECT_NOT_POISONED(TrashParamTLS(*x, *y, *z)); + EXPECT_NOT_POISONED(dso_callfn1(CheckParamTLS)); +} + +static void StackStoreInDSOFn(int* x, int* y) { + EXPECT_NOT_POISONED(*x); + EXPECT_NOT_POISONED(*y); +} + +TEST(MemorySanitizerDr, StackStoreInDSOTest) { + if (!__msan_has_dynamic_component()) return; + dso_stack_store(StackStoreInDSOFn, 1); +} + +TEST(MemorySanitizerOrigins, SetGet) { + EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins()); + if (!TrackingOrigins()) return; + int x; + __msan_set_origin(&x, sizeof(x), 1234); + EXPECT_EQ(1234, __msan_get_origin(&x)); + __msan_set_origin(&x, sizeof(x), 5678); + EXPECT_EQ(5678, __msan_get_origin(&x)); + __msan_set_origin(&x, sizeof(x), 0); + EXPECT_EQ(0, __msan_get_origin(&x)); +} + +namespace { +struct S { + U4 dummy; + U2 a; + U2 b; +}; + +// http://code.google.com/p/memory-sanitizer/issues/detail?id=6 +TEST(MemorySanitizerOrigins, DISABLED_InitializedStoreDoesNotChangeOrigin) { + if (!TrackingOrigins()) return; + + S s; + U4 origin = rand(); // NOLINT + s.a = *GetPoisonedO(0, origin); + EXPECT_EQ(origin, __msan_get_origin(&s.a)); + EXPECT_EQ(origin, __msan_get_origin(&s.b)); + + s.b = 42; + EXPECT_EQ(origin, __msan_get_origin(&s.a)); + EXPECT_EQ(origin, __msan_get_origin(&s.b)); +} +} // namespace + +template +INLINE +void BinaryOpOriginTest(BinaryOp op) { + U4 ox = rand(); //NOLINT + U4 oy = rand(); //NOLINT + T *x = GetPoisonedO(0, ox, 0); + T *y = GetPoisonedO(1, oy, 0); + T *z = GetPoisonedO(2, 0, 0); + + *z = op(*x, *y); + U4 origin = __msan_get_origin(z); + EXPECT_POISONED_O(*z, origin); + EXPECT_EQ(true, origin == ox || origin == oy); + + // y is poisoned, x is not. + *x = 10101; + *y = *GetPoisonedO(1, oy); + break_optimization(x); + __msan_set_origin(z, sizeof(*z), 0); + *z = op(*x, *y); + EXPECT_POISONED_O(*z, oy); + EXPECT_EQ(__msan_get_origin(z), oy); + + // x is poisoned, y is not. + *x = *GetPoisonedO(0, ox); + *y = 10101010; + break_optimization(y); + __msan_set_origin(z, sizeof(*z), 0); + *z = op(*x, *y); + EXPECT_POISONED_O(*z, ox); + EXPECT_EQ(__msan_get_origin(z), ox); +} + +template INLINE T XOR(const T &a, const T&b) { return a ^ b; } +template INLINE T ADD(const T &a, const T&b) { return a + b; } +template INLINE T SUB(const T &a, const T&b) { return a - b; } +template INLINE T MUL(const T &a, const T&b) { return a * b; } +template INLINE T AND(const T &a, const T&b) { return a & b; } +template INLINE T OR (const T &a, const T&b) { return a | b; } + +TEST(MemorySanitizerOrigins, BinaryOp) { + if (!TrackingOrigins()) return; + BinaryOpOriginTest(XOR); + BinaryOpOriginTest(ADD); + BinaryOpOriginTest(SUB); + BinaryOpOriginTest(MUL); + BinaryOpOriginTest(OR); + BinaryOpOriginTest(AND); + BinaryOpOriginTest(ADD); + BinaryOpOriginTest(ADD); + BinaryOpOriginTest(ADD); + BinaryOpOriginTest(ADD); +} + +TEST(MemorySanitizerOrigins, Unary) { + if (!TrackingOrigins()) return; + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + + EXPECT_POISONED_O((void*)*GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O((U8)*GetPoisonedO(0, __LINE__), __LINE__); +} + +TEST(MemorySanitizerOrigins, EQ) { + if (!TrackingOrigins()) return; + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) <= 11, __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) == 11, __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) == 1.1, __LINE__); +} + +TEST(MemorySanitizerOrigins, DIV) { + if (!TrackingOrigins()) return; + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) / 100, __LINE__); + unsigned o = __LINE__; + EXPECT_UMR_O(volatile unsigned y = 100 / *GetPoisonedO(0, o, 1), o); +} + +TEST(MemorySanitizerOrigins, SHIFT) { + if (!TrackingOrigins()) return; + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) >> 10, __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) >> 10, __LINE__); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) << 10, __LINE__); + EXPECT_POISONED_O(10U << *GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(-10 >> *GetPoisonedO(0, __LINE__), __LINE__); + EXPECT_POISONED_O(-10 << *GetPoisonedO(0, __LINE__), __LINE__); +} + +template +void MemCpyTest() { + int ox = __LINE__; + T *x = new T[N]; + T *y = new T[N]; + T *z = new T[N]; + T *q = new T[N]; + __msan_poison(x, N * sizeof(T)); + __msan_set_origin(x, N * sizeof(T), ox); + __msan_set_origin(y, N * sizeof(T), 777777); + __msan_set_origin(z, N * sizeof(T), 888888); + EXPECT_NOT_POISONED(x); + memcpy(y, x, N * sizeof(T)); + EXPECT_POISONED_O(y[0], ox); + EXPECT_POISONED_O(y[N/2], ox); + EXPECT_POISONED_O(y[N-1], ox); + EXPECT_NOT_POISONED(x); + void *res = mempcpy(q, x, N * sizeof(T)); + ASSERT_EQ(q + N, res); + EXPECT_POISONED_O(q[0], ox); + EXPECT_POISONED_O(q[N/2], ox); + EXPECT_POISONED_O(q[N-1], ox); + EXPECT_NOT_POISONED(x); + memmove(z, x, N * sizeof(T)); + EXPECT_POISONED_O(z[0], ox); + EXPECT_POISONED_O(z[N/2], ox); + EXPECT_POISONED_O(z[N-1], ox); +} + +TEST(MemorySanitizerOrigins, LargeMemCpy) { + if (!TrackingOrigins()) return; + MemCpyTest(); + MemCpyTest(); +} + +TEST(MemorySanitizerOrigins, SmallMemCpy) { + if (!TrackingOrigins()) return; + MemCpyTest(); + MemCpyTest(); + MemCpyTest(); +} + +TEST(MemorySanitizerOrigins, Select) { + if (!TrackingOrigins()) return; + EXPECT_NOT_POISONED(g_one ? 1 : *GetPoisonedO(0, __LINE__)); + EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); + S4 x; + break_optimization(&x); + x = g_1 ? *GetPoisonedO(0, __LINE__) : 0; + + EXPECT_POISONED_O(g_1 ? *GetPoisonedO(0, __LINE__) : 1, __LINE__); + EXPECT_POISONED_O(g_0 ? 1 : *GetPoisonedO(0, __LINE__), __LINE__); +} + +extern "C" +NOINLINE char AllocaTO() { + int ar[100]; + break_optimization(ar); + return ar[10]; + // fprintf(stderr, "Descr: %s\n", + // __msan_get_origin_descr_if_stack(__msan_get_origin_tls())); +} + +TEST(MemorySanitizerOrigins, Alloca) { + if (!TrackingOrigins()) return; + EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); + EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); + EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); + EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); +} + +// FIXME: replace with a lit-like test. +TEST(MemorySanitizerOrigins, DISABLED_AllocaDeath) { + if (!TrackingOrigins()) return; + EXPECT_DEATH(AllocaTO(), "ORIGIN: stack allocation: ar@AllocaTO"); +} + +NOINLINE int RetvalOriginTest(U4 origin) { + int *a = new int; + break_optimization(a); + __msan_set_origin(a, sizeof(*a), origin); + int res = *a; + delete a; + return res; +} + +TEST(MemorySanitizerOrigins, Retval) { + if (!TrackingOrigins()) return; + EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); +} + +NOINLINE void ParamOriginTest(int param, U4 origin) { + EXPECT_POISONED_O(param, origin); +} + +TEST(MemorySanitizerOrigins, Param) { + if (!TrackingOrigins()) return; + int *a = new int; + U4 origin = __LINE__; + break_optimization(a); + __msan_set_origin(a, sizeof(*a), origin); + ParamOriginTest(*a, origin); + delete a; +} + +TEST(MemorySanitizerOrigins, Invoke) { + if (!TrackingOrigins()) return; + StructWithDtor s; // Will cause the calls to become invokes. + EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); +} + +TEST(MemorySanitizerOrigins, strlen) { + S8 alignment; + break_optimization(&alignment); + char x[4] = {'a', 'b', 0, 0}; + __msan_poison(&x[2], 1); + U4 origin = __LINE__; + __msan_set_origin(x, sizeof(x), origin); + EXPECT_UMR_O(volatile unsigned y = strlen(x), origin); +} + +TEST(MemorySanitizerOrigins, wcslen) { + wchar_t w[3] = {'a', 'b', 0}; + U4 origin = __LINE__; + __msan_set_origin(w, sizeof(w), origin); + __msan_poison(&w[2], sizeof(wchar_t)); + EXPECT_UMR_O(volatile unsigned y = wcslen(w), origin); +} + +#if MSAN_HAS_M128 +TEST(MemorySanitizerOrigins, StoreIntrinsic) { + __m128 x, y; + U4 origin = __LINE__; + __msan_set_origin(&x, sizeof(x), origin); + __msan_poison(&x, sizeof(x)); + __builtin_ia32_storeups((float*)&y, x); + EXPECT_POISONED_O(y, origin); +} +#endif + +NOINLINE void RecursiveMalloc(int depth) { + static int count; + count++; + if ((count % (1024 * 1024)) == 0) + printf("RecursiveMalloc: %d\n", count); + int *x1 = new int; + int *x2 = new int; + break_optimization(x1); + break_optimization(x2); + if (depth > 0) { + RecursiveMalloc(depth-1); + RecursiveMalloc(depth-1); + } + delete x1; + delete x2; +} + +TEST(MemorySanitizer, Select) { + int x; + int volatile* p = &x; + int z = *p ? 1 : 0; + EXPECT_POISONED(z); +} + +TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { + RecursiveMalloc(22); +} + +TEST(MemorySanitizerAllocator, get_estimated_allocated_size) { + size_t sizes[] = {0, 20, 5000, 1<<20}; + for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { + size_t alloc_size = __msan_get_estimated_allocated_size(sizes[i]); + EXPECT_EQ(alloc_size, sizes[i]); + } +} + +TEST(MemorySanitizerAllocator, get_allocated_size_and_ownership) { + char *array = reinterpret_cast(malloc(100)); + int *int_ptr = new int; + + EXPECT_TRUE(__msan_get_ownership(array)); + EXPECT_EQ(100, __msan_get_allocated_size(array)); + + EXPECT_TRUE(__msan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(*int_ptr), __msan_get_allocated_size(int_ptr)); + + void *wild_addr = reinterpret_cast(0x1); + EXPECT_FALSE(__msan_get_ownership(wild_addr)); + EXPECT_EQ(0, __msan_get_allocated_size(wild_addr)); + + EXPECT_FALSE(__msan_get_ownership(array + 50)); + EXPECT_EQ(0, __msan_get_allocated_size(array + 50)); + + // NULL is a valid argument for GetAllocatedSize but is not owned. + EXPECT_FALSE(__msan_get_ownership(NULL)); + EXPECT_EQ(0, __msan_get_allocated_size(NULL)); + + free(array); + EXPECT_FALSE(__msan_get_ownership(array)); + EXPECT_EQ(0, __msan_get_allocated_size(array)); + + delete int_ptr; +} + +TEST(MemorySanitizer, MlockTest) { + EXPECT_EQ(0, mlockall(MCL_CURRENT)); + EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); + EXPECT_EQ(0, munlockall()); + EXPECT_EQ(0, munlock((void*)0x987, 0x654)); +} diff --git a/projects/compiler-rt/lib/msan/tests/msan_test_config.h b/projects/compiler-rt/lib/msan/tests/msan_test_config.h new file mode 100644 index 00000000..5404c434 --- /dev/null +++ b/projects/compiler-rt/lib/msan/tests/msan_test_config.h @@ -0,0 +1,20 @@ +//===-- msan_test_config.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer unit tests. +//===----------------------------------------------------------------------===// + +#ifndef MSAN_TEST_CONFIG_H +#define MSAN_TEST_CONFIG_H + +#include "gtest/gtest.h" + +#endif // MSAN_TEST_CONFIG_H diff --git a/projects/compiler-rt/lib/msan/tests/msan_test_main.cc b/projects/compiler-rt/lib/msan/tests/msan_test_main.cc new file mode 100644 index 00000000..c8c5fefb --- /dev/null +++ b/projects/compiler-rt/lib/msan/tests/msan_test_main.cc @@ -0,0 +1,21 @@ +//===-- msan_test_main.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +//===----------------------------------------------------------------------===// +#ifndef MSAN_EXTERNAL_TEST_CONFIG +#include "msan_test_config.h" +#endif // MSAN_EXTERNAL_TEST_CONFIG + +int main(int argc, char **argv) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/projects/compiler-rt/lib/msan/tests/msandr_test_so.cc b/projects/compiler-rt/lib/msan/tests/msandr_test_so.cc new file mode 100644 index 00000000..eb605d4d --- /dev/null +++ b/projects/compiler-rt/lib/msan/tests/msandr_test_so.cc @@ -0,0 +1,38 @@ +//===-- msandr_test_so.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer unit tests. +//===----------------------------------------------------------------------===// + +#include "msandr_test_so.h" + +void dso_memfill(char* s, unsigned n) { + for (unsigned i = 0; i < n; ++i) + s[i] = i; +} + +int dso_callfn(int (*fn)(void)) { + volatile int x = fn(); + return x; +} + +int dso_callfn1(int (*fn)(long long, long long, long long)) { //NOLINT + volatile int x = fn(1, 2, 3); + return x; +} + +int dso_stack_store(void (*fn)(int*, int*), int x) { + int y = x + 1; + fn(&x, &y); + return y; +} + +void break_optimization(void *x) {} diff --git a/projects/compiler-rt/lib/msan/tests/msandr_test_so.h b/projects/compiler-rt/lib/msan/tests/msandr_test_so.h new file mode 100644 index 00000000..cd75ff34 --- /dev/null +++ b/projects/compiler-rt/lib/msan/tests/msandr_test_so.h @@ -0,0 +1,24 @@ +//===-- msandr_test_so.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// MemorySanitizer unit tests. +//===----------------------------------------------------------------------===// + +#ifndef MSANDR_MSANDR_TEST_SO_H +#define MSANDR_MSANDR_TEST_SO_H + +void dso_memfill(char* s, unsigned n); +int dso_callfn(int (*fn)(void)); +int dso_callfn1(int (*fn)(long long, long long, long long)); //NOLINT +int dso_stack_store(void (*fn)(int*, int*), int x); +void break_optimization(void *x); + +#endif diff --git a/projects/compiler-rt/lib/msandr/CMakeLists.txt b/projects/compiler-rt/lib/msandr/CMakeLists.txt new file mode 100644 index 00000000..5a96a9dc --- /dev/null +++ b/projects/compiler-rt/lib/msandr/CMakeLists.txt @@ -0,0 +1,26 @@ + +if(DynamoRIO_DIR AND DrMemoryFramework_DIR) + set(CMAKE_COMPILER_IS_GNUCC 1) + find_package(DynamoRIO) + find_package(DrMemoryFramework) + + set(arch "x86_64") + add_library(clang_rt.msandr-${arch} SHARED msandr.cc) + configure_DynamoRIO_client(clang_rt.msandr-${arch}) + + function(append_target_cflags tgt cflags) + get_property(old_cflags TARGET clang_rt.msandr-${arch} PROPERTY COMPILE_FLAGS) + set_property(TARGET clang_rt.msandr-${arch} PROPERTY COMPILE_FLAGS "${old_cflags} ${cflags}") + endfunction(append_target_cflags) + + append_target_cflags(clang_rt.msandr-${arch} "-Wno-c++11-extensions") + + use_DynamoRIO_extension(clang_rt.msandr-${arch} drutil) + use_DynamoRIO_extension(clang_rt.msandr-${arch} drmgr) + use_DynamoRIO_extension(clang_rt.msandr-${arch} drsyscall) + + set_target_properties(clang_rt.msandr-${arch} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + install(TARGETS clang_rt.msandr-${arch} + LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) +endif() diff --git a/projects/compiler-rt/lib/msandr/README.txt b/projects/compiler-rt/lib/msandr/README.txt new file mode 100644 index 00000000..81a87c69 --- /dev/null +++ b/projects/compiler-rt/lib/msandr/README.txt @@ -0,0 +1,40 @@ +Experimental DynamoRIO-MSAN plugin (codename "MSanDR"). +Supports Linux/x86_64 only. + +Building: + 1. First, download and build DynamoRIO: + (svn co https://dynamorio.googlecode.com/svn/trunk dr && \ + cd dr && mkdir build && cd build && \ + cmake -DDR_EXT_DRMGR_STATIC=ON -DDR_EXT_DRSYMS_STATIC=ON \ + -DDR_EXT_DRUTIL_STATIC=ON -DDR_EXT_DRWRAP_STATIC=ON \ + -DDR_EXT_DRX_STATIC=ON .. && \ + make -j10 && make install) + + 2. Download and build DrMemory (for DrSyscall extension) + (svn co http://drmemory.googlecode.com/svn/trunk/ drmemory && \ + cd drmemory && mkdir build && cd build && \ + cmake -DDynamoRIO_DIR=`pwd`/../../dr/exports/cmake .. && \ + make -j10 && make install) + + NOTE: The line above will build a shared DrSyscall library in a non-standard + location. This will require the use of LD_LIBRARY_PATH when running MSanDR. + To build a static DrSyscall library (and link it into MSanDR), add + -DDR_EXT_DRSYSCALL_STATIC=ON to the CMake invocation above, but + beware: DrSyscall is LGPL. + + 3. Now, build LLVM with two extra CMake flags: + -DDynamoRIO_DIR=/exports/cmake + -DDrMemoryFramework_DIR=/exports64/drmf + + This will build a lib/clang/$VERSION/lib/linux/libclang_rt.msandr-x86_64.so + +Running: + /exports/bin64/drrun -c lib/clang/$VERSION/lib/linux/libclang_rt.msandr-x86_64.so -- test_binary + +MSan unit tests contain several tests for MSanDR (use MemorySanitizerDr.* gtest filter). + +Debugging: + Add -DCMAKE_BUILD_TYPE=Debug to the first and/or second cmake invocation(s). + Add -debug -v to drrun invocation line (right before -c). + Add -checklevel 1 to drrun (as the first argument) to make debug DR faster. + diff --git a/projects/compiler-rt/lib/msandr/msandr.cc b/projects/compiler-rt/lib/msandr/msandr.cc new file mode 100644 index 00000000..6c9d7c67 --- /dev/null +++ b/projects/compiler-rt/lib/msandr/msandr.cc @@ -0,0 +1,871 @@ +//===-- msandr.cc ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// DynamoRio client for MemorySanitizer. +// +// MemorySanitizer requires that all program code is instrumented. Any memory +// store that can turn an uninitialized value into an initialized value must be +// observed by the tool, otherwise we risk reporting a false UMR. +// +// This also includes any libraries that the program depends on. +// +// In the case when rebuilding all program dependencies with MemorySanitizer is +// problematic, an experimental MSanDR tool (the code you are currently looking +// at) can be used. It is a DynamoRio-based tool that uses dynamic +// instrumentation to +// * Unpoison all memory stores. +// * Unpoison TLS slots used by MemorySanitizer to pass function arguments and +// return value shadow on anything that looks like a function call or a return +// from a function. +// +// This tool does not detect the use of uninitialized values in uninstrumented +// libraries. It merely gets rid of false positives by marking all data that +// passes through uninstrumented code as fully initialized. +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +#include +#include /* for SYS_mmap */ + +#include +#include +#include +#include +#include + +#define TESTALL(mask, var) (((mask) & (var)) == (mask)) +#define TESTANY(mask, var) (((mask) & (var)) != 0) + +#define CHECK_IMPL(condition, file, line) \ + do { \ + if (!(condition)) { \ + dr_printf("Check failed: `%s`\nat %s:%d\n", #condition, file, line); \ + dr_abort(); \ + } \ + } while (0) // TODO: stacktrace + +#define CHECK(condition) CHECK_IMPL(condition, __FILE__, __LINE__) + +#define VERBOSITY 0 + +// XXX: it seems setting macro in CMakeLists.txt does not work, +// so manually set it here now. + +// Building msandr client for running in DynamoRIO hybrid mode, +// which allows some module running natively. +// TODO: turn it on by default when hybrid is stable enough +// #define MSANDR_NATIVE_EXEC + +// Building msandr client for standalone test that does not need to +// run with msan build executables. Disable by default. +// #define MSANDR_STANDALONE_TEST + +#define NUM_TLS_RETVAL 1 +#define NUM_TLS_PARAM 6 + +#ifdef MSANDR_STANDALONE_TEST +// For testing purpose, we map app to shadow memory at [0x100000, 0x20000). +// Normally, the app starts at 0x400000: +// 00400000-004e0000 r-xp 00000000 fc:00 524343 /bin/bash +// so there should be no problem. +# define SHADOW_MEMORY_BASE ((void *)0x100000) +# define SHADOW_MEMORY_SIZE (0x100000) +# define SHADOW_MEMORY_MASK (SHADOW_MEMORY_SIZE - 4 /* to avoid overflow */) +#else +// shadow memory range [0x200000000000, 0x400000000000) +// assuming no app memory below 0x200000000000 +# define SHADOW_MEMORY_MASK 0x3fffffffffffULL +#endif /* MSANDR_STANDALONE_TEST */ + +namespace { + +std::string g_app_path; + +int msan_retval_tls_offset; +int msan_param_tls_offset; + +#ifndef MSANDR_NATIVE_EXEC +class ModuleData { +public: + ModuleData(); + ModuleData(const module_data_t *info); + // Yes, we want default copy, assign, and dtor semantics. + +public: + app_pc start_; + app_pc end_; + // Full path to the module. + std::string path_; + module_handle_t handle_; + bool should_instrument_; + bool executed_; +}; + +// A vector of loaded modules sorted by module bounds. We lookup the current PC +// in here from the bb event. This is better than an rb tree because the lookup +// is faster and the bb event occurs far more than the module load event. +std::vector g_module_list; + +ModuleData::ModuleData() + : start_(NULL), end_(NULL), path_(""), handle_(NULL), + should_instrument_(false), executed_(false) { +} + +ModuleData::ModuleData(const module_data_t *info) + : start_(info->start), end_(info->end), path_(info->full_path), + handle_(info->handle), + // We'll check the black/white lists later and adjust this. + should_instrument_(true), executed_(false) { +} +#endif /* !MSANDR_NATIVE_EXEC */ + +int(*__msan_get_retval_tls_offset)(); +int(*__msan_get_param_tls_offset)(); +void (*__msan_unpoison)(void *base, size_t size); +bool (*__msan_is_in_loader)(); + +#ifdef MSANDR_STANDALONE_TEST +uint mock_msan_retval_tls_offset; +uint mock_msan_param_tls_offset; +static int mock_msan_get_retval_tls_offset() { + return (int)mock_msan_retval_tls_offset; +} + +static int mock_msan_get_param_tls_offset() { + return (int)mock_msan_param_tls_offset; +} + +static void mock_msan_unpoison(void *base, size_t size) { + /* do nothing */ +} + +static bool mock_msan_is_in_loader() { + return false; +} +#endif /* MSANDR_STANDALONE_TEST */ + +static generic_func_t LookupCallback(module_data_t *app, const char *name) { +#ifdef MSANDR_STANDALONE_TEST + if (strcmp("__msan_get_retval_tls_offset", name) == 0) { + return (generic_func_t)mock_msan_get_retval_tls_offset; + } else if (strcmp("__msan_get_param_tls_offset", name) == 0) { + return (generic_func_t)mock_msan_get_param_tls_offset; + } else if (strcmp("__msan_unpoison", name) == 0) { + return (generic_func_t)mock_msan_unpoison; + } else if (strcmp("__msan_is_in_loader", name) == 0) { + return (generic_func_t)mock_msan_is_in_loader; + } + CHECK(false); + return NULL; +#else /* !MSANDR_STANDALONE_TEST */ + generic_func_t callback = dr_get_proc_address(app->handle, name); + if (callback == NULL) { + dr_printf("Couldn't find `%s` in %s\n", name, app->full_path); + CHECK(callback); + } + return callback; +#endif /* !MSANDR_STANDALONE_TEST */ +} + +void InitializeMSanCallbacks() { + module_data_t *app = dr_lookup_module_by_name(dr_get_application_name()); + if (!app) { + dr_printf("%s - oops, dr_lookup_module_by_name failed!\n", + dr_get_application_name()); + CHECK(app); + } + g_app_path = app->full_path; + + __msan_get_retval_tls_offset = (int (*)()) + LookupCallback(app, "__msan_get_retval_tls_offset"); + __msan_get_param_tls_offset = (int (*)()) + LookupCallback(app, "__msan_get_param_tls_offset"); + __msan_unpoison = (void(*)(void *, size_t)) + LookupCallback(app, "__msan_unpoison"); + __msan_is_in_loader = (bool (*)()) + LookupCallback(app, "__msan_is_in_loader"); + + dr_free_module_data(app); +} + +// FIXME: Handle absolute addresses and PC-relative addresses. +// FIXME: Handle TLS accesses via FS or GS. DR assumes all other segments have +// a zero base anyway. +bool OperandIsInteresting(opnd_t opnd) { + return (opnd_is_base_disp(opnd) && opnd_get_segment(opnd) != DR_SEG_FS && + opnd_get_segment(opnd) != DR_SEG_GS); +} + +bool WantToInstrument(instr_t *instr) { + // TODO: skip push instructions? + switch (instr_get_opcode(instr)) { + // FIXME: support the instructions excluded below: + case OP_rep_cmps: + // f3 a6 rep cmps %ds:(%rsi) %es:(%rdi) %rsi %rdi %rcx -> %rsi %rdi %rcx + return false; + } + + // Labels appear due to drutil_expand_rep_string() + if (instr_is_label(instr)) + return false; + + CHECK(instr_ok_to_mangle(instr) == true); + + if (instr_writes_memory(instr)) { + for (int d = 0; d < instr_num_dsts(instr); d++) { + opnd_t op = instr_get_dst(instr, d); + if (OperandIsInteresting(op)) + return true; + } + } + + return false; +} + +#define PRE(at, what) instrlist_meta_preinsert(bb, at, INSTR_CREATE_##what); +#define PREF(at, what) instrlist_meta_preinsert(bb, at, what); + +void InstrumentMops(void *drcontext, instrlist_t *bb, instr_t *instr, opnd_t op, + bool is_write) { + bool need_to_restore_eflags = false; + uint flags = instr_get_arith_flags(instr); + // TODO: do something smarter with flags and spills in general? + // For example, spill them only once for a sequence of instrumented + // instructions that don't change/read flags. + + if (!TESTALL(EFLAGS_WRITE_6, flags) || TESTANY(EFLAGS_READ_6, flags)) { + if (VERBOSITY > 1) + dr_printf("Spilling eflags...\n"); + need_to_restore_eflags = true; + // TODO: Maybe sometimes don't need to 'seto'. + // TODO: Maybe sometimes don't want to spill XAX here? + // TODO: No need to spill XAX here if XAX is not used in the BB. + dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + dr_save_arith_flags_to_xax(drcontext, bb, instr); + dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_3); + dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + } + +#if 0 + dr_printf("==DRMSAN== DEBUG: %d %d %d %d %d %d\n", + opnd_is_memory_reference(op), opnd_is_base_disp(op), + opnd_is_base_disp(op) ? opnd_get_index(op) : -1, + opnd_is_far_memory_reference(op), opnd_is_reg_pointer_sized(op), + opnd_is_base_disp(op) ? opnd_get_disp(op) : -1); +#endif + + reg_id_t R1; + bool address_in_R1 = false; + if (opnd_is_base_disp(op) && opnd_get_index(op) == DR_REG_NULL && + opnd_get_disp(op) == 0) { + // If this is a simple access with no offset or index, we can just use the + // base for R1. + address_in_R1 = true; + R1 = opnd_get_base(op); + } else { + // Otherwise, we need to compute the addr into R1. + // TODO: reuse some spare register? e.g. r15 on x64 + // TODO: might be used as a non-mem-ref register? + R1 = DR_REG_XAX; + } + CHECK(reg_is_pointer_sized(R1)); // otherwise R2 may be wrong. + + // Pick R2 from R8 to R15. + // It's OK if the instr uses R2 elsewhere, since we'll restore it before instr. + reg_id_t R2; + for (R2 = DR_REG_R8; R2 <= DR_REG_R15; R2++) { + if (!opnd_uses_reg(op, R2)) + break; + } + CHECK((R2 <= DR_REG_R15) && R1 != R2); + + // Save the current values of R1 and R2. + dr_save_reg(drcontext, bb, instr, R1, SPILL_SLOT_1); + // TODO: Something smarter than spilling a "fixed" register R2? + dr_save_reg(drcontext, bb, instr, R2, SPILL_SLOT_2); + + if (!address_in_R1) + CHECK(drutil_insert_get_mem_addr(drcontext, bb, instr, op, R1, R2)); + PRE(instr, mov_imm(drcontext, opnd_create_reg(R2), + OPND_CREATE_INT64(SHADOW_MEMORY_MASK))); + PRE(instr, and(drcontext, opnd_create_reg(R1), opnd_create_reg(R2))); +#ifdef MSANDR_STANDALONE_TEST + PRE(instr, add(drcontext, opnd_create_reg(R1), + OPND_CREATE_INT32(SHADOW_MEMORY_BASE))); +#endif + // There is no mov_st of a 64-bit immediate, so... + opnd_size_t op_size = opnd_get_size(op); + CHECK(op_size != OPSZ_NA); + uint access_size = opnd_size_in_bytes(op_size); + if (access_size <= 4 || op_size == OPSZ_PTR /* x64 support sign extension */) { + instr_t *label = INSTR_CREATE_label(drcontext); + opnd_t immed; + if (op_size == OPSZ_PTR || op_size == OPSZ_4) + immed = OPND_CREATE_INT32(0); + else + immed = opnd_create_immed_int((ptr_int_t) 0, op_size); + // we check if target is 0 before write to reduce unnecessary memory stores. + PRE(instr, cmp(drcontext, + opnd_create_base_disp(R1, DR_REG_NULL, 0, 0, op_size), + immed)); + PRE(instr, jcc(drcontext, OP_je, opnd_create_instr(label))); + PRE(instr, mov_st(drcontext, + opnd_create_base_disp(R1, DR_REG_NULL, 0, 0, op_size), + immed)); + PREF(instr, label); + } else { + // FIXME: tail? + for (uint ofs = 0; ofs < access_size; ofs += 4) { + instr_t *label = INSTR_CREATE_label(drcontext); + opnd_t immed = OPND_CREATE_INT32(0); + PRE(instr, cmp(drcontext, OPND_CREATE_MEM32(R1, ofs), immed)); + PRE(instr, jcc(drcontext, OP_je, opnd_create_instr(label))); + PRE(instr, mov_st(drcontext, OPND_CREATE_MEM32(R1, ofs), immed)); + PREF(instr, label) + } + } + + // Restore the registers and flags. + dr_restore_reg(drcontext, bb, instr, R1, SPILL_SLOT_1); + dr_restore_reg(drcontext, bb, instr, R2, SPILL_SLOT_2); + + // TODO: move aflags save/restore to per instr instead of per opnd + if (need_to_restore_eflags) { + if (VERBOSITY > 1) + dr_printf("Restoring eflags\n"); + // TODO: Check if it's reverse to the dr_restore_reg above and optimize. + dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_3); + dr_restore_arith_flags_from_xax(drcontext, bb, instr); + dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + } + + // The original instruction is left untouched. The above instrumentation is just + // a prefix. +} + +void InstrumentReturn(void *drcontext, instrlist_t *bb, instr_t *instr) { +#ifdef MSANDR_STANDALONE_TEST + PRE(instr, + mov_st(drcontext, + opnd_create_far_base_disp(DR_SEG_GS /* DR's TLS */, + DR_REG_NULL, DR_REG_NULL, + 0, msan_retval_tls_offset, + OPSZ_PTR), + OPND_CREATE_INT32(0))); +#else /* !MSANDR_STANDALONE_TEST */ + /* XXX: the code below only works if -mangle_app_seg and -private_loader, + * which is turned of for optimized native exec + */ + dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + + // Clobbers nothing except xax. + bool res = + dr_insert_get_seg_base(drcontext, bb, instr, DR_SEG_FS, DR_REG_XAX); + CHECK(res); + + // TODO: unpoison more bytes? + PRE(instr, + mov_st(drcontext, OPND_CREATE_MEM64(DR_REG_XAX, msan_retval_tls_offset), + OPND_CREATE_INT32(0))); + + dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + + // The original instruction is left untouched. The above instrumentation is just + // a prefix. +#endif /* !MSANDR_STANDALONE_TEST */ +} + +void InstrumentIndirectBranch(void *drcontext, instrlist_t *bb, + instr_t *instr) { +#ifdef MSANDR_STANDALONE_TEST + for (int i = 0; i < NUM_TLS_PARAM; ++i) { + PRE(instr, + mov_st(drcontext, + opnd_create_far_base_disp(DR_SEG_GS /* DR's TLS */, + DR_REG_NULL, DR_REG_NULL, + 0, + msan_param_tls_offset + + i * sizeof(void *), + OPSZ_PTR), + OPND_CREATE_INT32(0))); + } +#else /* !MSANDR_STANDALONE_TEST */ + /* XXX: the code below only works if -mangle_app_seg and -private_loader, + * which is turned off for optimized native exec + */ + dr_save_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + + // Clobbers nothing except xax. + bool res = + dr_insert_get_seg_base(drcontext, bb, instr, DR_SEG_FS, DR_REG_XAX); + CHECK(res); + + // TODO: unpoison more bytes? + for (int i = 0; i < NUM_TLS_PARAM; ++i) { + PRE(instr, + mov_st(drcontext, OPND_CREATE_MEMPTR(DR_REG_XAX, msan_param_tls_offset + + i * sizeof(void *)), + OPND_CREATE_INT32(0))); + } + + dr_restore_reg(drcontext, bb, instr, DR_REG_XAX, SPILL_SLOT_1); + + // The original instruction is left untouched. The above instrumentation is just + // a prefix. +#endif /* !MSANDR_STANDALONE_TEST */ +} + +#ifndef MSANDR_NATIVE_EXEC +// For use with binary search. Modules shouldn't overlap, so we shouldn't have +// to look at end_. If that can happen, we won't support such an application. +bool ModuleDataCompareStart(const ModuleData &left, const ModuleData &right) { + return left.start_ < right.start_; +} + +// Look up the module containing PC. Should be relatively fast, as its called +// for each bb instrumentation. +ModuleData *LookupModuleByPC(app_pc pc) { + ModuleData fake_mod_data; + fake_mod_data.start_ = pc; + std::vector::iterator it = + lower_bound(g_module_list.begin(), g_module_list.end(), fake_mod_data, + ModuleDataCompareStart); + // if (it == g_module_list.end()) + // return NULL; + if (it == g_module_list.end() || pc < it->start_) + --it; + CHECK(it->start_ <= pc); + if (pc >= it->end_) { + // We're past the end of this module. We shouldn't be in the next module, + // or lower_bound lied to us. + ++it; + CHECK(it == g_module_list.end() || pc < it->start_); + return NULL; + } + + // OK, we found the module. + return &*it; +} + +bool ShouldInstrumentNonModuleCode() { return true; } + +bool ShouldInstrumentModule(ModuleData *mod_data) { + // TODO(rnk): Flags for blacklist would get wired in here. + generic_func_t p = + dr_get_proc_address(mod_data->handle_, "__msan_track_origins"); + return !p; +} + +bool ShouldInstrumentPc(app_pc pc, ModuleData **pmod_data) { + ModuleData *mod_data = LookupModuleByPC(pc); + if (pmod_data) + *pmod_data = mod_data; + if (mod_data != NULL) { + // This module is on a blacklist. + if (!mod_data->should_instrument_) { + return false; + } + } else if (!ShouldInstrumentNonModuleCode()) { + return false; + } + return true; +} +#endif /* !MSANDR_NATIVE_CLIENT */ + +// TODO(rnk): Make sure we instrument after __msan_init. +dr_emit_flags_t +event_basic_block_app2app(void *drcontext, void *tag, instrlist_t *bb, + bool for_trace, bool translating) { +#ifndef MSANDR_NATIVE_EXEC + app_pc pc = dr_fragment_app_pc(tag); + if (ShouldInstrumentPc(pc, NULL)) + CHECK(drutil_expand_rep_string(drcontext, bb)); +#else /* MSANDR_NATIVE_EXEC */ + CHECK(drutil_expand_rep_string(drcontext, bb)); +#endif /* MSANDR_NATIVE_EXEC */ + return DR_EMIT_PERSISTABLE; +} + +dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, + bool for_trace, bool translating) { + app_pc pc = dr_fragment_app_pc(tag); +#ifndef MSANDR_NATIVE_EXEC + ModuleData *mod_data; + + if (!ShouldInstrumentPc(pc, &mod_data)) + return DR_EMIT_PERSISTABLE; + + if (VERBOSITY > 1) + dr_printf("============================================================\n"); + if (VERBOSITY > 0) { + std::string mod_path = (mod_data ? mod_data->path_ : ""); + if (mod_data && !mod_data->executed_) { + mod_data->executed_ = true; // Nevermind this race. + dr_printf("Executing from new module: %s\n", mod_path.c_str()); + } + dr_printf("BB to be instrumented: %p [from %s]; translating = %s\n", pc, + mod_path.c_str(), translating ? "true" : "false"); + if (mod_data) { + // Match standard sanitizer trace format for free symbols. + // #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) + dr_printf(" #0 %p (%s+%p)\n", pc, mod_data->path_.c_str(), + pc - mod_data->start_); + } + } +#endif /* !MSANDR_NATIVE_EXEC */ + + if (VERBOSITY > 1) { + instrlist_disassemble(drcontext, pc, bb, STDOUT); + instr_t *instr; + for (instr = instrlist_first(bb); instr; instr = instr_get_next(instr)) { + dr_printf("opcode: %d\n", instr_get_opcode(instr)); + } + } + + for (instr_t *i = instrlist_first(bb); i != NULL; i = instr_get_next(i)) { + int opcode = instr_get_opcode(i); + if (opcode == OP_ret || opcode == OP_ret_far) { + InstrumentReturn(drcontext, bb, i); + continue; + } + + // These instructions hopefully cover all cases where control is transferred + // to a function in a different module (we only care about calls into + // compiler-instrumented modules). + // * call_ind is used for normal indirect calls. + // * jmp_ind is used for indirect tail calls, and calls through PLT (PLT + // stub includes a jump to an address from GOT). + if (opcode == OP_call_ind || opcode == OP_call_far_ind || + opcode == OP_jmp_ind || opcode == OP_jmp_far_ind) { + InstrumentIndirectBranch(drcontext, bb, i); + continue; + } + + if (!WantToInstrument(i)) + continue; + + if (VERBOSITY > 1) { + app_pc orig_pc = dr_fragment_app_pc(tag); + uint flags = instr_get_arith_flags(i); + dr_printf("+%d -> to be instrumented! [opcode=%d, flags = 0x%08X]\n", + instr_get_app_pc(i) - orig_pc, instr_get_opcode(i), flags); + } + + if (instr_writes_memory(i)) { + // Instrument memory writes + // bool instrumented_anything = false; + for (int d = 0; d < instr_num_dsts(i); d++) { + opnd_t op = instr_get_dst(i, d); + if (!OperandIsInteresting(op)) + continue; + + // CHECK(!instrumented_anything); + // instrumented_anything = true; + InstrumentMops(drcontext, bb, i, op, true); + break; // only instrumenting the first dst + } + } + } + +// TODO: optimize away redundant restore-spill pairs? + + if (VERBOSITY > 1) { + pc = dr_fragment_app_pc(tag); + dr_printf("\nFinished instrumenting dynamorio_basic_block(PC=" PFX ")\n", pc); + instrlist_disassemble(drcontext, pc, bb, STDOUT); + } + return DR_EMIT_PERSISTABLE; +} + +#ifndef MSANDR_NATIVE_EXEC +void event_module_load(void *drcontext, const module_data_t *info, + bool loaded) { + // Insert the module into the list while maintaining the ordering. + ModuleData mod_data(info); + std::vector::iterator it = + upper_bound(g_module_list.begin(), g_module_list.end(), mod_data, + ModuleDataCompareStart); + it = g_module_list.insert(it, mod_data); + // Check if we should instrument this module. + it->should_instrument_ = ShouldInstrumentModule(&*it); + dr_module_set_should_instrument(info->handle, it->should_instrument_); + + if (VERBOSITY > 0) + dr_printf("==DRMSAN== Loaded module: %s [%p...%p], instrumentation is %s\n", + info->full_path, info->start, info->end, + it->should_instrument_ ? "on" : "off"); +} + +void event_module_unload(void *drcontext, const module_data_t *info) { + if (VERBOSITY > 0) + dr_printf("==DRMSAN== Unloaded module: %s [%p...%p]\n", info->full_path, + info->start, info->end); + + // Remove the module from the list. + ModuleData mod_data(info); + std::vector::iterator it = + lower_bound(g_module_list.begin(), g_module_list.end(), mod_data, + ModuleDataCompareStart); + // It's a bug if we didn't actually find the module. + CHECK(it != g_module_list.end() && it->start_ == mod_data.start_ && + it->end_ == mod_data.end_ && it->path_ == mod_data.path_); + g_module_list.erase(it); +} +#endif /* !MSANDR_NATIVE_EXEC */ + +void event_exit() { + // Clean up so DR doesn't tell us we're leaking memory. + drsys_exit(); + drutil_exit(); + drmgr_exit(); + +#ifdef MSANDR_STANDALONE_TEST + /* free tls */ + bool res; + res = dr_raw_tls_cfree(msan_retval_tls_offset, NUM_TLS_RETVAL); + CHECK(res); + res = dr_raw_tls_cfree(msan_param_tls_offset, NUM_TLS_PARAM); + CHECK(res); + /* we do not bother to free the shadow memory */ +#endif /* !MSANDR_STANDALONE_TEST */ + if (VERBOSITY > 0) + dr_printf("==DRMSAN== DONE\n"); +} + +bool event_filter_syscall(void *drcontext, int sysnum) { + // FIXME: only intercept syscalls with memory effects. + return true; /* intercept everything */ +} + +bool drsys_iter_memarg_cb(drsys_arg_t *arg, void *user_data) { + CHECK(arg->valid); + + if (arg->pre) + return true; + if (!TESTANY(DRSYS_PARAM_OUT, arg->mode)) + return true; + + size_t sz = arg->size; + + if (sz > 0xFFFFFFFF) { + drmf_status_t res; + drsys_syscall_t *syscall = (drsys_syscall_t *)user_data; + const char *name; + res = drsys_syscall_name(syscall, &name); + CHECK(res == DRMF_SUCCESS); + + dr_printf("SANITY: syscall '%s' arg %d writes %llu bytes memory?!" + " Clipping to %llu.\n", + name, arg->ordinal, (unsigned long long) sz, + (unsigned long long)(sz & 0xFFFFFFFF)); + } + + if (VERBOSITY > 0) { + drmf_status_t res; + drsys_syscall_t *syscall = (drsys_syscall_t *)user_data; + const char *name; + res = drsys_syscall_name(syscall, &name); + CHECK(res == DRMF_SUCCESS); + dr_printf("drsyscall: syscall '%s' arg %d wrote range [%p, %p)\n", + name, arg->ordinal, arg->start_addr, + (char *)arg->start_addr + sz); + } + + // We don't switch to the app context because __msan_unpoison() doesn't need + // TLS segments. + __msan_unpoison(arg->start_addr, sz); + + return true; /* keep going */ +} + +bool event_pre_syscall(void *drcontext, int sysnum) { + drsys_syscall_t *syscall; + drsys_sysnum_t sysnum_full; + bool known; + drsys_param_type_t ret_type; + drmf_status_t res; + const char *name; + + res = drsys_cur_syscall(drcontext, &syscall); + CHECK(res == DRMF_SUCCESS); + + res = drsys_syscall_number(syscall, &sysnum_full); + CHECK(res == DRMF_SUCCESS); + CHECK(sysnum == sysnum_full.number); + + res = drsys_syscall_is_known(syscall, &known); + CHECK(res == DRMF_SUCCESS); + + res = drsys_syscall_name(syscall, &name); + CHECK(res == DRMF_SUCCESS); + + res = drsys_syscall_return_type(syscall, &ret_type); + CHECK(res == DRMF_SUCCESS); + CHECK(ret_type != DRSYS_TYPE_INVALID); + CHECK(!known || ret_type != DRSYS_TYPE_UNKNOWN); + + res = drsys_iterate_memargs(drcontext, drsys_iter_memarg_cb, NULL); + CHECK(res == DRMF_SUCCESS); + + return true; +} + +static bool IsInLoader(void *drcontext) { + // TODO: This segment swap is inefficient. DR should just let us query the + // app segment base, which it has. Alternatively, if we disable + // -mangle_app_seg, then we won't need the swap. + bool need_swap = !dr_using_app_state(drcontext); + if (need_swap) + dr_switch_to_app_state(drcontext); + bool is_in_loader = __msan_is_in_loader(); + if (need_swap) + dr_switch_to_dr_state(drcontext); + return is_in_loader; +} + +void event_post_syscall(void *drcontext, int sysnum) { + drsys_syscall_t *syscall; + drsys_sysnum_t sysnum_full; + bool success = false; + drmf_status_t res; + + res = drsys_cur_syscall(drcontext, &syscall); + CHECK(res == DRMF_SUCCESS); + + res = drsys_syscall_number(syscall, &sysnum_full); + CHECK(res == DRMF_SUCCESS); + CHECK(sysnum == sysnum_full.number); + + res = drsys_syscall_succeeded(syscall, dr_syscall_get_result(drcontext), + &success); + CHECK(res == DRMF_SUCCESS); + + if (success) { + res = + drsys_iterate_memargs(drcontext, drsys_iter_memarg_cb, (void *)syscall); + CHECK(res == DRMF_SUCCESS); + } + + // Our normal mmap interceptor can't intercept calls from the loader itself. + // This means we don't clear the shadow for calls to dlopen. For now, we + // solve this by intercepting mmap from ld.so here, but ideally we'd have a + // solution that doesn't rely on msandr. + // + // Be careful not to intercept maps done by the msan rtl. Otherwise we end up + // unpoisoning vast regions of memory and OOMing. + // TODO: __msan_unpoison() could "flush" large regions of memory like tsan + // does instead of doing a large memset. However, we need the memory to be + // zeroed, where as tsan does not, so plain madvise is not enough. + if (success && (sysnum == SYS_mmap IF_NOT_X64(|| sysnum == SYS_mmap2))) { + if (IsInLoader(drcontext)) { + app_pc base = (app_pc)dr_syscall_get_result(drcontext); + ptr_uint_t size; + drmf_status_t res = drsys_pre_syscall_arg(drcontext, 1, &size); + CHECK(res == DRMF_SUCCESS); + if (VERBOSITY > 0) + dr_printf("unpoisoning for dlopen: [%p-%p]\n", base, base + size); + // We don't switch to the app context because __msan_unpoison() doesn't + // need TLS segments. + __msan_unpoison(base, size); + } + } +} + +} // namespace + +DR_EXPORT void dr_init(client_id_t id) { + drmf_status_t res; + + drmgr_init(); + drutil_init(); + + std::string app_name = dr_get_application_name(); + // This blacklist will still run these apps through DR's code cache. On the + // other hand, we are able to follow children of these apps. + // FIXME: Once DR has detach, we could just detach here. Alternatively, + // if DR had a fork or exec hook to let us decide there, that would be nice. + // FIXME: make the blacklist cmd-adjustable. + if (app_name == "python" || app_name == "python2.7" || app_name == "bash" || + app_name == "sh" || app_name == "true" || app_name == "exit" || + app_name == "yes" || app_name == "echo") + return; + + drsys_options_t ops; + memset(&ops, 0, sizeof(ops)); + ops.struct_size = sizeof(ops); + ops.analyze_unknown_syscalls = false; + + res = drsys_init(id, &ops); + CHECK(res == DRMF_SUCCESS); + + dr_register_filter_syscall_event(event_filter_syscall); + drmgr_register_pre_syscall_event(event_pre_syscall); + drmgr_register_post_syscall_event(event_post_syscall); + res = drsys_filter_all_syscalls(); + CHECK(res == DRMF_SUCCESS); + +#ifdef MSANDR_STANDALONE_TEST + reg_id_t reg_seg; + /* alloc tls */ + if (!dr_raw_tls_calloc(®_seg, &mock_msan_retval_tls_offset, NUM_TLS_RETVAL, 0)) + CHECK(false); + CHECK(reg_seg == DR_SEG_GS /* x64 only! */); + if (!dr_raw_tls_calloc(®_seg, &mock_msan_param_tls_offset, NUM_TLS_PARAM, 0)) + CHECK(false); + CHECK(reg_seg == DR_SEG_GS /* x64 only! */); + /* alloc shadow memory */ + if (mmap(SHADOW_MEMORY_BASE, SHADOW_MEMORY_SIZE, PROT_READ|PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0) != SHADOW_MEMORY_BASE) { + CHECK(false); + } +#endif /* MSANDR_STANDALONE_TEST */ + InitializeMSanCallbacks(); + + // FIXME: the shadow is initialized earlier when DR calls one of our wrapper + // functions. This may change one day. + // TODO: make this more robust. + + void *drcontext = dr_get_current_drcontext(); + + dr_switch_to_app_state(drcontext); + msan_retval_tls_offset = __msan_get_retval_tls_offset(); + msan_param_tls_offset = __msan_get_param_tls_offset(); + dr_switch_to_dr_state(drcontext); + if (VERBOSITY > 0) { + dr_printf("__msan_retval_tls offset: %d\n", msan_retval_tls_offset); + dr_printf("__msan_param_tls offset: %d\n", msan_param_tls_offset); + } + + // Standard DR events. + dr_register_exit_event(event_exit); + + drmgr_priority_t priority = { + sizeof(priority), /* size of struct */ + "msandr", /* name of our operation */ + NULL, /* optional name of operation we should precede */ + NULL, /* optional name of operation we should follow */ + 0 + }; /* numeric priority */ + + drmgr_register_bb_app2app_event(event_basic_block_app2app, &priority); + drmgr_register_bb_instru2instru_event(event_basic_block, &priority); +#ifndef MSANDR_NATIVE_EXEC + drmgr_register_module_load_event(event_module_load); + drmgr_register_module_unload_event(event_module_unload); +#endif /* MSANDR_NATIVE_EXEC */ + if (VERBOSITY > 0) + dr_printf("==MSANDR== Starting!\n"); +} diff --git a/projects/compiler-rt/lib/muldc3.c b/projects/compiler-rt/lib/muldc3.c new file mode 100644 index 00000000..5f4a6d16 --- /dev/null +++ b/projects/compiler-rt/lib/muldc3.c @@ -0,0 +1,73 @@ +/* ===-- muldc3.c - Implement __muldc3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __muldc3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" +#include "int_math.h" + +/* Returns: the product of a + ib and c + id */ + +double _Complex +__muldc3(double __a, double __b, double __c, double __d) +{ + double __ac = __a * __c; + double __bd = __b * __d; + double __ad = __a * __d; + double __bc = __b * __c; + double _Complex z; + __real__ z = __ac - __bd; + __imag__ z = __ad + __bc; + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + { + int __recalc = 0; + if (crt_isinf(__a) || crt_isinf(__b)) + { + __a = crt_copysign(crt_isinf(__a) ? 1 : 0, __a); + __b = crt_copysign(crt_isinf(__b) ? 1 : 0, __b); + if (crt_isnan(__c)) + __c = crt_copysign(0, __c); + if (crt_isnan(__d)) + __d = crt_copysign(0, __d); + __recalc = 1; + } + if (crt_isinf(__c) || crt_isinf(__d)) + { + __c = crt_copysign(crt_isinf(__c) ? 1 : 0, __c); + __d = crt_copysign(crt_isinf(__d) ? 1 : 0, __d); + if (crt_isnan(__a)) + __a = crt_copysign(0, __a); + if (crt_isnan(__b)) + __b = crt_copysign(0, __b); + __recalc = 1; + } + if (!__recalc && (crt_isinf(__ac) || crt_isinf(__bd) || + crt_isinf(__ad) || crt_isinf(__bc))) + { + if (crt_isnan(__a)) + __a = crt_copysign(0, __a); + if (crt_isnan(__b)) + __b = crt_copysign(0, __b); + if (crt_isnan(__c)) + __c = crt_copysign(0, __c); + if (crt_isnan(__d)) + __d = crt_copysign(0, __d); + __recalc = 1; + } + if (__recalc) + { + __real__ z = CRT_INFINITY * (__a * __c - __b * __d); + __imag__ z = CRT_INFINITY * (__a * __d + __b * __c); + } + } + return z; +} diff --git a/projects/compiler-rt/lib/muldf3.c b/projects/compiler-rt/lib/muldf3.c new file mode 100644 index 00000000..c38edba9 --- /dev/null +++ b/projects/compiler-rt/lib/muldf3.c @@ -0,0 +1,122 @@ +//===-- lib/muldf3.c - Double-precision multiplication ------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float multiplication +// with the IEEE-754 default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(dmul, muldf3) + +COMPILER_RT_ABI fp_t +__muldf3(fp_t a, fp_t b) { + + const unsigned int aExponent = toRep(a) >> significandBits & maxExponent; + const unsigned int bExponent = toRep(b) >> significandBits & maxExponent; + const rep_t productSign = (toRep(a) ^ toRep(b)) & signBit; + + rep_t aSignificand = toRep(a) & significandMask; + rep_t bSignificand = toRep(b) & significandMask; + int scale = 0; + + // Detect if a or b is zero, denormal, infinity, or NaN. + if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) { + + const rep_t aAbs = toRep(a) & absMask; + const rep_t bAbs = toRep(b) & absMask; + + // NaN * anything = qNaN + if (aAbs > infRep) return fromRep(toRep(a) | quietBit); + // anything * NaN = qNaN + if (bAbs > infRep) return fromRep(toRep(b) | quietBit); + + if (aAbs == infRep) { + // infinity * non-zero = +/- infinity + if (bAbs) return fromRep(aAbs | productSign); + // infinity * zero = NaN + else return fromRep(qnanRep); + } + + if (bAbs == infRep) { + // non-zero * infinity = +/- infinity + if (aAbs) return fromRep(bAbs | productSign); + // zero * infinity = NaN + else return fromRep(qnanRep); + } + + // zero * anything = +/- zero + if (!aAbs) return fromRep(productSign); + // anything * zero = +/- zero + if (!bAbs) return fromRep(productSign); + + // one or both of a or b is denormal, the other (if applicable) is a + // normal number. Renormalize one or both of a and b, and set scale to + // include the necessary exponent adjustment. + if (aAbs < implicitBit) scale += normalize(&aSignificand); + if (bAbs < implicitBit) scale += normalize(&bSignificand); + } + + // Or in the implicit significand bit. (If we fell through from the + // denormal path it was already set by normalize( ), but setting it twice + // won't hurt anything.) + aSignificand |= implicitBit; + bSignificand |= implicitBit; + + // Get the significand of a*b. Before multiplying the significands, shift + // one of them left to left-align it in the field. Thus, the product will + // have (exponentBits + 2) integral digits, all but two of which must be + // zero. Normalizing this result is just a conditional left-shift by one + // and bumping the exponent accordingly. + rep_t productHi, productLo; + wideMultiply(aSignificand, bSignificand << exponentBits, + &productHi, &productLo); + + int productExponent = aExponent + bExponent - exponentBias + scale; + + // Normalize the significand, adjust exponent if needed. + if (productHi & implicitBit) productExponent++; + else wideLeftShift(&productHi, &productLo, 1); + + // If we have overflowed the type, return +/- infinity. + if (productExponent >= maxExponent) return fromRep(infRep | productSign); + + if (productExponent <= 0) { + // Result is denormal before rounding + // + // If the result is so small that it just underflows to zero, return + // a zero of the appropriate sign. Mathematically there is no need to + // handle this case separately, but we make it a special case to + // simplify the shift logic. + const unsigned int shift = 1U - (unsigned int)productExponent; + if (shift >= typeWidth) return fromRep(productSign); + + // Otherwise, shift the significand of the result so that the round + // bit is the high bit of productLo. + wideRightShiftWithSticky(&productHi, &productLo, shift); + } + + else { + // Result is normal before rounding; insert the exponent. + productHi &= significandMask; + productHi |= (rep_t)productExponent << significandBits; + } + + // Insert the sign of the result: + productHi |= productSign; + + // Final rounding. The final result may overflow to infinity, or underflow + // to zero, but those are the correct results in those cases. We use the + // default IEEE-754 round-to-nearest, ties-to-even rounding mode. + if (productLo > signBit) productHi++; + if (productLo == signBit) productHi += productHi & 1; + return fromRep(productHi); +} diff --git a/projects/compiler-rt/lib/muldi3.c b/projects/compiler-rt/lib/muldi3.c new file mode 100644 index 00000000..2dae44c1 --- /dev/null +++ b/projects/compiler-rt/lib/muldi3.c @@ -0,0 +1,56 @@ +/* ===-- muldi3.c - Implement __muldi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __muldi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a * b */ + +static +di_int +__muldsi3(su_int a, su_int b) +{ + dwords r; + const int bits_in_word_2 = (int)(sizeof(si_int) * CHAR_BIT) / 2; + const su_int lower_mask = (su_int)~0 >> bits_in_word_2; + r.s.low = (a & lower_mask) * (b & lower_mask); + su_int t = r.s.low >> bits_in_word_2; + r.s.low &= lower_mask; + t += (a >> bits_in_word_2) * (b & lower_mask); + r.s.low += (t & lower_mask) << bits_in_word_2; + r.s.high = t >> bits_in_word_2; + t = r.s.low >> bits_in_word_2; + r.s.low &= lower_mask; + t += (b >> bits_in_word_2) * (a & lower_mask); + r.s.low += (t & lower_mask) << bits_in_word_2; + r.s.high += t >> bits_in_word_2; + r.s.high += (a >> bits_in_word_2) * (b >> bits_in_word_2); + return r.all; +} + +/* Returns: a * b */ + +ARM_EABI_FNALIAS(lmul, muldi3) + +COMPILER_RT_ABI di_int +__muldi3(di_int a, di_int b) +{ + dwords x; + x.all = a; + dwords y; + y.all = b; + dwords r; + r.all = __muldsi3(x.s.low, y.s.low); + r.s.high += x.s.high * y.s.low + x.s.low * y.s.high; + return r.all; +} diff --git a/projects/compiler-rt/lib/mulodi4.c b/projects/compiler-rt/lib/mulodi4.c new file mode 100644 index 00000000..0c1b5cda --- /dev/null +++ b/projects/compiler-rt/lib/mulodi4.c @@ -0,0 +1,58 @@ +/*===-- mulodi4.c - Implement __mulodi4 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __mulodi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a * b */ + +/* Effects: sets *overflow to 1 if a * b overflows */ + +di_int +__mulodi4(di_int a, di_int b, int* overflow) +{ + const int N = (int)(sizeof(di_int) * CHAR_BIT); + const di_int MIN = (di_int)1 << (N-1); + const di_int MAX = ~MIN; + *overflow = 0; + di_int result = a * b; + if (a == MIN) + { + if (b != 0 && b != 1) + *overflow = 1; + return result; + } + if (b == MIN) + { + if (a != 0 && a != 1) + *overflow = 1; + return result; + } + di_int sa = a >> (N - 1); + di_int abs_a = (a ^ sa) - sa; + di_int sb = b >> (N - 1); + di_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return result; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + *overflow = 1; + } + else + { + if (abs_a > MIN / -abs_b) + *overflow = 1; + } + return result; +} diff --git a/projects/compiler-rt/lib/mulosi4.c b/projects/compiler-rt/lib/mulosi4.c new file mode 100644 index 00000000..f3398d1f --- /dev/null +++ b/projects/compiler-rt/lib/mulosi4.c @@ -0,0 +1,58 @@ +/*===-- mulosi4.c - Implement __mulosi4 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __mulosi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a * b */ + +/* Effects: sets *overflow to 1 if a * b overflows */ + +si_int +__mulosi4(si_int a, si_int b, int* overflow) +{ + const int N = (int)(sizeof(si_int) * CHAR_BIT); + const si_int MIN = (si_int)1 << (N-1); + const si_int MAX = ~MIN; + *overflow = 0; + si_int result = a * b; + if (a == MIN) + { + if (b != 0 && b != 1) + *overflow = 1; + return result; + } + if (b == MIN) + { + if (a != 0 && a != 1) + *overflow = 1; + return result; + } + si_int sa = a >> (N - 1); + si_int abs_a = (a ^ sa) - sa; + si_int sb = b >> (N - 1); + si_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return result; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + *overflow = 1; + } + else + { + if (abs_a > MIN / -abs_b) + *overflow = 1; + } + return result; +} diff --git a/projects/compiler-rt/lib/muloti4.c b/projects/compiler-rt/lib/muloti4.c new file mode 100644 index 00000000..f58dd074 --- /dev/null +++ b/projects/compiler-rt/lib/muloti4.c @@ -0,0 +1,62 @@ +/*===-- muloti4.c - Implement __muloti4 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __muloti4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: a * b */ + +/* Effects: sets *overflow to 1 if a * b overflows */ + +ti_int +__muloti4(ti_int a, ti_int b, int* overflow) +{ + const int N = (int)(sizeof(ti_int) * CHAR_BIT); + const ti_int MIN = (ti_int)1 << (N-1); + const ti_int MAX = ~MIN; + *overflow = 0; + ti_int result = a * b; + if (a == MIN) + { + if (b != 0 && b != 1) + *overflow = 1; + return result; + } + if (b == MIN) + { + if (a != 0 && a != 1) + *overflow = 1; + return result; + } + ti_int sa = a >> (N - 1); + ti_int abs_a = (a ^ sa) - sa; + ti_int sb = b >> (N - 1); + ti_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return result; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + *overflow = 1; + } + else + { + if (abs_a > MIN / -abs_b) + *overflow = 1; + } + return result; +} + +#endif diff --git a/projects/compiler-rt/lib/mulsc3.c b/projects/compiler-rt/lib/mulsc3.c new file mode 100644 index 00000000..6d433fbc --- /dev/null +++ b/projects/compiler-rt/lib/mulsc3.c @@ -0,0 +1,73 @@ +/* ===-- mulsc3.c - Implement __mulsc3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __mulsc3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" +#include "int_math.h" + +/* Returns: the product of a + ib and c + id */ + +float _Complex +__mulsc3(float __a, float __b, float __c, float __d) +{ + float __ac = __a * __c; + float __bd = __b * __d; + float __ad = __a * __d; + float __bc = __b * __c; + float _Complex z; + __real__ z = __ac - __bd; + __imag__ z = __ad + __bc; + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + { + int __recalc = 0; + if (crt_isinf(__a) || crt_isinf(__b)) + { + __a = crt_copysignf(crt_isinf(__a) ? 1 : 0, __a); + __b = crt_copysignf(crt_isinf(__b) ? 1 : 0, __b); + if (crt_isnan(__c)) + __c = crt_copysignf(0, __c); + if (crt_isnan(__d)) + __d = crt_copysignf(0, __d); + __recalc = 1; + } + if (crt_isinf(__c) || crt_isinf(__d)) + { + __c = crt_copysignf(crt_isinf(__c) ? 1 : 0, __c); + __d = crt_copysignf(crt_isinf(__d) ? 1 : 0, __d); + if (crt_isnan(__a)) + __a = crt_copysignf(0, __a); + if (crt_isnan(__b)) + __b = crt_copysignf(0, __b); + __recalc = 1; + } + if (!__recalc && (crt_isinf(__ac) || crt_isinf(__bd) || + crt_isinf(__ad) || crt_isinf(__bc))) + { + if (crt_isnan(__a)) + __a = crt_copysignf(0, __a); + if (crt_isnan(__b)) + __b = crt_copysignf(0, __b); + if (crt_isnan(__c)) + __c = crt_copysignf(0, __c); + if (crt_isnan(__d)) + __d = crt_copysignf(0, __d); + __recalc = 1; + } + if (__recalc) + { + __real__ z = CRT_INFINITY * (__a * __c - __b * __d); + __imag__ z = CRT_INFINITY * (__a * __d + __b * __c); + } + } + return z; +} diff --git a/projects/compiler-rt/lib/mulsf3.c b/projects/compiler-rt/lib/mulsf3.c new file mode 100644 index 00000000..861a9ba5 --- /dev/null +++ b/projects/compiler-rt/lib/mulsf3.c @@ -0,0 +1,112 @@ +//===-- lib/mulsf3.c - Single-precision multiplication ------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float multiplication +// with the IEEE-754 default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(fmul, mulsf3) + +COMPILER_RT_ABI fp_t +__mulsf3(fp_t a, fp_t b) { + + const unsigned int aExponent = toRep(a) >> significandBits & maxExponent; + const unsigned int bExponent = toRep(b) >> significandBits & maxExponent; + const rep_t productSign = (toRep(a) ^ toRep(b)) & signBit; + + rep_t aSignificand = toRep(a) & significandMask; + rep_t bSignificand = toRep(b) & significandMask; + int scale = 0; + + // Detect if a or b is zero, denormal, infinity, or NaN. + if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) { + + const rep_t aAbs = toRep(a) & absMask; + const rep_t bAbs = toRep(b) & absMask; + + // NaN * anything = qNaN + if (aAbs > infRep) return fromRep(toRep(a) | quietBit); + // anything * NaN = qNaN + if (bAbs > infRep) return fromRep(toRep(b) | quietBit); + + if (aAbs == infRep) { + // infinity * non-zero = +/- infinity + if (bAbs) return fromRep(aAbs | productSign); + // infinity * zero = NaN + else return fromRep(qnanRep); + } + + if (bAbs == infRep) { + // non-zero * infinity = +/- infinity + if (aAbs) return fromRep(bAbs | productSign); + // zero * infinity = NaN + else return fromRep(qnanRep); + } + + // zero * anything = +/- zero + if (!aAbs) return fromRep(productSign); + // anything * zero = +/- zero + if (!bAbs) return fromRep(productSign); + + // one or both of a or b is denormal, the other (if applicable) is a + // normal number. Renormalize one or both of a and b, and set scale to + // include the necessary exponent adjustment. + if (aAbs < implicitBit) scale += normalize(&aSignificand); + if (bAbs < implicitBit) scale += normalize(&bSignificand); + } + + // Or in the implicit significand bit. (If we fell through from the + // denormal path it was already set by normalize( ), but setting it twice + // won't hurt anything.) + aSignificand |= implicitBit; + bSignificand |= implicitBit; + + // Get the significand of a*b. Before multiplying the significands, shift + // one of them left to left-align it in the field. Thus, the product will + // have (exponentBits + 2) integral digits, all but two of which must be + // zero. Normalizing this result is just a conditional left-shift by one + // and bumping the exponent accordingly. + rep_t productHi, productLo; + wideMultiply(aSignificand, bSignificand << exponentBits, + &productHi, &productLo); + + int productExponent = aExponent + bExponent - exponentBias + scale; + + // Normalize the significand, adjust exponent if needed. + if (productHi & implicitBit) productExponent++; + else wideLeftShift(&productHi, &productLo, 1); + + // If we have overflowed the type, return +/- infinity. + if (productExponent >= maxExponent) return fromRep(infRep | productSign); + + if (productExponent <= 0) { + // Result is denormal before rounding, the exponent is zero and we + // need to shift the significand. + wideRightShiftWithSticky(&productHi, &productLo, 1U - (unsigned)productExponent); + } + + else { + // Result is normal before rounding; insert the exponent. + productHi &= significandMask; + productHi |= (rep_t)productExponent << significandBits; + } + + // Insert the sign of the result: + productHi |= productSign; + + // Final rounding. The final result may overflow to infinity, or underflow + // to zero, but those are the correct results in those cases. + if (productLo > signBit) productHi++; + if (productLo == signBit) productHi += productHi & 1; + return fromRep(productHi); +} diff --git a/projects/compiler-rt/lib/multi3.c b/projects/compiler-rt/lib/multi3.c new file mode 100644 index 00000000..0b8730f0 --- /dev/null +++ b/projects/compiler-rt/lib/multi3.c @@ -0,0 +1,58 @@ +/* ===-- multi3.c - Implement __multi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + + * This file implements __multi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: a * b */ + +static +ti_int +__mulddi3(du_int a, du_int b) +{ + twords r; + const int bits_in_dword_2 = (int)(sizeof(di_int) * CHAR_BIT) / 2; + const du_int lower_mask = (du_int)~0 >> bits_in_dword_2; + r.s.low = (a & lower_mask) * (b & lower_mask); + du_int t = r.s.low >> bits_in_dword_2; + r.s.low &= lower_mask; + t += (a >> bits_in_dword_2) * (b & lower_mask); + r.s.low += (t & lower_mask) << bits_in_dword_2; + r.s.high = t >> bits_in_dword_2; + t = r.s.low >> bits_in_dword_2; + r.s.low &= lower_mask; + t += (b >> bits_in_dword_2) * (a & lower_mask); + r.s.low += (t & lower_mask) << bits_in_dword_2; + r.s.high += t >> bits_in_dword_2; + r.s.high += (a >> bits_in_dword_2) * (b >> bits_in_dword_2); + return r.all; +} + +/* Returns: a * b */ + +ti_int +__multi3(ti_int a, ti_int b) +{ + twords x; + x.all = a; + twords y; + y.all = b; + twords r; + r.all = __mulddi3(x.s.low, y.s.low); + r.s.high += x.s.high * y.s.low + x.s.low * y.s.high; + return r.all; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/mulvdi3.c b/projects/compiler-rt/lib/mulvdi3.c new file mode 100644 index 00000000..bcc8e659 --- /dev/null +++ b/projects/compiler-rt/lib/mulvdi3.c @@ -0,0 +1,56 @@ +/*===-- mulvdi3.c - Implement __mulvdi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __mulvdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a * b */ + +/* Effects: aborts if a * b overflows */ + +di_int +__mulvdi3(di_int a, di_int b) +{ + const int N = (int)(sizeof(di_int) * CHAR_BIT); + const di_int MIN = (di_int)1 << (N-1); + const di_int MAX = ~MIN; + if (a == MIN) + { + if (b == 0 || b == 1) + return a * b; + compilerrt_abort(); + } + if (b == MIN) + { + if (a == 0 || a == 1) + return a * b; + compilerrt_abort(); + } + di_int sa = a >> (N - 1); + di_int abs_a = (a ^ sa) - sa; + di_int sb = b >> (N - 1); + di_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return a * b; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + compilerrt_abort(); + } + else + { + if (abs_a > MIN / -abs_b) + compilerrt_abort(); + } + return a * b; +} diff --git a/projects/compiler-rt/lib/mulvsi3.c b/projects/compiler-rt/lib/mulvsi3.c new file mode 100644 index 00000000..d372b201 --- /dev/null +++ b/projects/compiler-rt/lib/mulvsi3.c @@ -0,0 +1,56 @@ +/* ===-- mulvsi3.c - Implement __mulvsi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __mulvsi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a * b */ + +/* Effects: aborts if a * b overflows */ + +si_int +__mulvsi3(si_int a, si_int b) +{ + const int N = (int)(sizeof(si_int) * CHAR_BIT); + const si_int MIN = (si_int)1 << (N-1); + const si_int MAX = ~MIN; + if (a == MIN) + { + if (b == 0 || b == 1) + return a * b; + compilerrt_abort(); + } + if (b == MIN) + { + if (a == 0 || a == 1) + return a * b; + compilerrt_abort(); + } + si_int sa = a >> (N - 1); + si_int abs_a = (a ^ sa) - sa; + si_int sb = b >> (N - 1); + si_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return a * b; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + compilerrt_abort(); + } + else + { + if (abs_a > MIN / -abs_b) + compilerrt_abort(); + } + return a * b; +} diff --git a/projects/compiler-rt/lib/mulvti3.c b/projects/compiler-rt/lib/mulvti3.c new file mode 100644 index 00000000..31f7d2fd --- /dev/null +++ b/projects/compiler-rt/lib/mulvti3.c @@ -0,0 +1,60 @@ +/* ===-- mulvti3.c - Implement __mulvti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __mulvti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: a * b */ + +/* Effects: aborts if a * b overflows */ + +ti_int +__mulvti3(ti_int a, ti_int b) +{ + const int N = (int)(sizeof(ti_int) * CHAR_BIT); + const ti_int MIN = (ti_int)1 << (N-1); + const ti_int MAX = ~MIN; + if (a == MIN) + { + if (b == 0 || b == 1) + return a * b; + compilerrt_abort(); + } + if (b == MIN) + { + if (a == 0 || a == 1) + return a * b; + compilerrt_abort(); + } + ti_int sa = a >> (N - 1); + ti_int abs_a = (a ^ sa) - sa; + ti_int sb = b >> (N - 1); + ti_int abs_b = (b ^ sb) - sb; + if (abs_a < 2 || abs_b < 2) + return a * b; + if (sa == sb) + { + if (abs_a > MAX / abs_b) + compilerrt_abort(); + } + else + { + if (abs_a > MIN / -abs_b) + compilerrt_abort(); + } + return a * b; +} + +#endif diff --git a/projects/compiler-rt/lib/mulxc3.c b/projects/compiler-rt/lib/mulxc3.c new file mode 100644 index 00000000..cec05736 --- /dev/null +++ b/projects/compiler-rt/lib/mulxc3.c @@ -0,0 +1,77 @@ +/* ===-- mulxc3.c - Implement __mulxc3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __mulxc3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#if !_ARCH_PPC + +#include "int_lib.h" +#include "int_math.h" + +/* Returns: the product of a + ib and c + id */ + +long double _Complex +__mulxc3(long double __a, long double __b, long double __c, long double __d) +{ + long double __ac = __a * __c; + long double __bd = __b * __d; + long double __ad = __a * __d; + long double __bc = __b * __c; + long double _Complex z; + __real__ z = __ac - __bd; + __imag__ z = __ad + __bc; + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) + { + int __recalc = 0; + if (crt_isinf(__a) || crt_isinf(__b)) + { + __a = crt_copysignl(crt_isinf(__a) ? 1 : 0, __a); + __b = crt_copysignl(crt_isinf(__b) ? 1 : 0, __b); + if (crt_isnan(__c)) + __c = crt_copysignl(0, __c); + if (crt_isnan(__d)) + __d = crt_copysignl(0, __d); + __recalc = 1; + } + if (crt_isinf(__c) || crt_isinf(__d)) + { + __c = crt_copysignl(crt_isinf(__c) ? 1 : 0, __c); + __d = crt_copysignl(crt_isinf(__d) ? 1 : 0, __d); + if (crt_isnan(__a)) + __a = crt_copysignl(0, __a); + if (crt_isnan(__b)) + __b = crt_copysignl(0, __b); + __recalc = 1; + } + if (!__recalc && (crt_isinf(__ac) || crt_isinf(__bd) || + crt_isinf(__ad) || crt_isinf(__bc))) + { + if (crt_isnan(__a)) + __a = crt_copysignl(0, __a); + if (crt_isnan(__b)) + __b = crt_copysignl(0, __b); + if (crt_isnan(__c)) + __c = crt_copysignl(0, __c); + if (crt_isnan(__d)) + __d = crt_copysignl(0, __d); + __recalc = 1; + } + if (__recalc) + { + __real__ z = CRT_INFINITY * (__a * __c - __b * __d); + __imag__ z = CRT_INFINITY * (__a * __d + __b * __c); + } + } + return z; +} + +#endif diff --git a/projects/compiler-rt/lib/negdf2.c b/projects/compiler-rt/lib/negdf2.c new file mode 100644 index 00000000..4e17513a --- /dev/null +++ b/projects/compiler-rt/lib/negdf2.c @@ -0,0 +1,21 @@ +//===-- lib/negdf2.c - double-precision negation ------------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float negation. +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(dneg, negdf2) + +fp_t __negdf2(fp_t a) { + return fromRep(toRep(a) ^ signBit); +} diff --git a/projects/compiler-rt/lib/negdi2.c b/projects/compiler-rt/lib/negdi2.c new file mode 100644 index 00000000..b000dda3 --- /dev/null +++ b/projects/compiler-rt/lib/negdi2.c @@ -0,0 +1,26 @@ +/* ===-- negdi2.c - Implement __negdi2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __negdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: -a */ + +di_int +__negdi2(di_int a) +{ + /* Note: this routine is here for API compatibility; any sane compiler + * should expand it inline. + */ + return -a; +} diff --git a/projects/compiler-rt/lib/negsf2.c b/projects/compiler-rt/lib/negsf2.c new file mode 100644 index 00000000..29c17be4 --- /dev/null +++ b/projects/compiler-rt/lib/negsf2.c @@ -0,0 +1,22 @@ +//===-- lib/negsf2.c - single-precision negation ------------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float negation. +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +ARM_EABI_FNALIAS(fneg, negsf2) + +COMPILER_RT_ABI fp_t +__negsf2(fp_t a) { + return fromRep(toRep(a) ^ signBit); +} diff --git a/projects/compiler-rt/lib/negti2.c b/projects/compiler-rt/lib/negti2.c new file mode 100644 index 00000000..f7e4ad3b --- /dev/null +++ b/projects/compiler-rt/lib/negti2.c @@ -0,0 +1,30 @@ +/* ===-- negti2.c - Implement __negti2 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __negti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: -a */ + +ti_int +__negti2(ti_int a) +{ + /* Note: this routine is here for API compatibility; any sane compiler + * should expand it inline. + */ + return -a; +} + +#endif diff --git a/projects/compiler-rt/lib/negvdi2.c b/projects/compiler-rt/lib/negvdi2.c new file mode 100644 index 00000000..e336ecf2 --- /dev/null +++ b/projects/compiler-rt/lib/negvdi2.c @@ -0,0 +1,28 @@ +/* ===-- negvdi2.c - Implement __negvdi2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __negvdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: -a */ + +/* Effects: aborts if -a overflows */ + +COMPILER_RT_ABI di_int +__negvdi2(di_int a) +{ + const di_int MIN = (di_int)1 << ((int)(sizeof(di_int) * CHAR_BIT)-1); + if (a == MIN) + compilerrt_abort(); + return -a; +} diff --git a/projects/compiler-rt/lib/negvsi2.c b/projects/compiler-rt/lib/negvsi2.c new file mode 100644 index 00000000..b9e93fef --- /dev/null +++ b/projects/compiler-rt/lib/negvsi2.c @@ -0,0 +1,28 @@ +/* ===-- negvsi2.c - Implement __negvsi2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __negvsi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: -a */ + +/* Effects: aborts if -a overflows */ + +COMPILER_RT_ABI si_int +__negvsi2(si_int a) +{ + const si_int MIN = (si_int)1 << ((int)(sizeof(si_int) * CHAR_BIT)-1); + if (a == MIN) + compilerrt_abort(); + return -a; +} diff --git a/projects/compiler-rt/lib/negvti2.c b/projects/compiler-rt/lib/negvti2.c new file mode 100644 index 00000000..05df6152 --- /dev/null +++ b/projects/compiler-rt/lib/negvti2.c @@ -0,0 +1,32 @@ +/*===-- negvti2.c - Implement __negvti2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + *===----------------------------------------------------------------------=== + * + *This file implements __negvti2 for the compiler_rt library. + * + *===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: -a */ + +/* Effects: aborts if -a overflows */ + +ti_int +__negvti2(ti_int a) +{ + const ti_int MIN = (ti_int)1 << ((int)(sizeof(ti_int) * CHAR_BIT)-1); + if (a == MIN) + compilerrt_abort(); + return -a; +} + +#endif diff --git a/projects/compiler-rt/lib/paritydi2.c b/projects/compiler-rt/lib/paritydi2.c new file mode 100644 index 00000000..2ded54c9 --- /dev/null +++ b/projects/compiler-rt/lib/paritydi2.c @@ -0,0 +1,27 @@ +/* ===-- paritydi2.c - Implement __paritydi2 -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __paritydi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: 1 if number of bits is odd else returns 0 */ + +si_int COMPILER_RT_ABI __paritysi2(si_int a); + +COMPILER_RT_ABI si_int +__paritydi2(di_int a) +{ + dwords x; + x.all = a; + return __paritysi2(x.s.high ^ x.s.low); +} diff --git a/projects/compiler-rt/lib/paritysi2.c b/projects/compiler-rt/lib/paritysi2.c new file mode 100644 index 00000000..59998466 --- /dev/null +++ b/projects/compiler-rt/lib/paritysi2.c @@ -0,0 +1,27 @@ +/* ===-- paritysi2.c - Implement __paritysi2 -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __paritysi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: 1 if number of bits is odd else returns 0 */ + +COMPILER_RT_ABI si_int +__paritysi2(si_int a) +{ + su_int x = (su_int)a; + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + return (0x6996 >> (x & 0xF)) & 1; +} diff --git a/projects/compiler-rt/lib/parityti2.c b/projects/compiler-rt/lib/parityti2.c new file mode 100644 index 00000000..a1f47b1d --- /dev/null +++ b/projects/compiler-rt/lib/parityti2.c @@ -0,0 +1,31 @@ +/* ===-- parityti2.c - Implement __parityti2 -------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __parityti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: 1 if number of bits is odd else returns 0 */ + +si_int __paritydi2(di_int a); + +si_int +__parityti2(ti_int a) +{ + twords x; + x.all = a; + return __paritydi2(x.s.high ^ x.s.low); +} + +#endif diff --git a/projects/compiler-rt/lib/popcountdi2.c b/projects/compiler-rt/lib/popcountdi2.c new file mode 100644 index 00000000..5e8a62f0 --- /dev/null +++ b/projects/compiler-rt/lib/popcountdi2.c @@ -0,0 +1,36 @@ +/* ===-- popcountdi2.c - Implement __popcountdi2 ----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __popcountdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: count of 1 bits */ + +COMPILER_RT_ABI si_int +__popcountdi2(di_int a) +{ + du_int x2 = (du_int)a; + x2 = x2 - ((x2 >> 1) & 0x5555555555555555uLL); + /* Every 2 bits holds the sum of every pair of bits (32) */ + x2 = ((x2 >> 2) & 0x3333333333333333uLL) + (x2 & 0x3333333333333333uLL); + /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (16) */ + x2 = (x2 + (x2 >> 4)) & 0x0F0F0F0F0F0F0F0FuLL; + /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (8) */ + su_int x = (su_int)(x2 + (x2 >> 32)); + /* The lower 32 bits hold four 16 bit sums (5 significant bits). */ + /* Upper 32 bits are garbage */ + x = x + (x >> 16); + /* The lower 16 bits hold two 32 bit sums (6 significant bits). */ + /* Upper 16 bits are garbage */ + return (x + (x >> 8)) & 0x0000007F; /* (7 significant bits) */ +} diff --git a/projects/compiler-rt/lib/popcountsi2.c b/projects/compiler-rt/lib/popcountsi2.c new file mode 100644 index 00000000..44544ff4 --- /dev/null +++ b/projects/compiler-rt/lib/popcountsi2.c @@ -0,0 +1,33 @@ +/* ===-- popcountsi2.c - Implement __popcountsi2 ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __popcountsi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: count of 1 bits */ + +COMPILER_RT_ABI si_int +__popcountsi2(si_int a) +{ + su_int x = (su_int)a; + x = x - ((x >> 1) & 0x55555555); + /* Every 2 bits holds the sum of every pair of bits */ + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) */ + x = (x + (x >> 4)) & 0x0F0F0F0F; + /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) */ + x = (x + (x >> 16)); + /* The lower 16 bits hold two 8 bit sums (5 significant bits).*/ + /* Upper 16 bits are garbage */ + return (x + (x >> 8)) & 0x0000003F; /* (6 significant bits) */ +} diff --git a/projects/compiler-rt/lib/popcountti2.c b/projects/compiler-rt/lib/popcountti2.c new file mode 100644 index 00000000..95666738 --- /dev/null +++ b/projects/compiler-rt/lib/popcountti2.c @@ -0,0 +1,44 @@ +/* ===-- popcountti2.c - Implement __popcountti2 ----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __popcountti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: count of 1 bits */ + +si_int +__popcountti2(ti_int a) +{ + tu_int x3 = (tu_int)a; + x3 = x3 - ((x3 >> 1) & (((tu_int)0x5555555555555555uLL << 64) | + 0x5555555555555555uLL)); + /* Every 2 bits holds the sum of every pair of bits (64) */ + x3 = ((x3 >> 2) & (((tu_int)0x3333333333333333uLL << 64) | 0x3333333333333333uLL)) + + (x3 & (((tu_int)0x3333333333333333uLL << 64) | 0x3333333333333333uLL)); + /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (32) */ + x3 = (x3 + (x3 >> 4)) + & (((tu_int)0x0F0F0F0F0F0F0F0FuLL << 64) | 0x0F0F0F0F0F0F0F0FuLL); + /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (16) */ + du_int x2 = (du_int)(x3 + (x3 >> 64)); + /* Every 8 bits holds the sum of every 8-set of bits (5 significant bits) (8) */ + su_int x = (su_int)(x2 + (x2 >> 32)); + /* Every 8 bits holds the sum of every 8-set of bits (6 significant bits) (4) */ + x = x + (x >> 16); + /* Every 8 bits holds the sum of every 8-set of bits (7 significant bits) (2) */ + /* Upper 16 bits are garbage */ + return (x + (x >> 8)) & 0xFF; /* (8 significant bits) */ +} + +#endif diff --git a/projects/compiler-rt/lib/powidf2.c b/projects/compiler-rt/lib/powidf2.c new file mode 100644 index 00000000..ac13b172 --- /dev/null +++ b/projects/compiler-rt/lib/powidf2.c @@ -0,0 +1,34 @@ +/* ===-- powidf2.cpp - Implement __powidf2 ---------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __powidf2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a ^ b */ + +COMPILER_RT_ABI double +__powidf2(double a, si_int b) +{ + const int recip = b < 0; + double r = 1; + while (1) + { + if (b & 1) + r *= a; + b /= 2; + if (b == 0) + break; + a *= a; + } + return recip ? 1/r : r; +} diff --git a/projects/compiler-rt/lib/powisf2.c b/projects/compiler-rt/lib/powisf2.c new file mode 100644 index 00000000..0c400ec6 --- /dev/null +++ b/projects/compiler-rt/lib/powisf2.c @@ -0,0 +1,34 @@ +/*===-- powisf2.cpp - Implement __powisf2 ---------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __powisf2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a ^ b */ + +COMPILER_RT_ABI float +__powisf2(float a, si_int b) +{ + const int recip = b < 0; + float r = 1; + while (1) + { + if (b & 1) + r *= a; + b /= 2; + if (b == 0) + break; + a *= a; + } + return recip ? 1/r : r; +} diff --git a/projects/compiler-rt/lib/powitf2.c b/projects/compiler-rt/lib/powitf2.c new file mode 100644 index 00000000..d3b93492 --- /dev/null +++ b/projects/compiler-rt/lib/powitf2.c @@ -0,0 +1,38 @@ +/* ===-- powitf2.cpp - Implement __powitf2 ---------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __powitf2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if _ARCH_PPC + +/* Returns: a ^ b */ + +long double +__powitf2(long double a, si_int b) +{ + const int recip = b < 0; + long double r = 1; + while (1) + { + if (b & 1) + r *= a; + b /= 2; + if (b == 0) + break; + a *= a; + } + return recip ? 1/r : r; +} + +#endif diff --git a/projects/compiler-rt/lib/powixf2.c b/projects/compiler-rt/lib/powixf2.c new file mode 100644 index 00000000..f050964d --- /dev/null +++ b/projects/compiler-rt/lib/powixf2.c @@ -0,0 +1,38 @@ +/* ===-- powixf2.cpp - Implement __powixf2 ---------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __powixf2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#if !_ARCH_PPC + +#include "int_lib.h" + +/* Returns: a ^ b */ + +long double +__powixf2(long double a, si_int b) +{ + const int recip = b < 0; + long double r = 1; + while (1) + { + if (b & 1) + r *= a; + b /= 2; + if (b == 0) + break; + a *= a; + } + return recip ? 1/r : r; +} + +#endif diff --git a/projects/compiler-rt/lib/ppc/DD.h b/projects/compiler-rt/lib/ppc/DD.h new file mode 100644 index 00000000..13862dc9 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/DD.h @@ -0,0 +1,46 @@ +#ifndef __DD_HEADER +#define __DD_HEADER + +#include "../int_lib.h" + +typedef union { + long double ld; + struct { + double hi; + double lo; + }s; +}DD; + +typedef union { + double d; + uint64_t x; +} doublebits; + +#define LOWORDER(xy,xHi,xLo,yHi,yLo) \ + (((((xHi)*(yHi) - (xy)) + (xHi)*(yLo)) + (xLo)*(yHi)) + (xLo)*(yLo)) + +static inline double __attribute__((always_inline)) +fabs(double x) +{ + doublebits result = { .d = x }; + result.x &= UINT64_C(0x7fffffffffffffff); + return result.d; +} + +static inline double __attribute__((always_inline)) +high26bits(double x) +{ + doublebits result = { .d = x }; + result.x &= UINT64_C(0xfffffffff8000000); + return result.d; +} + +static inline int __attribute__((always_inline)) +different_sign(double x, double y) +{ + doublebits xsignbit = { .d = x }, ysignbit = { .d = y }; + int result = (int)(xsignbit.x >> 63) ^ (int)(ysignbit.x >> 63); + return result; +} + +#endif /* __DD_HEADER */ diff --git a/projects/compiler-rt/lib/ppc/Makefile.mk b/projects/compiler-rt/lib/ppc/Makefile.mk new file mode 100644 index 00000000..b78d3860 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/Makefile.mk @@ -0,0 +1,20 @@ +#===- lib/ppc/Makefile.mk ----------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := builtins +SubDirs := +OnlyArchs := ppc + +AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) +ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o) +Implementation := Optimized + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard lib/*.h $(Dir)/*.h) diff --git a/projects/compiler-rt/lib/ppc/divtc3.c b/projects/compiler-rt/lib/ppc/divtc3.c new file mode 100644 index 00000000..29912818 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/divtc3.c @@ -0,0 +1,96 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +#include "DD.h" +#include "../int_math.h" + +#if !defined(CRT_INFINITY) && defined(HUGE_VAL) +#define CRT_INFINITY HUGE_VAL +#endif /* CRT_INFINITY */ + +#define makeFinite(x) { \ + (x).s.hi = crt_copysign(crt_isinf((x).s.hi) ? 1.0 : 0.0, (x).s.hi); \ + (x).s.lo = 0.0; \ + } + +long double __gcc_qadd(long double, long double); +long double __gcc_qsub(long double, long double); +long double __gcc_qmul(long double, long double); +long double __gcc_qdiv(long double, long double); + +long double _Complex +__divtc3(long double a, long double b, long double c, long double d) +{ + DD cDD = { .ld = c }; + DD dDD = { .ld = d }; + + int ilogbw = 0; + const double logbw = crt_logb(crt_fmax(crt_fabs(cDD.s.hi), crt_fabs(dDD.s.hi) )); + + if (crt_isfinite(logbw)) + { + ilogbw = (int)logbw; + + cDD.s.hi = crt_scalbn(cDD.s.hi, -ilogbw); + cDD.s.lo = crt_scalbn(cDD.s.lo, -ilogbw); + dDD.s.hi = crt_scalbn(dDD.s.hi, -ilogbw); + dDD.s.lo = crt_scalbn(dDD.s.lo, -ilogbw); + } + + const long double denom = __gcc_qadd(__gcc_qmul(cDD.ld, cDD.ld), __gcc_qmul(dDD.ld, dDD.ld)); + const long double realNumerator = __gcc_qadd(__gcc_qmul(a,cDD.ld), __gcc_qmul(b,dDD.ld)); + const long double imagNumerator = __gcc_qsub(__gcc_qmul(b,cDD.ld), __gcc_qmul(a,dDD.ld)); + + DD real = { .ld = __gcc_qdiv(realNumerator, denom) }; + DD imag = { .ld = __gcc_qdiv(imagNumerator, denom) }; + + real.s.hi = crt_scalbn(real.s.hi, -ilogbw); + real.s.lo = crt_scalbn(real.s.lo, -ilogbw); + imag.s.hi = crt_scalbn(imag.s.hi, -ilogbw); + imag.s.lo = crt_scalbn(imag.s.lo, -ilogbw); + + if (crt_isnan(real.s.hi) && crt_isnan(imag.s.hi)) + { + DD aDD = { .ld = a }; + DD bDD = { .ld = b }; + DD rDD = { .ld = denom }; + + if ((rDD.s.hi == 0.0) && (!crt_isnan(aDD.s.hi) || + !crt_isnan(bDD.s.hi))) + { + real.s.hi = crt_copysign(CRT_INFINITY,cDD.s.hi) * aDD.s.hi; + real.s.lo = 0.0; + imag.s.hi = crt_copysign(CRT_INFINITY,cDD.s.hi) * bDD.s.hi; + imag.s.lo = 0.0; + } + + else if ((crt_isinf(aDD.s.hi) || crt_isinf(bDD.s.hi)) && + crt_isfinite(cDD.s.hi) && crt_isfinite(dDD.s.hi)) + { + makeFinite(aDD); + makeFinite(bDD); + real.s.hi = CRT_INFINITY * (aDD.s.hi*cDD.s.hi + bDD.s.hi*dDD.s.hi); + real.s.lo = 0.0; + imag.s.hi = CRT_INFINITY * (bDD.s.hi*cDD.s.hi - aDD.s.hi*dDD.s.hi); + imag.s.lo = 0.0; + } + + else if ((crt_isinf(cDD.s.hi) || crt_isinf(dDD.s.hi)) && + crt_isfinite(aDD.s.hi) && crt_isfinite(bDD.s.hi)) + { + makeFinite(cDD); + makeFinite(dDD); + real.s.hi = crt_copysign(0.0,(aDD.s.hi*cDD.s.hi + bDD.s.hi*dDD.s.hi)); + real.s.lo = 0.0; + imag.s.hi = crt_copysign(0.0,(bDD.s.hi*cDD.s.hi - aDD.s.hi*dDD.s.hi)); + imag.s.lo = 0.0; + } + } + + long double _Complex z; + __real__ z = real.ld; + __imag__ z = imag.ld; + + return z; +} diff --git a/projects/compiler-rt/lib/ppc/fixtfdi.c b/projects/compiler-rt/lib/ppc/fixtfdi.c new file mode 100644 index 00000000..56e7b3fb --- /dev/null +++ b/projects/compiler-rt/lib/ppc/fixtfdi.c @@ -0,0 +1,104 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* int64_t __fixunstfdi(long double x); + * This file implements the PowerPC 128-bit double-double -> int64_t conversion + */ + +#include "DD.h" +#include "../int_math.h" + +uint64_t __fixtfdi(long double input) +{ + const DD x = { .ld = input }; + const doublebits hibits = { .d = x.s.hi }; + + const uint32_t absHighWord = (uint32_t)(hibits.x >> 32) & UINT32_C(0x7fffffff); + const uint32_t absHighWordMinusOne = absHighWord - UINT32_C(0x3ff00000); + + /* If (1.0 - tiny) <= input < 0x1.0p63: */ + if (UINT32_C(0x03f00000) > absHighWordMinusOne) + { + /* Do an unsigned conversion of the absolute value, then restore the sign. */ + const int unbiasedHeadExponent = absHighWordMinusOne >> 20; + + int64_t result = hibits.x & INT64_C(0x000fffffffffffff); /* mantissa(hi) */ + result |= INT64_C(0x0010000000000000); /* matissa(hi) with implicit bit */ + result <<= 10; /* mantissa(hi) with one zero preceeding bit. */ + + const int64_t hiNegationMask = ((int64_t)(hibits.x)) >> 63; + + /* If the tail is non-zero, we need to patch in the tail bits. */ + if (0.0 != x.s.lo) + { + const doublebits lobits = { .d = x.s.lo }; + int64_t tailMantissa = lobits.x & INT64_C(0x000fffffffffffff); + tailMantissa |= INT64_C(0x0010000000000000); + + /* At this point we have the mantissa of |tail| */ + /* We need to negate it if head and tail have different signs. */ + const int64_t loNegationMask = ((int64_t)(lobits.x)) >> 63; + const int64_t negationMask = loNegationMask ^ hiNegationMask; + tailMantissa = (tailMantissa ^ negationMask) - negationMask; + + /* Now we have the mantissa of tail as a signed 2s-complement integer */ + + const int biasedTailExponent = (int)(lobits.x >> 52) & 0x7ff; + + /* Shift the tail mantissa into the right position, accounting for the + * bias of 10 that we shifted the head mantissa by. + */ + tailMantissa >>= (unbiasedHeadExponent - (biasedTailExponent - (1023 - 10))); + + result += tailMantissa; + } + + result >>= (62 - unbiasedHeadExponent); + + /* Restore the sign of the result and return */ + result = (result ^ hiNegationMask) - hiNegationMask; + return result; + + } + + /* Edge cases handled here: */ + + /* |x| < 1, result is zero. */ + if (1.0 > crt_fabs(x.s.hi)) + return INT64_C(0); + + /* x very close to INT64_MIN, care must be taken to see which side we are on. */ + if (x.s.hi == -0x1.0p63) { + + int64_t result = INT64_MIN; + + if (0.0 < x.s.lo) + { + /* If the tail is positive, the correct result is something other than INT64_MIN. + * we'll need to figure out what it is. + */ + + const doublebits lobits = { .d = x.s.lo }; + int64_t tailMantissa = lobits.x & INT64_C(0x000fffffffffffff); + tailMantissa |= INT64_C(0x0010000000000000); + + /* Now we negate the tailMantissa */ + tailMantissa = (tailMantissa ^ INT64_C(-1)) + INT64_C(1); + + /* And shift it by the appropriate amount */ + const int biasedTailExponent = (int)(lobits.x >> 52) & 0x7ff; + tailMantissa >>= 1075 - biasedTailExponent; + + result -= tailMantissa; + } + + return result; + } + + /* Signed overflows, infinities, and NaNs */ + if (x.s.hi > 0.0) + return INT64_MAX; + else + return INT64_MIN; +} diff --git a/projects/compiler-rt/lib/ppc/fixunstfdi.c b/projects/compiler-rt/lib/ppc/fixunstfdi.c new file mode 100644 index 00000000..5e6e2ced --- /dev/null +++ b/projects/compiler-rt/lib/ppc/fixunstfdi.c @@ -0,0 +1,59 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* uint64_t __fixunstfdi(long double x); */ +/* This file implements the PowerPC 128-bit double-double -> uint64_t conversion */ + +#include "DD.h" + +uint64_t __fixunstfdi(long double input) +{ + const DD x = { .ld = input }; + const doublebits hibits = { .d = x.s.hi }; + + const uint32_t highWordMinusOne = (uint32_t)(hibits.x >> 32) - UINT32_C(0x3ff00000); + + /* If (1.0 - tiny) <= input < 0x1.0p64: */ + if (UINT32_C(0x04000000) > highWordMinusOne) + { + const int unbiasedHeadExponent = highWordMinusOne >> 20; + + uint64_t result = hibits.x & UINT64_C(0x000fffffffffffff); /* mantissa(hi) */ + result |= UINT64_C(0x0010000000000000); /* matissa(hi) with implicit bit */ + result <<= 11; /* mantissa(hi) left aligned in the int64 field. */ + + /* If the tail is non-zero, we need to patch in the tail bits. */ + if (0.0 != x.s.lo) + { + const doublebits lobits = { .d = x.s.lo }; + int64_t tailMantissa = lobits.x & INT64_C(0x000fffffffffffff); + tailMantissa |= INT64_C(0x0010000000000000); + + /* At this point we have the mantissa of |tail| */ + + const int64_t negationMask = ((int64_t)(lobits.x)) >> 63; + tailMantissa = (tailMantissa ^ negationMask) - negationMask; + + /* Now we have the mantissa of tail as a signed 2s-complement integer */ + + const int biasedTailExponent = (int)(lobits.x >> 52) & 0x7ff; + + /* Shift the tail mantissa into the right position, accounting for the + * bias of 11 that we shifted the head mantissa by. + */ + tailMantissa >>= (unbiasedHeadExponent - (biasedTailExponent - (1023 - 11))); + + result += tailMantissa; + } + + result >>= (63 - unbiasedHeadExponent); + return result; + } + + /* Edge cases are handled here, with saturation. */ + if (1.0 > x.s.hi) + return UINT64_C(0); + else + return UINT64_MAX; +} diff --git a/projects/compiler-rt/lib/ppc/floatditf.c b/projects/compiler-rt/lib/ppc/floatditf.c new file mode 100644 index 00000000..beabdd01 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/floatditf.c @@ -0,0 +1,36 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* long double __floatditf(long long x); */ +/* This file implements the PowerPC long long -> long double conversion */ + +#include "DD.h" + +long double __floatditf(int64_t a) { + + static const double twop32 = 0x1.0p32; + static const double twop52 = 0x1.0p52; + + doublebits low = { .d = twop52 }; + low.x |= a & UINT64_C(0x00000000ffffffff); /* 0x1.0p52 + low 32 bits of a. */ + + const double high_addend = (double)((int32_t)(a >> 32))*twop32 - twop52; + + /* At this point, we have two double precision numbers + * high_addend and low.d, and we wish to return their sum + * as a canonicalized long double: + */ + + /* This implementation sets the inexact flag spuriously. + * This could be avoided, but at some substantial cost. + */ + + DD result; + + result.s.hi = high_addend + low.d; + result.s.lo = (high_addend - result.s.hi) + low.d; + + return result.ld; + +} diff --git a/projects/compiler-rt/lib/ppc/floatunditf.c b/projects/compiler-rt/lib/ppc/floatunditf.c new file mode 100644 index 00000000..b12e1e73 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/floatunditf.c @@ -0,0 +1,41 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* long double __floatunditf(unsigned long long x); */ +/* This file implements the PowerPC unsigned long long -> long double conversion */ + +#include "DD.h" + +long double __floatunditf(uint64_t a) { + + /* Begins with an exact copy of the code from __floatundidf */ + + static const double twop52 = 0x1.0p52; + static const double twop84 = 0x1.0p84; + static const double twop84_plus_twop52 = 0x1.00000001p84; + + doublebits high = { .d = twop84 }; + doublebits low = { .d = twop52 }; + + high.x |= a >> 32; /* 0x1.0p84 + high 32 bits of a */ + low.x |= a & UINT64_C(0x00000000ffffffff); /* 0x1.0p52 + low 32 bits of a */ + + const double high_addend = high.d - twop84_plus_twop52; + + /* At this point, we have two double precision numbers + * high_addend and low.d, and we wish to return their sum + * as a canonicalized long double: + */ + + /* This implementation sets the inexact flag spuriously. */ + /* This could be avoided, but at some substantial cost. */ + + DD result; + + result.s.hi = high_addend + low.d; + result.s.lo = (high_addend - result.s.hi) + low.d; + + return result.ld; + +} diff --git a/projects/compiler-rt/lib/ppc/gcc_qadd.c b/projects/compiler-rt/lib/ppc/gcc_qadd.c new file mode 100644 index 00000000..c388c7e9 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/gcc_qadd.c @@ -0,0 +1,76 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* long double __gcc_qadd(long double x, long double y); + * This file implements the PowerPC 128-bit double-double add operation. + * This implementation is shamelessly cribbed from Apple's DDRT, circa 1993(!) + */ + +#include "DD.h" + +long double __gcc_qadd(long double x, long double y) +{ + static const uint32_t infinityHi = UINT32_C(0x7ff00000); + + DD dst = { .ld = x }, src = { .ld = y }; + + register double A = dst.s.hi, a = dst.s.lo, + B = src.s.hi, b = src.s.lo; + + /* If both operands are zero: */ + if ((A == 0.0) && (B == 0.0)) { + dst.s.hi = A + B; + dst.s.lo = 0.0; + return dst.ld; + } + + /* If either operand is NaN or infinity: */ + const doublebits abits = { .d = A }; + const doublebits bbits = { .d = B }; + if ((((uint32_t)(abits.x >> 32) & infinityHi) == infinityHi) || + (((uint32_t)(bbits.x >> 32) & infinityHi) == infinityHi)) { + dst.s.hi = A + B; + dst.s.lo = 0.0; + return dst.ld; + } + + /* If the computation overflows: */ + /* This may be playing things a little bit fast and loose, but it will do for a start. */ + const double testForOverflow = A + (B + (a + b)); + const doublebits testbits = { .d = testForOverflow }; + if (((uint32_t)(testbits.x >> 32) & infinityHi) == infinityHi) { + dst.s.hi = testForOverflow; + dst.s.lo = 0.0; + return dst.ld; + } + + double H, h; + double T, t; + double W, w; + double Y; + + H = B + (A - (A + B)); + T = b + (a - (a + b)); + h = A + (B - (A + B)); + t = a + (b - (a + b)); + + if (fabs(A) <= fabs(B)) + w = (a + b) + h; + else + w = (a + b) + H; + + W = (A + B) + w; + Y = (A + B) - W; + Y += w; + + if (fabs(a) <= fabs(b)) + w = t + Y; + else + w = T + Y; + + dst.s.hi = Y = W + w; + dst.s.lo = (W - Y) + w; + + return dst.ld; +} diff --git a/projects/compiler-rt/lib/ppc/gcc_qdiv.c b/projects/compiler-rt/lib/ppc/gcc_qdiv.c new file mode 100644 index 00000000..70aa00b6 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/gcc_qdiv.c @@ -0,0 +1,55 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* long double __gcc_qdiv(long double x, long double y); + * This file implements the PowerPC 128-bit double-double division operation. + * This implementation is shamelessly cribbed from Apple's DDRT, circa 1993(!) + */ + +#include "DD.h" + +long double __gcc_qdiv(long double a, long double b) +{ + static const uint32_t infinityHi = UINT32_C(0x7ff00000); + DD dst = { .ld = a }, src = { .ld = b }; + + register double x = dst.s.hi, x1 = dst.s.lo, + y = src.s.hi, y1 = src.s.lo; + + double yHi, yLo, qHi, qLo; + double yq, tmp, q; + + q = x / y; + + /* Detect special cases */ + if (q == 0.0) { + dst.s.hi = q; + dst.s.lo = 0.0; + return dst.ld; + } + + const doublebits qBits = { .d = q }; + if (((uint32_t)(qBits.x >> 32) & infinityHi) == infinityHi) { + dst.s.hi = q; + dst.s.lo = 0.0; + return dst.ld; + } + + yHi = high26bits(y); + qHi = high26bits(q); + + yq = y * q; + yLo = y - yHi; + qLo = q - qHi; + + tmp = LOWORDER(yq, yHi, yLo, qHi, qLo); + tmp = (x - yq) - tmp; + tmp = ((tmp + x1) - y1 * q) / y; + x = q + tmp; + + dst.s.lo = (q - x) + tmp; + dst.s.hi = x; + + return dst.ld; +} diff --git a/projects/compiler-rt/lib/ppc/gcc_qmul.c b/projects/compiler-rt/lib/ppc/gcc_qmul.c new file mode 100644 index 00000000..fb4c5164 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/gcc_qmul.c @@ -0,0 +1,53 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* long double __gcc_qmul(long double x, long double y); + * This file implements the PowerPC 128-bit double-double multiply operation. + * This implementation is shamelessly cribbed from Apple's DDRT, circa 1993(!) + */ + +#include "DD.h" + +long double __gcc_qmul(long double x, long double y) +{ + static const uint32_t infinityHi = UINT32_C(0x7ff00000); + DD dst = { .ld = x }, src = { .ld = y }; + + register double A = dst.s.hi, a = dst.s.lo, + B = src.s.hi, b = src.s.lo; + + double aHi, aLo, bHi, bLo; + double ab, tmp, tau; + + ab = A * B; + + /* Detect special cases */ + if (ab == 0.0) { + dst.s.hi = ab; + dst.s.lo = 0.0; + return dst.ld; + } + + const doublebits abBits = { .d = ab }; + if (((uint32_t)(abBits.x >> 32) & infinityHi) == infinityHi) { + dst.s.hi = ab; + dst.s.lo = 0.0; + return dst.ld; + } + + /* Generic cases handled here. */ + aHi = high26bits(A); + bHi = high26bits(B); + aLo = A - aHi; + bLo = B - bHi; + + tmp = LOWORDER(ab, aHi, aLo, bHi, bLo); + tmp += (A * b + a * B); + tau = ab + tmp; + + dst.s.lo = (ab - tau) + tmp; + dst.s.hi = tau; + + return dst.ld; +} diff --git a/projects/compiler-rt/lib/ppc/gcc_qsub.c b/projects/compiler-rt/lib/ppc/gcc_qsub.c new file mode 100644 index 00000000..4f1f7ac7 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/gcc_qsub.c @@ -0,0 +1,76 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* long double __gcc_qsub(long double x, long double y); + * This file implements the PowerPC 128-bit double-double add operation. + * This implementation is shamelessly cribbed from Apple's DDRT, circa 1993(!) + */ + +#include "DD.h" + +long double __gcc_qsub(long double x, long double y) +{ + static const uint32_t infinityHi = UINT32_C(0x7ff00000); + + DD dst = { .ld = x }, src = { .ld = y }; + + register double A = dst.s.hi, a = dst.s.lo, + B = -src.s.hi, b = -src.s.lo; + + /* If both operands are zero: */ + if ((A == 0.0) && (B == 0.0)) { + dst.s.hi = A + B; + dst.s.lo = 0.0; + return dst.ld; + } + + /* If either operand is NaN or infinity: */ + const doublebits abits = { .d = A }; + const doublebits bbits = { .d = B }; + if ((((uint32_t)(abits.x >> 32) & infinityHi) == infinityHi) || + (((uint32_t)(bbits.x >> 32) & infinityHi) == infinityHi)) { + dst.s.hi = A + B; + dst.s.lo = 0.0; + return dst.ld; + } + + /* If the computation overflows: */ + /* This may be playing things a little bit fast and loose, but it will do for a start. */ + const double testForOverflow = A + (B + (a + b)); + const doublebits testbits = { .d = testForOverflow }; + if (((uint32_t)(testbits.x >> 32) & infinityHi) == infinityHi) { + dst.s.hi = testForOverflow; + dst.s.lo = 0.0; + return dst.ld; + } + + double H, h; + double T, t; + double W, w; + double Y; + + H = B + (A - (A + B)); + T = b + (a - (a + b)); + h = A + (B - (A + B)); + t = a + (b - (a + b)); + + if (fabs(A) <= fabs(B)) + w = (a + b) + h; + else + w = (a + b) + H; + + W = (A + B) + w; + Y = (A + B) - W; + Y += w; + + if (fabs(a) <= fabs(b)) + w = t + Y; + else + w = T + Y; + + dst.s.hi = Y = W + w; + dst.s.lo = (W - Y) + w; + + return dst.ld; +} diff --git a/projects/compiler-rt/lib/ppc/multc3.c b/projects/compiler-rt/lib/ppc/multc3.c new file mode 100644 index 00000000..738b65a8 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/multc3.c @@ -0,0 +1,94 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +#include "DD.h" +#include "../int_math.h" + +#define makeFinite(x) { \ + (x).s.hi = crt_copysign(crt_isinf((x).s.hi) ? 1.0 : 0.0, (x).s.hi); \ + (x).s.lo = 0.0; \ + } + +#define zeroNaN(x) { \ + if (crt_isnan((x).s.hi)) { \ + (x).s.hi = crt_copysign(0.0, (x).s.hi); \ + (x).s.lo = 0.0; \ + } \ + } + +long double __gcc_qadd(long double, long double); +long double __gcc_qsub(long double, long double); +long double __gcc_qmul(long double, long double); + +long double _Complex +__multc3(long double a, long double b, long double c, long double d) +{ + long double ac = __gcc_qmul(a,c); + long double bd = __gcc_qmul(b,d); + long double ad = __gcc_qmul(a,d); + long double bc = __gcc_qmul(b,c); + + DD real = { .ld = __gcc_qsub(ac,bd) }; + DD imag = { .ld = __gcc_qadd(ad,bc) }; + + if (crt_isnan(real.s.hi) && crt_isnan(imag.s.hi)) + { + int recalc = 0; + + DD aDD = { .ld = a }; + DD bDD = { .ld = b }; + DD cDD = { .ld = c }; + DD dDD = { .ld = d }; + + if (crt_isinf(aDD.s.hi) || crt_isinf(bDD.s.hi)) + { + makeFinite(aDD); + makeFinite(bDD); + zeroNaN(cDD); + zeroNaN(dDD); + recalc = 1; + } + + if (crt_isinf(cDD.s.hi) || crt_isinf(dDD.s.hi)) + { + makeFinite(cDD); + makeFinite(dDD); + zeroNaN(aDD); + zeroNaN(bDD); + recalc = 1; + } + + if (!recalc) + { + DD acDD = { .ld = ac }; + DD bdDD = { .ld = bd }; + DD adDD = { .ld = ad }; + DD bcDD = { .ld = bc }; + + if (crt_isinf(acDD.s.hi) || crt_isinf(bdDD.s.hi) || + crt_isinf(adDD.s.hi) || crt_isinf(bcDD.s.hi)) + { + zeroNaN(aDD); + zeroNaN(bDD); + zeroNaN(cDD); + zeroNaN(dDD); + recalc = 1; + } + } + + if (recalc) + { + real.s.hi = CRT_INFINITY * (aDD.s.hi*cDD.s.hi - bDD.s.hi*dDD.s.hi); + real.s.lo = 0.0; + imag.s.hi = CRT_INFINITY * (aDD.s.hi*dDD.s.hi + bDD.s.hi*cDD.s.hi); + imag.s.lo = 0.0; + } + } + + long double _Complex z; + __real__ z = real.ld; + __imag__ z = imag.ld; + + return z; +} diff --git a/projects/compiler-rt/lib/ppc/restFP.S b/projects/compiler-rt/lib/ppc/restFP.S new file mode 100644 index 00000000..95032897 --- /dev/null +++ b/projects/compiler-rt/lib/ppc/restFP.S @@ -0,0 +1,43 @@ +//===-- restFP.S - Implement restFP ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// Helper function used by compiler to restore ppc floating point registers at +// the end of the function epilog. This function returns to the address +// in the LR slot. So a function epilog must branch (b) not branch and link +// (bl) to this function. +// If the compiler wants to restore f27..f31, it does a "b restFP+52" +// +// This function should never be exported by a shared library. Each linkage +// unit carries its own copy of this function. +// +DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(restFP) + lfd f14,-144(r1) + lfd f15,-136(r1) + lfd f16,-128(r1) + lfd f17,-120(r1) + lfd f18,-112(r1) + lfd f19,-104(r1) + lfd f20,-96(r1) + lfd f21,-88(r1) + lfd f22,-80(r1) + lfd f23,-72(r1) + lfd f24,-64(r1) + lfd f25,-56(r1) + lfd f26,-48(r1) + lfd f27,-40(r1) + lfd f28,-32(r1) + lfd f29,-24(r1) + lfd f30,-16(r1) + lfd f31,-8(r1) + lwz r0,8(r1) + mtlr r0 + blr diff --git a/projects/compiler-rt/lib/ppc/saveFP.S b/projects/compiler-rt/lib/ppc/saveFP.S new file mode 100644 index 00000000..72bd459f --- /dev/null +++ b/projects/compiler-rt/lib/ppc/saveFP.S @@ -0,0 +1,40 @@ +//===-- saveFP.S - Implement saveFP ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// +// Helper function used by compiler to save ppc floating point registers in +// function prologs. This routines also saves r0 in the LR slot. +// If the compiler wants to save f27..f31, it does a "bl saveFP+52" +// +// This function should never be exported by a shared library. Each linkage +// unit carries its own copy of this function. +// +DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(saveFP) + stfd f14,-144(r1) + stfd f15,-136(r1) + stfd f16,-128(r1) + stfd f17,-120(r1) + stfd f18,-112(r1) + stfd f19,-104(r1) + stfd f20,-96(r1) + stfd f21,-88(r1) + stfd f22,-80(r1) + stfd f23,-72(r1) + stfd f24,-64(r1) + stfd f25,-56(r1) + stfd f26,-48(r1) + stfd f27,-40(r1) + stfd f28,-32(r1) + stfd f29,-24(r1) + stfd f30,-16(r1) + stfd f31,-8(r1) + stw r0,8(r1) + blr diff --git a/projects/compiler-rt/lib/profile/CMakeLists.txt b/projects/compiler-rt/lib/profile/CMakeLists.txt new file mode 100644 index 00000000..bb4fd9e2 --- /dev/null +++ b/projects/compiler-rt/lib/profile/CMakeLists.txt @@ -0,0 +1,16 @@ +set(PROFILE_SOURCES + GCDAProfiling.c) + +filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386) + +if(APPLE) + add_compiler_rt_osx_static_runtime(clang_rt.profile_osx + ARCH ${PROFILE_SUPPORTED_ARCH} + SOURCES ${PROFILE_SOURCES}) +else() + foreach(arch ${PROFILE_SUPPORTED_ARCH}) + add_compiler_rt_static_runtime(clang_rt.profile-${arch} + ${arch} + SOURCES ${PROFILE_SOURCES}) + endforeach() +endif() diff --git a/projects/compiler-rt/lib/profile/GCDAProfiling.c b/projects/compiler-rt/lib/profile/GCDAProfiling.c new file mode 100644 index 00000000..7edf6829 --- /dev/null +++ b/projects/compiler-rt/lib/profile/GCDAProfiling.c @@ -0,0 +1,556 @@ +/*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +|*===----------------------------------------------------------------------===*| +|* +|* This file implements the call back routines for the gcov profiling +|* instrumentation pass. Link against this library when running code through +|* the -insert-gcov-profiling LLVM pass. +|* +|* We emit files in a corrupt version of GCOV's "gcda" file format. These files +|* are only close enough that LCOV will happily parse them. Anything that lcov +|* ignores is missing. +|* +|* TODO: gcov is multi-process safe by having each exit open the existing file +|* and append to it. We'd like to achieve that and be thread-safe too. +|* +\*===----------------------------------------------------------------------===*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +#ifndef _MSC_VER +#include +#else +typedef unsigned int uint32_t; +typedef unsigned int uint64_t; +#endif + +/* #define DEBUG_GCDAPROFILING */ + +/* + * --- GCOV file format I/O primitives --- + */ + +/* + * The current file name we're outputting. Used primarily for error logging. + */ +static char *filename = NULL; + +/* + * The current file we're outputting. + */ +static FILE *output_file = NULL; + +/* + * Buffer that we write things into. + */ +#define WRITE_BUFFER_SIZE (128 * 1024) +static char *write_buffer = NULL; +static uint64_t cur_buffer_size = 0; +static uint64_t cur_pos = 0; +static uint64_t file_size = 0; +static int new_file = 0; +static int fd = -1; + +/* + * A list of functions to write out the data. + */ +typedef void (*writeout_fn)(); + +struct writeout_fn_node { + writeout_fn fn; + struct writeout_fn_node *next; +}; + +static struct writeout_fn_node *writeout_fn_head = NULL; +static struct writeout_fn_node *writeout_fn_tail = NULL; + +/* + * A list of flush functions that our __gcov_flush() function should call. + */ +typedef void (*flush_fn)(); + +struct flush_fn_node { + flush_fn fn; + struct flush_fn_node *next; +}; + +static struct flush_fn_node *flush_fn_head = NULL; +static struct flush_fn_node *flush_fn_tail = NULL; + +static void resize_write_buffer(uint64_t size) { + if (!new_file) return; + size += cur_pos; + if (size <= cur_buffer_size) return; + size = (size - 1) / WRITE_BUFFER_SIZE + 1; + size *= WRITE_BUFFER_SIZE; + write_buffer = realloc(write_buffer, size); + cur_buffer_size = size; +} + +static void write_bytes(const char *s, size_t len) { + resize_write_buffer(len); + memcpy(&write_buffer[cur_pos], s, len); + cur_pos += len; +} + +static void write_32bit_value(uint32_t i) { + write_bytes((char*)&i, 4); +} + +static void write_64bit_value(uint64_t i) { + write_bytes((char*)&i, 8); +} + +static uint32_t length_of_string(const char *s) { + return (strlen(s) / 4) + 1; +} + +static void write_string(const char *s) { + uint32_t len = length_of_string(s); + write_32bit_value(len); + write_bytes(s, strlen(s)); + write_bytes("\0\0\0\0", 4 - (strlen(s) % 4)); +} + +static uint32_t read_32bit_value() { + uint32_t val; + + if (new_file) + return (uint32_t)-1; + + val = *(uint32_t*)&write_buffer[cur_pos]; + cur_pos += 4; + return val; +} + +static uint64_t read_64bit_value() { + uint64_t val; + + if (new_file) + return (uint64_t)-1; + + val = *(uint64_t*)&write_buffer[cur_pos]; + cur_pos += 8; + return val; +} + +static char *mangle_filename(const char *orig_filename) { + char *filename = 0; + int prefix_len = 0; + int prefix_strip = 0; + int level = 0; + const char *fname = orig_filename, *ptr = NULL; + const char *prefix = getenv("GCOV_PREFIX"); + const char *prefix_strip_str = getenv("GCOV_PREFIX_STRIP"); + + if (!prefix) + return strdup(orig_filename); + + if (prefix_strip_str) { + prefix_strip = atoi(prefix_strip_str); + + /* Negative GCOV_PREFIX_STRIP values are ignored */ + if (prefix_strip < 0) + prefix_strip = 0; + } + + prefix_len = strlen(prefix); + filename = malloc(prefix_len + 1 + strlen(orig_filename) + 1); + strcpy(filename, prefix); + + if (prefix[prefix_len - 1] != '/') + strcat(filename, "/"); + + for (ptr = fname + 1; *ptr != '\0' && level < prefix_strip; ++ptr) { + if (*ptr != '/') continue; + fname = ptr; + ++level; + } + + strcat(filename, fname); + + return filename; +} + +static void recursive_mkdir(char *filename) { + int i; + + for (i = 1; filename[i] != '\0'; ++i) { + if (filename[i] != '/') continue; + filename[i] = '\0'; +#ifdef _WIN32 + _mkdir(filename); +#else + mkdir(filename, 0755); /* Some of these will fail, ignore it. */ +#endif + filename[i] = '/'; + } +} + +static int map_file() { + fseek(output_file, 0L, SEEK_END); + file_size = ftell(output_file); + + /* A size of 0 is invalid to `mmap'. Return a fail here, but don't issue an + * error message because it should "just work" for the user. */ + if (file_size == 0) + return -1; + + write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0); + if (write_buffer == (void *)-1) { + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot map: %s\n", filename, + strerror(errnum)); + return -1; + } + return 0; +} + +static void unmap_file() { + if (msync(write_buffer, file_size, MS_SYNC) == -1) { + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename, + strerror(errnum)); + } + + /* We explicitly ignore errors from unmapping because at this point the data + * is written and we don't care. + */ + (void)munmap(write_buffer, file_size); + write_buffer = NULL; + file_size = 0; +} + +/* + * --- LLVM line counter API --- + */ + +/* A file in this case is a translation unit. Each .o file built with line + * profiling enabled will emit to a different file. Only one file may be + * started at a time. + */ +void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { + const char *mode = "r+b"; + filename = mangle_filename(orig_filename); + + /* Try just opening the file. */ + new_file = 0; + fd = open(filename, O_RDWR); + + if (fd == -1) { + /* Try opening the file, creating it if necessary. */ + new_file = 1; + mode = "w+b"; + fd = open(filename, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + /* Try creating the directories first then opening the file. */ + recursive_mkdir(filename); + fd = open(filename, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + /* Bah! It's hopeless. */ + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, + strerror(errnum)); + return; + } + } + } + + output_file = fdopen(fd, mode); + + /* Initialize the write buffer. */ + write_buffer = NULL; + cur_buffer_size = 0; + cur_pos = 0; + + if (new_file) { + resize_write_buffer(WRITE_BUFFER_SIZE); + memset(write_buffer, 0, WRITE_BUFFER_SIZE); + } else { + if (map_file() == -1) { + /* mmap failed, try to recover by clobbering */ + new_file = 1; + write_buffer = NULL; + cur_buffer_size = 0; + resize_write_buffer(WRITE_BUFFER_SIZE); + memset(write_buffer, 0, WRITE_BUFFER_SIZE); + } + } + + /* gcda file, version, stamp LLVM. */ + write_bytes("adcg", 4); + write_bytes(version, 4); + write_bytes("MVLL", 4); + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); +#endif +} + +/* Given an array of pointers to counters (counters), increment the n-th one, + * where we're also given a pointer to n (predecessor). + */ +void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, + uint64_t **counters) { + uint64_t *counter; + uint32_t pred; + + pred = *predecessor; + if (pred == 0xffffffff) + return; + counter = counters[pred]; + + /* Don't crash if the pred# is out of sync. This can happen due to threads, + or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ + if (counter) + ++*counter; +#ifdef DEBUG_GCDAPROFILING + else + fprintf(stderr, + "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n", + *counter, *predecessor); +#endif +} + +void llvm_gcda_emit_function(uint32_t ident, const char *function_name, + uint8_t use_extra_checksum) { + uint32_t len = 2; + + if (use_extra_checksum) + len++; +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident, + function_name ? function_name : "NULL"); +#endif + if (!output_file) return; + + /* function tag */ + write_bytes("\0\0\0\1", 4); + if (function_name) + len += 1 + length_of_string(function_name); + write_32bit_value(len); + write_32bit_value(ident); + write_32bit_value(0); + if (use_extra_checksum) + write_32bit_value(0); + if (function_name) + write_string(function_name); +} + +void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { + uint32_t i; + uint64_t *old_ctrs = NULL; + uint32_t val = 0; + uint64_t save_cur_pos = cur_pos; + + if (!output_file) return; + + val = read_32bit_value(); + + if (val != (uint32_t)-1) { + /* There are counters present in the file. Merge them. */ + if (val != 0x01a10000) { + fprintf(stderr, "profiling:invalid arc tag (0x%08x)\n", val); + return; + } + + val = read_32bit_value(); + if (val == (uint32_t)-1 || val / 2 != num_counters) { + fprintf(stderr, "profiling:invalid number of counters (%d)\n", val); + return; + } + + old_ctrs = malloc(sizeof(uint64_t) * num_counters); + for (i = 0; i < num_counters; ++i) + old_ctrs[i] = read_64bit_value(); + } + + cur_pos = save_cur_pos; + + /* Counter #1 (arcs) tag */ + write_bytes("\0\0\xa1\1", 4); + write_32bit_value(num_counters * 2); + for (i = 0; i < num_counters; ++i) { + counters[i] += (old_ctrs ? old_ctrs[i] : 0); + write_64bit_value(counters[i]); + } + + free(old_ctrs); + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); + for (i = 0; i < num_counters; ++i) + fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); +#endif +} + +void llvm_gcda_summary_info() { + const int obj_summary_len = 9; // length for gcov compatibility + uint32_t i; + uint32_t runs = 1; + uint32_t val = 0; + uint64_t save_cur_pos = cur_pos; + + if (!output_file) return; + + val = read_32bit_value(); + + if (val != (uint32_t)-1) { + /* There are counters present in the file. Merge them. */ + if (val != 0xa1000000) { + fprintf(stderr, "profiling:invalid object tag (0x%08x)\n", val); + return; + } + + val = read_32bit_value(); // length + if (val != obj_summary_len) { + fprintf(stderr, "profiling:invalid object length (%d)\n", val); // length + return; + } + + read_32bit_value(); // checksum, unused + read_32bit_value(); // num, unused + runs += read_32bit_value(); // add previous run count to new counter + } + + cur_pos = save_cur_pos; + + /* Object summary tag */ + write_bytes("\0\0\0\xa1", 4); + write_32bit_value(obj_summary_len); + write_32bit_value(0); // checksum, unused + write_32bit_value(0); // num, unused + write_32bit_value(runs); + for (i = 3; i < obj_summary_len; ++i) + write_32bit_value(0); + + /* Program summary tag */ + write_bytes("\0\0\0\xa3", 4); // tag indicates 1 program + write_32bit_value(0); // 0 length + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: %u runs\n", runs); +#endif +} + +void llvm_gcda_end_file() { + /* Write out EOF record. */ + if (output_file) { + write_bytes("\0\0\0\0\0\0\0\0", 8); + + if (new_file) { + fwrite(write_buffer, cur_pos, 1, output_file); + free(write_buffer); + } else { + unmap_file(); + } + + fclose(output_file); + output_file = NULL; + write_buffer = NULL; + } + free(filename); + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: -----\n"); +#endif +} + +void llvm_register_writeout_function(writeout_fn fn) { + struct writeout_fn_node *new_node = malloc(sizeof(struct writeout_fn_node)); + new_node->fn = fn; + new_node->next = NULL; + + if (!writeout_fn_head) { + writeout_fn_head = writeout_fn_tail = new_node; + } else { + writeout_fn_tail->next = new_node; + writeout_fn_tail = new_node; + } +} + +void llvm_writeout_files() { + struct writeout_fn_node *curr = writeout_fn_head; + + while (curr) { + curr->fn(); + curr = curr->next; + } +} + +void llvm_delete_writeout_function_list() { + while (writeout_fn_head) { + struct writeout_fn_node *node = writeout_fn_head; + writeout_fn_head = writeout_fn_head->next; + free(node); + } + + writeout_fn_head = writeout_fn_tail = NULL; +} + +void llvm_register_flush_function(flush_fn fn) { + struct flush_fn_node *new_node = malloc(sizeof(struct flush_fn_node)); + new_node->fn = fn; + new_node->next = NULL; + + if (!flush_fn_head) { + flush_fn_head = flush_fn_tail = new_node; + } else { + flush_fn_tail->next = new_node; + flush_fn_tail = new_node; + } +} + +void __gcov_flush() { + struct flush_fn_node *curr = flush_fn_head; + + while (curr) { + curr->fn(); + curr = curr->next; + } +} + +void llvm_delete_flush_function_list() { + while (flush_fn_head) { + struct flush_fn_node *node = flush_fn_head; + flush_fn_head = flush_fn_head->next; + free(node); + } + + flush_fn_head = flush_fn_tail = NULL; +} + +void llvm_gcov_init(writeout_fn wfn, flush_fn ffn) { + static int atexit_ran = 0; + + if (wfn) + llvm_register_writeout_function(wfn); + + if (ffn) + llvm_register_flush_function(ffn); + + if (atexit_ran == 0) { + atexit_ran = 1; + + /* Make sure we write out the data and delete the data structures. */ + atexit(llvm_delete_flush_function_list); + atexit(llvm_delete_writeout_function_list); + atexit(llvm_writeout_files); + } +} diff --git a/projects/compiler-rt/lib/profile/Makefile.mk b/projects/compiler-rt/lib/profile/Makefile.mk new file mode 100644 index 00000000..7689c9a0 --- /dev/null +++ b/projects/compiler-rt/lib/profile/Makefile.mk @@ -0,0 +1,18 @@ +#===- lib/profile/Makefile.mk ------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := profile +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) +ObjNames := $(Sources:%.c=%.o) +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) diff --git a/projects/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/projects/compiler-rt/lib/sanitizer_common/CMakeLists.txt new file mode 100644 index 00000000..84c1e67d --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/CMakeLists.txt @@ -0,0 +1,115 @@ +# Build system for the common Sanitizer runtime support library components. +# These components are shared between AddressSanitizer and ThreadSanitizer. + +set(SANITIZER_SOURCES + sanitizer_allocator.cc + sanitizer_common.cc + sanitizer_coverage.cc + sanitizer_flags.cc + sanitizer_libc.cc + sanitizer_libignore.cc + sanitizer_linux.cc + sanitizer_mac.cc + sanitizer_platform_limits_linux.cc + sanitizer_platform_limits_posix.cc + sanitizer_posix.cc + sanitizer_printf.cc + sanitizer_stackdepot.cc + sanitizer_stacktrace.cc + sanitizer_suppressions.cc + sanitizer_symbolizer.cc + sanitizer_symbolizer_win.cc + sanitizer_thread_registry.cc + sanitizer_win.cc) + +set(SANITIZER_LIBCDEP_SOURCES + sanitizer_common_libcdep.cc + sanitizer_linux_libcdep.cc + sanitizer_posix_libcdep.cc + sanitizer_stacktrace_libcdep.cc + sanitizer_stoptheworld_linux_libcdep.cc + sanitizer_symbolizer_libcdep.cc + sanitizer_symbolizer_posix_libcdep.cc) + +# Explicitly list all sanitizer_common headers. Not all of these are +# included in sanitizer_common source files, but we need to depend on +# headers when building our custom unit tests. +set(SANITIZER_HEADERS + sanitizer_allocator.h + sanitizer_allocator_internal.h + sanitizer_atomic_clang.h + sanitizer_atomic_msvc.h + sanitizer_atomic.h + sanitizer_common.h + sanitizer_common_interceptors.inc + sanitizer_common_interceptors_ioctl.inc + sanitizer_common_interceptors_scanf.inc + sanitizer_common_syscalls.inc + sanitizer_flags.h + sanitizer_internal_defs.h + sanitizer_lfstack.h + sanitizer_libc.h + sanitizer_libignore.h + sanitizer_linux.h + sanitizer_list.h + sanitizer_mutex.h + sanitizer_placement_new.h + sanitizer_platform_interceptors.h + sanitizer_procmaps.h + sanitizer_quarantine.h + sanitizer_report_decorator.h + sanitizer_stackdepot.h + sanitizer_stacktrace.h + sanitizer_symbolizer.h + sanitizer_thread_registry.h) + +if (NOT MSVC) + set(SANITIZER_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fno-rtti) +else() + set(SANITIZER_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + /GR-) +endif() + +if(SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG) + list(APPEND SANITIZER_CFLAGS -Wglobal-constructors) +endif() + +set(SANITIZER_RUNTIME_LIBRARIES) +if(APPLE) + # Build universal binary on APPLE. + foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + add_compiler_rt_darwin_object_library(RTSanitizerCommon ${os} + ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES} + CFLAGS ${SANITIZER_CFLAGS}) + list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${os}) + endforeach() +elseif(ANDROID) + add_library(RTSanitizerCommon.arm.android OBJECT + ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES}) + set_target_compile_flags(RTSanitizerCommon.arm.android + ${SANITIZER_CFLAGS}) + list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.arm.android) +else() + # Otherwise, build separate libraries for each target. + foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH}) + add_compiler_rt_object_library(RTSanitizerCommon ${arch} + SOURCES ${SANITIZER_SOURCES} CFLAGS ${SANITIZER_CFLAGS}) + add_compiler_rt_object_library(RTSanitizerCommonLibc ${arch} + SOURCES ${SANITIZER_LIBCDEP_SOURCES} CFLAGS ${SANITIZER_CFLAGS}) + add_compiler_rt_static_runtime(clang_rt.san-${arch} ${arch} + SOURCES $ + $ + CFLAGS ${SANITIZER_CFLAGS}) + list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${arch} + RTSanitizerCommonLibc.${arch}) + endforeach() +endif() + +# Unit tests for common sanitizer runtime. +if(LLVM_INCLUDE_TESTS) + add_subdirectory(tests) +endif() diff --git a/projects/compiler-rt/lib/sanitizer_common/Makefile.mk b/projects/compiler-rt/lib/sanitizer_common/Makefile.mk new file mode 100644 index 00000000..da83c2d6 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/Makefile.mk @@ -0,0 +1,22 @@ +#===- lib/sanitizer_common/Makefile.mk ---------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := sanitizer_common +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) + +# Define a convenience variable for all the sanitizer_common functions. +SanitizerCommonFunctions := $(Sources:%.cc=%) diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc new file mode 100644 index 00000000..daaf7e1c --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc @@ -0,0 +1,153 @@ +//===-- sanitizer_allocator.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// This allocator is used inside run-times. +//===----------------------------------------------------------------------===// +#include "sanitizer_allocator.h" +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_flags.h" + +namespace __sanitizer { + +// ThreadSanitizer for Go uses libc malloc/free. +#if defined(SANITIZER_GO) +# if SANITIZER_LINUX && !SANITIZER_ANDROID +extern "C" void *__libc_malloc(uptr size); +extern "C" void __libc_free(void *ptr); +# define LIBC_MALLOC __libc_malloc +# define LIBC_FREE __libc_free +# else +# include +# define LIBC_MALLOC malloc +# define LIBC_FREE free +# endif + +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + (void)cache; + return LIBC_MALLOC(size); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + (void)cache; + LIBC_FREE(ptr); +} + +InternalAllocator *internal_allocator() { + return 0; +} + +#else // SANITIZER_GO + +static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; +static atomic_uint8_t internal_allocator_initialized; +static StaticSpinMutex internal_alloc_init_mu; + +static InternalAllocatorCache internal_allocator_cache; +static StaticSpinMutex internal_allocator_cache_mu; + +InternalAllocator *internal_allocator() { + InternalAllocator *internal_allocator_instance = + reinterpret_cast(&internal_alloc_placeholder); + if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) { + SpinMutexLock l(&internal_alloc_init_mu); + if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == + 0) { + internal_allocator_instance->Init(); + atomic_store(&internal_allocator_initialized, 1, memory_order_release); + } + } + return internal_allocator_instance; +} + +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Allocate(&internal_allocator_cache, size, 8, + false); + } + return internal_allocator()->Allocate(cache, size, 8, false); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Deallocate(&internal_allocator_cache, ptr); + } + internal_allocator()->Deallocate(cache, ptr); +} + +#endif // SANITIZER_GO + +const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; + +void *InternalAlloc(uptr size, InternalAllocatorCache *cache) { + if (size + sizeof(u64) < size) + return 0; + void *p = RawInternalAlloc(size + sizeof(u64), cache); + if (p == 0) + return 0; + ((u64*)p)[0] = kBlockMagic; + return (char*)p + sizeof(u64); +} + +void InternalFree(void *addr, InternalAllocatorCache *cache) { + if (addr == 0) + return; + addr = (char*)addr - sizeof(u64); + CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); + ((u64*)addr)[0] = 0; + RawInternalFree(addr, cache); +} + +// LowLevelAllocator +static LowLevelAllocateCallback low_level_alloc_callback; + +void *LowLevelAllocator::Allocate(uptr size) { + // Align allocation size. + size = RoundUpTo(size, 8); + if (allocated_end_ - allocated_current_ < (sptr)size) { + uptr size_to_allocate = Max(size, GetPageSizeCached()); + allocated_current_ = + (char*)MmapOrDie(size_to_allocate, __FUNCTION__); + allocated_end_ = allocated_current_ + size_to_allocate; + if (low_level_alloc_callback) { + low_level_alloc_callback((uptr)allocated_current_, + size_to_allocate); + } + } + CHECK(allocated_end_ - allocated_current_ >= (sptr)size); + void *res = allocated_current_; + allocated_current_ += size; + return res; +} + +void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { + low_level_alloc_callback = callback; +} + +bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { + if (!size) return false; + uptr max = (uptr)-1L; + return (max / size) < n; +} + +void *AllocatorReturnNull() { + if (common_flags()->allocator_may_return_null) + return 0; + Report("%s's allocator is terminating the process instead of returning 0\n", + SanitizerToolName); + Report("If you don't like this behavior set allocator_may_return_null=1\n"); + CHECK(0); + return 0; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h new file mode 100644 index 00000000..6075cfe9 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h @@ -0,0 +1,1318 @@ +//===-- sanitizer_allocator.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Specialized memory allocator for ThreadSanitizer, MemorySanitizer, etc. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ALLOCATOR_H +#define SANITIZER_ALLOCATOR_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_list.h" +#include "sanitizer_mutex.h" +#include "sanitizer_lfstack.h" + +namespace __sanitizer { + +// Depending on allocator_may_return_null either return 0 or crash. +void *AllocatorReturnNull(); + +// SizeClassMap maps allocation sizes into size classes and back. +// Class 0 corresponds to size 0. +// Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16). +// Next 4 classes: 256 + i * 64 (i = 1 to 4). +// Next 4 classes: 512 + i * 128 (i = 1 to 4). +// ... +// Next 4 classes: 2^k + i * 2^(k-2) (i = 1 to 4). +// Last class corresponds to kMaxSize = 1 << kMaxSizeLog. +// +// This structure of the size class map gives us: +// - Efficient table-free class-to-size and size-to-class functions. +// - Difference between two consequent size classes is betweed 14% and 25% +// +// This class also gives a hint to a thread-caching allocator about the amount +// of chunks that need to be cached per-thread: +// - kMaxNumCached is the maximal number of chunks per size class. +// - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class. +// +// Part of output of SizeClassMap::Print(): +// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0 +// c01 => s: 16 diff: +16 00% l 4 cached: 256 4096; id 1 +// c02 => s: 32 diff: +16 100% l 5 cached: 256 8192; id 2 +// c03 => s: 48 diff: +16 50% l 5 cached: 256 12288; id 3 +// c04 => s: 64 diff: +16 33% l 6 cached: 256 16384; id 4 +// c05 => s: 80 diff: +16 25% l 6 cached: 256 20480; id 5 +// c06 => s: 96 diff: +16 20% l 6 cached: 256 24576; id 6 +// c07 => s: 112 diff: +16 16% l 6 cached: 256 28672; id 7 +// +// c08 => s: 128 diff: +16 14% l 7 cached: 256 32768; id 8 +// c09 => s: 144 diff: +16 12% l 7 cached: 256 36864; id 9 +// c10 => s: 160 diff: +16 11% l 7 cached: 256 40960; id 10 +// c11 => s: 176 diff: +16 10% l 7 cached: 256 45056; id 11 +// c12 => s: 192 diff: +16 09% l 7 cached: 256 49152; id 12 +// c13 => s: 208 diff: +16 08% l 7 cached: 256 53248; id 13 +// c14 => s: 224 diff: +16 07% l 7 cached: 256 57344; id 14 +// c15 => s: 240 diff: +16 07% l 7 cached: 256 61440; id 15 +// +// c16 => s: 256 diff: +16 06% l 8 cached: 256 65536; id 16 +// c17 => s: 320 diff: +64 25% l 8 cached: 204 65280; id 17 +// c18 => s: 384 diff: +64 20% l 8 cached: 170 65280; id 18 +// c19 => s: 448 diff: +64 16% l 8 cached: 146 65408; id 19 +// +// c20 => s: 512 diff: +64 14% l 9 cached: 128 65536; id 20 +// c21 => s: 640 diff: +128 25% l 9 cached: 102 65280; id 21 +// c22 => s: 768 diff: +128 20% l 9 cached: 85 65280; id 22 +// c23 => s: 896 diff: +128 16% l 9 cached: 73 65408; id 23 +// +// c24 => s: 1024 diff: +128 14% l 10 cached: 64 65536; id 24 +// c25 => s: 1280 diff: +256 25% l 10 cached: 51 65280; id 25 +// c26 => s: 1536 diff: +256 20% l 10 cached: 42 64512; id 26 +// c27 => s: 1792 diff: +256 16% l 10 cached: 36 64512; id 27 +// +// ... +// +// c48 => s: 65536 diff: +8192 14% l 16 cached: 1 65536; id 48 +// c49 => s: 81920 diff: +16384 25% l 16 cached: 1 81920; id 49 +// c50 => s: 98304 diff: +16384 20% l 16 cached: 1 98304; id 50 +// c51 => s: 114688 diff: +16384 16% l 16 cached: 1 114688; id 51 +// +// c52 => s: 131072 diff: +16384 14% l 17 cached: 1 131072; id 52 + +template +class SizeClassMap { + static const uptr kMinSizeLog = 4; + static const uptr kMidSizeLog = kMinSizeLog + 4; + static const uptr kMinSize = 1 << kMinSizeLog; + static const uptr kMidSize = 1 << kMidSizeLog; + static const uptr kMidClass = kMidSize / kMinSize; + static const uptr S = 2; + static const uptr M = (1 << S) - 1; + + public: + static const uptr kMaxNumCached = kMaxNumCachedT; + // We transfer chunks between central and thread-local free lists in batches. + // For small size classes we allocate batches separately. + // For large size classes we use one of the chunks to store the batch. + struct TransferBatch { + TransferBatch *next; + uptr count; + void *batch[kMaxNumCached]; + }; + + static const uptr kMaxSize = 1UL << kMaxSizeLog; + static const uptr kNumClasses = + kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1; + COMPILER_CHECK(kNumClasses >= 32 && kNumClasses <= 256); + static const uptr kNumClassesRounded = + kNumClasses == 32 ? 32 : + kNumClasses <= 64 ? 64 : + kNumClasses <= 128 ? 128 : 256; + + static uptr Size(uptr class_id) { + if (class_id <= kMidClass) + return kMinSize * class_id; + class_id -= kMidClass; + uptr t = kMidSize << (class_id >> S); + return t + (t >> S) * (class_id & M); + } + + static uptr ClassID(uptr size) { + if (size <= kMidSize) + return (size + kMinSize - 1) >> kMinSizeLog; + if (size > kMaxSize) return 0; + uptr l = MostSignificantSetBitIndex(size); + uptr hbits = (size >> (l - S)) & M; + uptr lbits = size & ((1 << (l - S)) - 1); + uptr l1 = l - kMidSizeLog; + return kMidClass + (l1 << S) + hbits + (lbits > 0); + } + + static uptr MaxCached(uptr class_id) { + if (class_id == 0) return 0; + uptr n = (1UL << kMaxBytesCachedLog) / Size(class_id); + return Max(1, Min(kMaxNumCached, n)); + } + + static void Print() { + uptr prev_s = 0; + uptr total_cached = 0; + for (uptr i = 0; i < kNumClasses; i++) { + uptr s = Size(i); + if (s >= kMidSize / 2 && (s & (s - 1)) == 0) + Printf("\n"); + uptr d = s - prev_s; + uptr p = prev_s ? (d * 100 / prev_s) : 0; + uptr l = s ? MostSignificantSetBitIndex(s) : 0; + uptr cached = MaxCached(i) * s; + Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd " + "cached: %zd %zd; id %zd\n", + i, Size(i), d, p, l, MaxCached(i), cached, ClassID(s)); + total_cached += cached; + prev_s = s; + } + Printf("Total cached: %zd\n", total_cached); + } + + static bool SizeClassRequiresSeparateTransferBatch(uptr class_id) { + return Size(class_id) < sizeof(TransferBatch) - + sizeof(uptr) * (kMaxNumCached - MaxCached(class_id)); + } + + static void Validate() { + for (uptr c = 1; c < kNumClasses; c++) { + // Printf("Validate: c%zd\n", c); + uptr s = Size(c); + CHECK_NE(s, 0U); + CHECK_EQ(ClassID(s), c); + if (c != kNumClasses - 1) + CHECK_EQ(ClassID(s + 1), c + 1); + CHECK_EQ(ClassID(s - 1), c); + if (c) + CHECK_GT(Size(c), Size(c-1)); + } + CHECK_EQ(ClassID(kMaxSize + 1), 0); + + for (uptr s = 1; s <= kMaxSize; s++) { + uptr c = ClassID(s); + // Printf("s%zd => c%zd\n", s, c); + CHECK_LT(c, kNumClasses); + CHECK_GE(Size(c), s); + if (c > 0) + CHECK_LT(Size(c-1), s); + } + } +}; + +typedef SizeClassMap<17, 128, 16> DefaultSizeClassMap; +typedef SizeClassMap<17, 64, 14> CompactSizeClassMap; +template struct SizeClassAllocatorLocalCache; + +// Memory allocator statistics +enum AllocatorStat { + AllocatorStatMalloced, + AllocatorStatFreed, + AllocatorStatMmapped, + AllocatorStatUnmapped, + AllocatorStatCount +}; + +typedef u64 AllocatorStatCounters[AllocatorStatCount]; + +// Per-thread stats, live in per-thread cache. +class AllocatorStats { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + + void Add(AllocatorStat i, u64 v) { + v += atomic_load(&stats_[i], memory_order_relaxed); + atomic_store(&stats_[i], v, memory_order_relaxed); + } + + void Set(AllocatorStat i, u64 v) { + atomic_store(&stats_[i], v, memory_order_relaxed); + } + + u64 Get(AllocatorStat i) const { + return atomic_load(&stats_[i], memory_order_relaxed); + } + + private: + friend class AllocatorGlobalStats; + AllocatorStats *next_; + AllocatorStats *prev_; + atomic_uint64_t stats_[AllocatorStatCount]; +}; + +// Global stats, used for aggregation and querying. +class AllocatorGlobalStats : public AllocatorStats { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + next_ = this; + prev_ = this; + } + + void Register(AllocatorStats *s) { + SpinMutexLock l(&mu_); + s->next_ = next_; + s->prev_ = this; + next_->prev_ = s; + next_ = s; + } + + void Unregister(AllocatorStats *s) { + SpinMutexLock l(&mu_); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + for (int i = 0; i < AllocatorStatCount; i++) + Add(AllocatorStat(i), s->Get(AllocatorStat(i))); + } + + void Get(AllocatorStatCounters s) const { + internal_memset(s, 0, AllocatorStatCount * sizeof(u64)); + SpinMutexLock l(&mu_); + const AllocatorStats *stats = this; + for (;;) { + for (int i = 0; i < AllocatorStatCount; i++) + s[i] += stats->Get(AllocatorStat(i)); + stats = stats->next_; + if (stats == this) + break; + } + } + + private: + mutable SpinMutex mu_; +}; + +// Allocators call these callbacks on mmap/munmap. +struct NoOpMapUnmapCallback { + void OnMap(uptr p, uptr size) const { } + void OnUnmap(uptr p, uptr size) const { } +}; + +// Callback type for iterating over chunks. +typedef void (*ForEachChunkCallback)(uptr chunk, void *arg); + +// SizeClassAllocator64 -- allocator for 64-bit address space. +// +// Space: a portion of address space of kSpaceSize bytes starting at +// a fixed address (kSpaceBeg). Both constants are powers of two and +// kSpaceBeg is kSpaceSize-aligned. +// At the beginning the entire space is mprotect-ed, then small parts of it +// are mapped on demand. +// +// Region: a part of Space dedicated to a single size class. +// There are kNumClasses Regions of equal size. +// +// UserChunk: a piece of memory returned to user. +// MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk. +// +// A Region looks like this: +// UserChunk1 ... UserChunkN MetaChunkN ... MetaChunk1 +template +class SizeClassAllocator64 { + public: + typedef typename SizeClassMap::TransferBatch Batch; + typedef SizeClassAllocator64 ThisT; + typedef SizeClassAllocatorLocalCache AllocatorCache; + + void Init() { + CHECK_EQ(kSpaceBeg, + reinterpret_cast(Mprotect(kSpaceBeg, kSpaceSize))); + MapWithCallback(kSpaceEnd, AdditionalSize()); + } + + void MapWithCallback(uptr beg, uptr size) { + CHECK_EQ(beg, reinterpret_cast(MmapFixedOrDie(beg, size))); + MapUnmapCallback().OnMap(beg, size); + } + + void UnmapWithCallback(uptr beg, uptr size) { + MapUnmapCallback().OnUnmap(beg, size); + UnmapOrDie(reinterpret_cast(beg), size); + } + + static bool CanAllocate(uptr size, uptr alignment) { + return size <= SizeClassMap::kMaxSize && + alignment <= SizeClassMap::kMaxSize; + } + + NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, + uptr class_id) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + Batch *b = region->free_list.Pop(); + if (b == 0) + b = PopulateFreeList(stat, c, class_id, region); + region->n_allocated += b->count; + return b; + } + + NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { + RegionInfo *region = GetRegionInfo(class_id); + CHECK_GT(b->count, 0); + region->free_list.Push(b); + region->n_freed += b->count; + } + + static bool PointerIsMine(const void *p) { + return reinterpret_cast(p) / kSpaceSize == kSpaceBeg / kSpaceSize; + } + + static uptr GetSizeClass(const void *p) { + return (reinterpret_cast(p) / kRegionSize) % kNumClassesRounded; + } + + void *GetBlockBegin(const void *p) { + uptr class_id = GetSizeClass(p); + uptr size = SizeClassMap::Size(class_id); + if (!size) return 0; + uptr chunk_idx = GetChunkIdx((uptr)p, size); + uptr reg_beg = (uptr)p & ~(kRegionSize - 1); + uptr beg = chunk_idx * size; + uptr next_beg = beg + size; + if (class_id >= kNumClasses) return 0; + RegionInfo *region = GetRegionInfo(class_id); + if (region->mapped_user >= next_beg) + return reinterpret_cast(reg_beg + beg); + return 0; + } + + static uptr GetActuallyAllocatedSize(void *p) { + CHECK(PointerIsMine(p)); + return SizeClassMap::Size(GetSizeClass(p)); + } + + uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } + + void *GetMetaData(const void *p) { + uptr class_id = GetSizeClass(p); + uptr size = SizeClassMap::Size(class_id); + uptr chunk_idx = GetChunkIdx(reinterpret_cast(p), size); + return reinterpret_cast(kSpaceBeg + (kRegionSize * (class_id + 1)) - + (1 + chunk_idx) * kMetadataSize); + } + + uptr TotalMemoryUsed() { + uptr res = 0; + for (uptr i = 0; i < kNumClasses; i++) + res += GetRegionInfo(i)->allocated_user; + return res; + } + + // Test-only. + void TestOnlyUnmap() { + UnmapWithCallback(kSpaceBeg, kSpaceSize + AdditionalSize()); + } + + void PrintStats() { + uptr total_mapped = 0; + uptr n_allocated = 0; + uptr n_freed = 0; + for (uptr class_id = 1; class_id < kNumClasses; class_id++) { + RegionInfo *region = GetRegionInfo(class_id); + total_mapped += region->mapped_user; + n_allocated += region->n_allocated; + n_freed += region->n_freed; + } + Printf("Stats: SizeClassAllocator64: %zdM mapped in %zd allocations; " + "remains %zd\n", + total_mapped >> 20, n_allocated, n_allocated - n_freed); + for (uptr class_id = 1; class_id < kNumClasses; class_id++) { + RegionInfo *region = GetRegionInfo(class_id); + if (region->mapped_user == 0) continue; + Printf(" %02zd (%zd): total: %zd K allocs: %zd remains: %zd\n", + class_id, + SizeClassMap::Size(class_id), + region->mapped_user >> 10, + region->n_allocated, + region->n_allocated - region->n_freed); + } + } + + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + for (uptr i = 0; i < kNumClasses; i++) { + GetRegionInfo(i)->mutex.Lock(); + } + } + + void ForceUnlock() { + for (int i = (int)kNumClasses - 1; i >= 0; i--) { + GetRegionInfo(i)->mutex.Unlock(); + } + } + + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + for (uptr class_id = 1; class_id < kNumClasses; class_id++) { + RegionInfo *region = GetRegionInfo(class_id); + uptr chunk_size = SizeClassMap::Size(class_id); + uptr region_beg = kSpaceBeg + class_id * kRegionSize; + for (uptr chunk = region_beg; + chunk < region_beg + region->allocated_user; + chunk += chunk_size) { + // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); + callback(chunk, arg); + } + } + } + + typedef SizeClassMap SizeClassMapT; + static const uptr kNumClasses = SizeClassMap::kNumClasses; + static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; + + private: + static const uptr kRegionSize = kSpaceSize / kNumClassesRounded; + static const uptr kSpaceEnd = kSpaceBeg + kSpaceSize; + COMPILER_CHECK(kSpaceBeg % kSpaceSize == 0); + // kRegionSize must be >= 2^32. + COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2))); + // Populate the free list with at most this number of bytes at once + // or with one element if its size is greater. + static const uptr kPopulateSize = 1 << 14; + // Call mmap for user memory with at least this size. + static const uptr kUserMapSize = 1 << 16; + // Call mmap for metadata memory with at least this size. + static const uptr kMetaMapSize = 1 << 16; + + struct RegionInfo { + BlockingMutex mutex; + LFStack free_list; + uptr allocated_user; // Bytes allocated for user memory. + uptr allocated_meta; // Bytes allocated for metadata. + uptr mapped_user; // Bytes mapped for user memory. + uptr mapped_meta; // Bytes mapped for metadata. + uptr n_allocated, n_freed; // Just stats. + }; + COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize); + + static uptr AdditionalSize() { + return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded, + GetPageSizeCached()); + } + + RegionInfo *GetRegionInfo(uptr class_id) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *regions = reinterpret_cast(kSpaceBeg + kSpaceSize); + return ®ions[class_id]; + } + + static uptr GetChunkIdx(uptr chunk, uptr size) { + uptr offset = chunk % kRegionSize; + // Here we divide by a non-constant. This is costly. + // size always fits into 32-bits. If the offset fits too, use 32-bit div. + if (offset >> (SANITIZER_WORDSIZE / 2)) + return offset / size; + return (u32)offset / (u32)size; + } + + NOINLINE Batch* PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, + uptr class_id, RegionInfo *region) { + BlockingMutexLock l(®ion->mutex); + Batch *b = region->free_list.Pop(); + if (b) + return b; + uptr size = SizeClassMap::Size(class_id); + uptr count = size < kPopulateSize ? SizeClassMap::MaxCached(class_id) : 1; + uptr beg_idx = region->allocated_user; + uptr end_idx = beg_idx + count * size; + uptr region_beg = kSpaceBeg + kRegionSize * class_id; + if (end_idx + size > region->mapped_user) { + // Do the mmap for the user memory. + uptr map_size = kUserMapSize; + while (end_idx + size > region->mapped_user + map_size) + map_size += kUserMapSize; + CHECK_GE(region->mapped_user + map_size, end_idx); + MapWithCallback(region_beg + region->mapped_user, map_size); + stat->Add(AllocatorStatMmapped, map_size); + region->mapped_user += map_size; + } + uptr total_count = (region->mapped_user - beg_idx - size) + / size / count * count; + region->allocated_meta += total_count * kMetadataSize; + if (region->allocated_meta > region->mapped_meta) { + uptr map_size = kMetaMapSize; + while (region->allocated_meta > region->mapped_meta + map_size) + map_size += kMetaMapSize; + // Do the mmap for the metadata. + CHECK_GE(region->mapped_meta + map_size, region->allocated_meta); + MapWithCallback(region_beg + kRegionSize - + region->mapped_meta - map_size, map_size); + region->mapped_meta += map_size; + } + CHECK_LE(region->allocated_meta, region->mapped_meta); + if (region->mapped_user + region->mapped_meta > kRegionSize) { + Printf("%s: Out of memory. Dying. ", SanitizerToolName); + Printf("The process has exhausted %zuMB for size class %zu.\n", + kRegionSize / 1024 / 1024, size); + Die(); + } + for (;;) { + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) + b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch))); + else + b = (Batch*)(region_beg + beg_idx); + b->count = count; + for (uptr i = 0; i < count; i++) + b->batch[i] = (void*)(region_beg + beg_idx + i * size); + region->allocated_user += count * size; + CHECK_LE(region->allocated_user, region->mapped_user); + beg_idx += count * size; + if (beg_idx + count * size + size > region->mapped_user) + break; + CHECK_GT(b->count, 0); + region->free_list.Push(b); + } + return b; + } +}; + +// Maps integers in rage [0, kSize) to u8 values. +template +class FlatByteMap { + public: + void TestOnlyInit() { + internal_memset(map_, 0, sizeof(map_)); + } + + void set(uptr idx, u8 val) { + CHECK_LT(idx, kSize); + CHECK_EQ(0U, map_[idx]); + map_[idx] = val; + } + u8 operator[] (uptr idx) { + CHECK_LT(idx, kSize); + // FIXME: CHECK may be too expensive here. + return map_[idx]; + } + private: + u8 map_[kSize]; +}; + +// FIXME: Also implement TwoLevelByteMap. + +// SizeClassAllocator32 -- allocator for 32-bit address space. +// This allocator can theoretically be used on 64-bit arch, but there it is less +// efficient than SizeClassAllocator64. +// +// [kSpaceBeg, kSpaceBeg + kSpaceSize) is the range of addresses which can +// be returned by MmapOrDie(). +// +// Region: +// a result of a single call to MmapAlignedOrDie(kRegionSize, kRegionSize). +// Since the regions are aligned by kRegionSize, there are exactly +// kNumPossibleRegions possible regions in the address space and so we keep +// a ByteMap possible_regions to store the size classes of each Region. +// 0 size class means the region is not used by the allocator. +// +// One Region is used to allocate chunks of a single size class. +// A Region looks like this: +// UserChunk1 .. UserChunkN MetaChunkN .. MetaChunk1 +// +// In order to avoid false sharing the objects of this class should be +// chache-line aligned. +template +class SizeClassAllocator32 { + public: + typedef typename SizeClassMap::TransferBatch Batch; + typedef SizeClassAllocator32 ThisT; + typedef SizeClassAllocatorLocalCache AllocatorCache; + + void Init() { + possible_regions.TestOnlyInit(); + internal_memset(size_class_info_array, 0, sizeof(size_class_info_array)); + } + + void *MapWithCallback(uptr size) { + size = RoundUpTo(size, GetPageSizeCached()); + void *res = MmapOrDie(size, "SizeClassAllocator32"); + MapUnmapCallback().OnMap((uptr)res, size); + return res; + } + + void UnmapWithCallback(uptr beg, uptr size) { + MapUnmapCallback().OnUnmap(beg, size); + UnmapOrDie(reinterpret_cast(beg), size); + } + + static bool CanAllocate(uptr size, uptr alignment) { + return size <= SizeClassMap::kMaxSize && + alignment <= SizeClassMap::kMaxSize; + } + + void *GetMetaData(const void *p) { + CHECK(PointerIsMine(p)); + uptr mem = reinterpret_cast(p); + uptr beg = ComputeRegionBeg(mem); + uptr size = SizeClassMap::Size(GetSizeClass(p)); + u32 offset = mem - beg; + uptr n = offset / (u32)size; // 32-bit division + uptr meta = (beg + kRegionSize) - (n + 1) * kMetadataSize; + return reinterpret_cast(meta); + } + + NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, + uptr class_id) { + CHECK_LT(class_id, kNumClasses); + SizeClassInfo *sci = GetSizeClassInfo(class_id); + SpinMutexLock l(&sci->mutex); + if (sci->free_list.empty()) + PopulateFreeList(stat, c, sci, class_id); + CHECK(!sci->free_list.empty()); + Batch *b = sci->free_list.front(); + sci->free_list.pop_front(); + return b; + } + + NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { + CHECK_LT(class_id, kNumClasses); + SizeClassInfo *sci = GetSizeClassInfo(class_id); + SpinMutexLock l(&sci->mutex); + CHECK_GT(b->count, 0); + sci->free_list.push_front(b); + } + + bool PointerIsMine(const void *p) { + return GetSizeClass(p) != 0; + } + + uptr GetSizeClass(const void *p) { + return possible_regions[ComputeRegionId(reinterpret_cast(p))]; + } + + void *GetBlockBegin(const void *p) { + CHECK(PointerIsMine(p)); + uptr mem = reinterpret_cast(p); + uptr beg = ComputeRegionBeg(mem); + uptr size = SizeClassMap::Size(GetSizeClass(p)); + u32 offset = mem - beg; + u32 n = offset / (u32)size; // 32-bit division + uptr res = beg + (n * (u32)size); + return reinterpret_cast(res); + } + + uptr GetActuallyAllocatedSize(void *p) { + CHECK(PointerIsMine(p)); + return SizeClassMap::Size(GetSizeClass(p)); + } + + uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } + + uptr TotalMemoryUsed() { + // No need to lock here. + uptr res = 0; + for (uptr i = 0; i < kNumPossibleRegions; i++) + if (possible_regions[i]) + res += kRegionSize; + return res; + } + + void TestOnlyUnmap() { + for (uptr i = 0; i < kNumPossibleRegions; i++) + if (possible_regions[i]) + UnmapWithCallback((i * kRegionSize), kRegionSize); + } + + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + for (uptr i = 0; i < kNumClasses; i++) { + GetSizeClassInfo(i)->mutex.Lock(); + } + } + + void ForceUnlock() { + for (int i = kNumClasses - 1; i >= 0; i--) { + GetSizeClassInfo(i)->mutex.Unlock(); + } + } + + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + for (uptr region = 0; region < kNumPossibleRegions; region++) + if (possible_regions[region]) { + uptr chunk_size = SizeClassMap::Size(possible_regions[region]); + uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize); + uptr region_beg = region * kRegionSize; + for (uptr chunk = region_beg; + chunk < region_beg + max_chunks_in_region * chunk_size; + chunk += chunk_size) { + // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); + callback(chunk, arg); + } + } + } + + void PrintStats() { + } + + typedef SizeClassMap SizeClassMapT; + static const uptr kNumClasses = SizeClassMap::kNumClasses; + + private: + static const uptr kRegionSize = 1 << kRegionSizeLog; + static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize; + + struct SizeClassInfo { + SpinMutex mutex; + IntrusiveList free_list; + char padding[kCacheLineSize - sizeof(uptr) - sizeof(IntrusiveList)]; + }; + COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize); + + uptr ComputeRegionId(uptr mem) { + uptr res = mem >> kRegionSizeLog; + CHECK_LT(res, kNumPossibleRegions); + return res; + } + + uptr ComputeRegionBeg(uptr mem) { + return mem & ~(kRegionSize - 1); + } + + uptr AllocateRegion(AllocatorStats *stat, uptr class_id) { + CHECK_LT(class_id, kNumClasses); + uptr res = reinterpret_cast(MmapAlignedOrDie(kRegionSize, kRegionSize, + "SizeClassAllocator32")); + MapUnmapCallback().OnMap(res, kRegionSize); + stat->Add(AllocatorStatMmapped, kRegionSize); + CHECK_EQ(0U, (res & (kRegionSize - 1))); + possible_regions.set(ComputeRegionId(res), static_cast(class_id)); + return res; + } + + SizeClassInfo *GetSizeClassInfo(uptr class_id) { + CHECK_LT(class_id, kNumClasses); + return &size_class_info_array[class_id]; + } + + void PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, + SizeClassInfo *sci, uptr class_id) { + uptr size = SizeClassMap::Size(class_id); + uptr reg = AllocateRegion(stat, class_id); + uptr n_chunks = kRegionSize / (size + kMetadataSize); + uptr max_count = SizeClassMap::MaxCached(class_id); + Batch *b = 0; + for (uptr i = reg; i < reg + n_chunks * size; i += size) { + if (b == 0) { + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) + b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch))); + else + b = (Batch*)i; + b->count = 0; + } + b->batch[b->count++] = (void*)i; + if (b->count == max_count) { + CHECK_GT(b->count, 0); + sci->free_list.push_back(b); + b = 0; + } + } + if (b) { + CHECK_GT(b->count, 0); + sci->free_list.push_back(b); + } + } + + ByteMap possible_regions; + SizeClassInfo size_class_info_array[kNumClasses]; +}; + +// Objects of this type should be used as local caches for SizeClassAllocator64 +// or SizeClassAllocator32. Since the typical use of this class is to have one +// object per thread in TLS, is has to be POD. +template +struct SizeClassAllocatorLocalCache { + typedef SizeClassAllocator Allocator; + static const uptr kNumClasses = SizeClassAllocator::kNumClasses; + + void Init(AllocatorGlobalStats *s) { + stats_.Init(); + if (s) + s->Register(&stats_); + } + + void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) { + Drain(allocator); + if (s) + s->Unregister(&stats_); + } + + void *Allocate(SizeClassAllocator *allocator, uptr class_id) { + CHECK_NE(class_id, 0UL); + CHECK_LT(class_id, kNumClasses); + stats_.Add(AllocatorStatMalloced, SizeClassMap::Size(class_id)); + PerClass *c = &per_class_[class_id]; + if (UNLIKELY(c->count == 0)) + Refill(allocator, class_id); + void *res = c->batch[--c->count]; + PREFETCH(c->batch[c->count - 1]); + return res; + } + + void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { + CHECK_NE(class_id, 0UL); + CHECK_LT(class_id, kNumClasses); + // If the first allocator call on a new thread is a deallocation, then + // max_count will be zero, leading to check failure. + InitCache(); + stats_.Add(AllocatorStatFreed, SizeClassMap::Size(class_id)); + PerClass *c = &per_class_[class_id]; + CHECK_NE(c->max_count, 0UL); + if (UNLIKELY(c->count == c->max_count)) + Drain(allocator, class_id); + c->batch[c->count++] = p; + } + + void Drain(SizeClassAllocator *allocator) { + for (uptr class_id = 0; class_id < kNumClasses; class_id++) { + PerClass *c = &per_class_[class_id]; + while (c->count > 0) + Drain(allocator, class_id); + } + } + + // private: + typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap; + typedef typename SizeClassMap::TransferBatch Batch; + struct PerClass { + uptr count; + uptr max_count; + void *batch[2 * SizeClassMap::kMaxNumCached]; + }; + PerClass per_class_[kNumClasses]; + AllocatorStats stats_; + + void InitCache() { + if (per_class_[1].max_count) + return; + for (uptr i = 0; i < kNumClasses; i++) { + PerClass *c = &per_class_[i]; + c->max_count = 2 * SizeClassMap::MaxCached(i); + } + } + + NOINLINE void Refill(SizeClassAllocator *allocator, uptr class_id) { + InitCache(); + PerClass *c = &per_class_[class_id]; + Batch *b = allocator->AllocateBatch(&stats_, this, class_id); + CHECK_GT(b->count, 0); + for (uptr i = 0; i < b->count; i++) + c->batch[i] = b->batch[i]; + c->count = b->count; + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) + Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b); + } + + NOINLINE void Drain(SizeClassAllocator *allocator, uptr class_id) { + InitCache(); + PerClass *c = &per_class_[class_id]; + Batch *b; + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) + b = (Batch*)Allocate(allocator, SizeClassMap::ClassID(sizeof(Batch))); + else + b = (Batch*)c->batch[0]; + uptr cnt = Min(c->max_count / 2, c->count); + for (uptr i = 0; i < cnt; i++) { + b->batch[i] = c->batch[i]; + c->batch[i] = c->batch[i + c->max_count / 2]; + } + b->count = cnt; + c->count -= cnt; + CHECK_GT(b->count, 0); + allocator->DeallocateBatch(&stats_, class_id, b); + } +}; + +// This class can (de)allocate only large chunks of memory using mmap/unmap. +// The main purpose of this allocator is to cover large and rare allocation +// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64). +template +class LargeMmapAllocator { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + page_size_ = GetPageSizeCached(); + } + + void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) { + CHECK(IsPowerOfTwo(alignment)); + uptr map_size = RoundUpMapSize(size); + if (alignment > page_size_) + map_size += alignment; + if (map_size < size) return AllocatorReturnNull(); // Overflow. + uptr map_beg = reinterpret_cast( + MmapOrDie(map_size, "LargeMmapAllocator")); + MapUnmapCallback().OnMap(map_beg, map_size); + uptr map_end = map_beg + map_size; + uptr res = map_beg + page_size_; + if (res & (alignment - 1)) // Align. + res += alignment - (res & (alignment - 1)); + CHECK_EQ(0, res & (alignment - 1)); + CHECK_LE(res + size, map_end); + Header *h = GetHeader(res); + h->size = size; + h->map_beg = map_beg; + h->map_size = map_size; + uptr size_log = MostSignificantSetBitIndex(map_size); + CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log)); + { + SpinMutexLock l(&mutex_); + uptr idx = n_chunks_++; + chunks_sorted_ = false; + CHECK_LT(idx, kMaxNumChunks); + h->chunk_idx = idx; + chunks_[idx] = h; + stats.n_allocs++; + stats.currently_allocated += map_size; + stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated); + stats.by_size_log[size_log]++; + stat->Add(AllocatorStatMalloced, map_size); + stat->Add(AllocatorStatMmapped, map_size); + } + return reinterpret_cast(res); + } + + void Deallocate(AllocatorStats *stat, void *p) { + Header *h = GetHeader(p); + { + SpinMutexLock l(&mutex_); + uptr idx = h->chunk_idx; + CHECK_EQ(chunks_[idx], h); + CHECK_LT(idx, n_chunks_); + chunks_[idx] = chunks_[n_chunks_ - 1]; + chunks_[idx]->chunk_idx = idx; + n_chunks_--; + chunks_sorted_ = false; + stats.n_frees++; + stats.currently_allocated -= h->map_size; + stat->Add(AllocatorStatFreed, h->map_size); + stat->Add(AllocatorStatUnmapped, h->map_size); + } + MapUnmapCallback().OnUnmap(h->map_beg, h->map_size); + UnmapOrDie(reinterpret_cast(h->map_beg), h->map_size); + } + + uptr TotalMemoryUsed() { + SpinMutexLock l(&mutex_); + uptr res = 0; + for (uptr i = 0; i < n_chunks_; i++) { + Header *h = chunks_[i]; + CHECK_EQ(h->chunk_idx, i); + res += RoundUpMapSize(h->size); + } + return res; + } + + bool PointerIsMine(const void *p) { + return GetBlockBegin(p) != 0; + } + + uptr GetActuallyAllocatedSize(void *p) { + return RoundUpTo(GetHeader(p)->size, page_size_); + } + + // At least page_size_/2 metadata bytes is available. + void *GetMetaData(const void *p) { + // Too slow: CHECK_EQ(p, GetBlockBegin(p)); + if (!IsAligned(reinterpret_cast(p), page_size_)) { + Printf("%s: bad pointer %p\n", SanitizerToolName, p); + CHECK(IsAligned(reinterpret_cast(p), page_size_)); + } + return GetHeader(p) + 1; + } + + void *GetBlockBegin(const void *ptr) { + uptr p = reinterpret_cast(ptr); + SpinMutexLock l(&mutex_); + uptr nearest_chunk = 0; + // Cache-friendly linear search. + for (uptr i = 0; i < n_chunks_; i++) { + uptr ch = reinterpret_cast(chunks_[i]); + if (p < ch) continue; // p is at left to this chunk, skip it. + if (p - ch < p - nearest_chunk) + nearest_chunk = ch; + } + if (!nearest_chunk) + return 0; + Header *h = reinterpret_cast
(nearest_chunk); + CHECK_GE(nearest_chunk, h->map_beg); + CHECK_LT(nearest_chunk, h->map_beg + h->map_size); + CHECK_LE(nearest_chunk, p); + if (h->map_beg + h->map_size <= p) + return 0; + return GetUser(h); + } + + // This function does the same as GetBlockBegin, but is much faster. + // Must be called with the allocator locked. + void *GetBlockBeginFastLocked(void *ptr) { + mutex_.CheckLocked(); + uptr p = reinterpret_cast(ptr); + uptr n = n_chunks_; + if (!n) return 0; + if (!chunks_sorted_) { + // Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate. + SortArray(reinterpret_cast(chunks_), n); + for (uptr i = 0; i < n; i++) + chunks_[i]->chunk_idx = i; + chunks_sorted_ = true; + min_mmap_ = reinterpret_cast(chunks_[0]); + max_mmap_ = reinterpret_cast(chunks_[n - 1]) + + chunks_[n - 1]->map_size; + } + if (p < min_mmap_ || p >= max_mmap_) + return 0; + uptr beg = 0, end = n - 1; + // This loop is a log(n) lower_bound. It does not check for the exact match + // to avoid expensive cache-thrashing loads. + while (end - beg >= 2) { + uptr mid = (beg + end) / 2; // Invariant: mid >= beg + 1 + if (p < reinterpret_cast(chunks_[mid])) + end = mid - 1; // We are not interested in chunks_[mid]. + else + beg = mid; // chunks_[mid] may still be what we want. + } + + if (beg < end) { + CHECK_EQ(beg + 1, end); + // There are 2 chunks left, choose one. + if (p >= reinterpret_cast(chunks_[end])) + beg = end; + } + + Header *h = chunks_[beg]; + if (h->map_beg + h->map_size <= p || p < h->map_beg) + return 0; + return GetUser(h); + } + + void PrintStats() { + Printf("Stats: LargeMmapAllocator: allocated %zd times, " + "remains %zd (%zd K) max %zd M; by size logs: ", + stats.n_allocs, stats.n_allocs - stats.n_frees, + stats.currently_allocated >> 10, stats.max_allocated >> 20); + for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) { + uptr c = stats.by_size_log[i]; + if (!c) continue; + Printf("%zd:%zd; ", i, c); + } + Printf("\n"); + } + + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + mutex_.Lock(); + } + + void ForceUnlock() { + mutex_.Unlock(); + } + + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + for (uptr i = 0; i < n_chunks_; i++) + callback(reinterpret_cast(GetUser(chunks_[i])), arg); + } + + private: + static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18); + struct Header { + uptr map_beg; + uptr map_size; + uptr size; + uptr chunk_idx; + }; + + Header *GetHeader(uptr p) { + CHECK(IsAligned(p, page_size_)); + return reinterpret_cast(p - page_size_); + } + Header *GetHeader(const void *p) { + return GetHeader(reinterpret_cast(p)); + } + + void *GetUser(Header *h) { + CHECK(IsAligned((uptr)h, page_size_)); + return reinterpret_cast(reinterpret_cast(h) + page_size_); + } + + uptr RoundUpMapSize(uptr size) { + return RoundUpTo(size, page_size_) + page_size_; + } + + uptr page_size_; + Header *chunks_[kMaxNumChunks]; + uptr n_chunks_; + uptr min_mmap_, max_mmap_; + bool chunks_sorted_; + struct Stats { + uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64]; + } stats; + SpinMutex mutex_; +}; + +// This class implements a complete memory allocator by using two +// internal allocators: +// PrimaryAllocator is efficient, but may not allocate some sizes (alignments). +// When allocating 2^x bytes it should return 2^x aligned chunk. +// PrimaryAllocator is used via a local AllocatorCache. +// SecondaryAllocator can allocate anything, but is not efficient. +template // NOLINT +class CombinedAllocator { + public: + void Init() { + primary_.Init(); + secondary_.Init(); + stats_.Init(); + } + + void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, + bool cleared = false) { + // Returning 0 on malloc(0) may break a lot of code. + if (size == 0) + size = 1; + if (size + alignment < size) + return AllocatorReturnNull(); + if (alignment > 8) + size = RoundUpTo(size, alignment); + void *res; + if (primary_.CanAllocate(size, alignment)) + res = cache->Allocate(&primary_, primary_.ClassID(size)); + else + res = secondary_.Allocate(&stats_, size, alignment); + if (alignment > 8) + CHECK_EQ(reinterpret_cast(res) & (alignment - 1), 0); + if (cleared && res) + internal_memset(res, 0, size); + return res; + } + + void Deallocate(AllocatorCache *cache, void *p) { + if (!p) return; + if (primary_.PointerIsMine(p)) + cache->Deallocate(&primary_, primary_.GetSizeClass(p), p); + else + secondary_.Deallocate(&stats_, p); + } + + void *Reallocate(AllocatorCache *cache, void *p, uptr new_size, + uptr alignment) { + if (!p) + return Allocate(cache, new_size, alignment); + if (!new_size) { + Deallocate(cache, p); + return 0; + } + CHECK(PointerIsMine(p)); + uptr old_size = GetActuallyAllocatedSize(p); + uptr memcpy_size = Min(new_size, old_size); + void *new_p = Allocate(cache, new_size, alignment); + if (new_p) + internal_memcpy(new_p, p, memcpy_size); + Deallocate(cache, p); + return new_p; + } + + bool PointerIsMine(void *p) { + if (primary_.PointerIsMine(p)) + return true; + return secondary_.PointerIsMine(p); + } + + bool FromPrimary(void *p) { + return primary_.PointerIsMine(p); + } + + void *GetMetaData(const void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetMetaData(p); + return secondary_.GetMetaData(p); + } + + void *GetBlockBegin(const void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetBlockBegin(p); + return secondary_.GetBlockBegin(p); + } + + // This function does the same as GetBlockBegin, but is much faster. + // Must be called with the allocator locked. + void *GetBlockBeginFastLocked(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetBlockBegin(p); + return secondary_.GetBlockBeginFastLocked(p); + } + + uptr GetActuallyAllocatedSize(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetActuallyAllocatedSize(p); + return secondary_.GetActuallyAllocatedSize(p); + } + + uptr TotalMemoryUsed() { + return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed(); + } + + void TestOnlyUnmap() { primary_.TestOnlyUnmap(); } + + void InitCache(AllocatorCache *cache) { + cache->Init(&stats_); + } + + void DestroyCache(AllocatorCache *cache) { + cache->Destroy(&primary_, &stats_); + } + + void SwallowCache(AllocatorCache *cache) { + cache->Drain(&primary_); + } + + void GetStats(AllocatorStatCounters s) const { + stats_.Get(s); + } + + void PrintStats() { + primary_.PrintStats(); + secondary_.PrintStats(); + } + + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + primary_.ForceLock(); + secondary_.ForceLock(); + } + + void ForceUnlock() { + secondary_.ForceUnlock(); + primary_.ForceUnlock(); + } + + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + primary_.ForEachChunk(callback, arg); + secondary_.ForEachChunk(callback, arg); + } + + private: + PrimaryAllocator primary_; + SecondaryAllocator secondary_; + AllocatorGlobalStats stats_; +}; + +// Returns true if calloc(size, n) should return 0 due to overflow in size*n. +bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n); + +} // namespace __sanitizer + +#endif // SANITIZER_ALLOCATOR_H + diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h new file mode 100644 index 00000000..5b24bfda --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h @@ -0,0 +1,64 @@ +//===-- sanitizer_allocator_internal.h -------------------------- C++ -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This allocator is used inside run-times. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ALLOCATOR_INTERNAL_H +#define SANITIZER_ALLOCATOR_INTERNAL_H + +#include "sanitizer_allocator.h" +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// FIXME: Check if we may use even more compact size class map for internal +// purposes. +typedef CompactSizeClassMap InternalSizeClassMap; + +static const uptr kInternalAllocatorSpace = 0; +#if SANITIZER_WORDSIZE == 32 +static const u64 kInternalAllocatorSize = (1ULL << 32); +static const uptr kInternalAllocatorRegionSizeLog = 20; +#else +static const u64 kInternalAllocatorSize = (1ULL << 47); +static const uptr kInternalAllocatorRegionSizeLog = 24; +#endif +static const uptr kInternalAllocatorFlatByteMapSize = + kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; +typedef SizeClassAllocator32< + kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap, + kInternalAllocatorRegionSizeLog, + FlatByteMap > PrimaryInternalAllocator; + +typedef SizeClassAllocatorLocalCache + InternalAllocatorCache; + +// We don't want our internal allocator to do any map/unmap operations. +struct CrashOnMapUnmap { + void OnMap(uptr p, uptr size) const { + RAW_CHECK_MSG(0, "Unexpected mmap in InternalAllocator!"); + } + void OnUnmap(uptr p, uptr size) const { + RAW_CHECK_MSG(0, "Unexpected munmap in InternalAllocator!"); + } +}; + +typedef CombinedAllocator > + InternalAllocator; + +void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); +void InternalFree(void *p, InternalAllocatorCache *cache = 0); +InternalAllocator *internal_allocator(); + +} // namespace __sanitizer + +#endif // SANITIZER_ALLOCATOR_INTERNAL_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h new file mode 100644 index 00000000..61e6dfd5 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic.h @@ -0,0 +1,65 @@ +//===-- sanitizer_atomic.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ATOMIC_H +#define SANITIZER_ATOMIC_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +enum memory_order { + memory_order_relaxed = 1 << 0, + memory_order_consume = 1 << 1, + memory_order_acquire = 1 << 2, + memory_order_release = 1 << 3, + memory_order_acq_rel = 1 << 4, + memory_order_seq_cst = 1 << 5 +}; + +struct atomic_uint8_t { + typedef u8 Type; + volatile Type val_dont_use; +}; + +struct atomic_uint16_t { + typedef u16 Type; + volatile Type val_dont_use; +}; + +struct atomic_uint32_t { + typedef u32 Type; + volatile Type val_dont_use; +}; + +struct atomic_uint64_t { + typedef u64 Type; + volatile Type val_dont_use; +}; + +struct atomic_uintptr_t { + typedef uptr Type; + volatile Type val_dont_use; +}; + +} // namespace __sanitizer + +#if defined(__GNUC__) +# include "sanitizer_atomic_clang.h" +#elif defined(_MSC_VER) +# include "sanitizer_atomic_msvc.h" +#else +# error "Unsupported compiler" +#endif + +#endif // SANITIZER_ATOMIC_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic_clang.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic_clang.h new file mode 100644 index 00000000..c5aa939b --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic_clang.h @@ -0,0 +1,135 @@ +//===-- sanitizer_atomic_clang.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Not intended for direct inclusion. Include sanitizer_atomic.h. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ATOMIC_CLANG_H +#define SANITIZER_ATOMIC_CLANG_H + +namespace __sanitizer { + +INLINE void atomic_signal_fence(memory_order) { + __asm__ __volatile__("" ::: "memory"); +} + +INLINE void atomic_thread_fence(memory_order) { + __sync_synchronize(); +} + +INLINE void proc_yield(int cnt) { + __asm__ __volatile__("" ::: "memory"); +#if defined(__i386__) || defined(__x86_64__) + for (int i = 0; i < cnt; i++) + __asm__ __volatile__("pause"); +#endif + __asm__ __volatile__("" ::: "memory"); +} + +template +INLINE typename T::Type atomic_load( + const volatile T *a, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_consume + | memory_order_acquire | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + typename T::Type v; + // FIXME: + // 64-bit atomic operations are not atomic on 32-bit platforms. + // The implementation lacks necessary memory fences on ARM/PPC. + // We would like to use compiler builtin atomic operations, + // but they are mostly broken: + // - they lead to vastly inefficient code generation + // (http://llvm.org/bugs/show_bug.cgi?id=17281) + // - 64-bit atomic operations are not implemented on x86_32 + // (http://llvm.org/bugs/show_bug.cgi?id=15034) + // - they are not implemented on ARM + // error: undefined reference to '__atomic_load_4' + if (mo == memory_order_relaxed) { + v = a->val_dont_use; + } else { + atomic_signal_fence(memory_order_seq_cst); + v = a->val_dont_use; + atomic_signal_fence(memory_order_seq_cst); + } + return v; +} + +template +INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_release + | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + if (mo == memory_order_relaxed) { + a->val_dont_use = v; + } else { + atomic_signal_fence(memory_order_seq_cst); + a->val_dont_use = v; + atomic_signal_fence(memory_order_seq_cst); + } + if (mo == memory_order_seq_cst) + atomic_thread_fence(memory_order_seq_cst); +} + +template +INLINE typename T::Type atomic_fetch_add(volatile T *a, + typename T::Type v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return __sync_fetch_and_add(&a->val_dont_use, v); +} + +template +INLINE typename T::Type atomic_fetch_sub(volatile T *a, + typename T::Type v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return __sync_fetch_and_add(&a->val_dont_use, -v); +} + +template +INLINE typename T::Type atomic_exchange(volatile T *a, + typename T::Type v, memory_order mo) { + DCHECK(!((uptr)a % sizeof(*a))); + if (mo & (memory_order_release | memory_order_acq_rel | memory_order_seq_cst)) + __sync_synchronize(); + v = __sync_lock_test_and_set(&a->val_dont_use, v); + if (mo == memory_order_seq_cst) + __sync_synchronize(); + return v; +} + +template +INLINE bool atomic_compare_exchange_strong(volatile T *a, + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { + typedef typename T::Type Type; + Type cmpv = *cmp; + Type prev = __sync_val_compare_and_swap(&a->val_dont_use, cmpv, xchg); + if (prev == cmpv) + return true; + *cmp = prev; + return false; +} + +template +INLINE bool atomic_compare_exchange_weak(volatile T *a, + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { + return atomic_compare_exchange_strong(a, cmp, xchg, mo); +} + +} // namespace __sanitizer + +#undef ATOMIC_ORDER + +#endif // SANITIZER_ATOMIC_CLANG_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic_msvc.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic_msvc.h new file mode 100644 index 00000000..dc22ef05 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_atomic_msvc.h @@ -0,0 +1,181 @@ +//===-- sanitizer_atomic_msvc.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Not intended for direct inclusion. Include sanitizer_atomic.h. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ATOMIC_MSVC_H +#define SANITIZER_ATOMIC_MSVC_H + +extern "C" void _ReadWriteBarrier(); +#pragma intrinsic(_ReadWriteBarrier) +extern "C" void _mm_mfence(); +#pragma intrinsic(_mm_mfence) +extern "C" void _mm_pause(); +#pragma intrinsic(_mm_pause) +extern "C" long _InterlockedExchangeAdd( // NOLINT + long volatile * Addend, long Value); // NOLINT +#pragma intrinsic(_InterlockedExchangeAdd) + +#ifdef _WIN64 +extern "C" void *_InterlockedCompareExchangePointer( + void *volatile *Destination, + void *Exchange, void *Comparand); +#pragma intrinsic(_InterlockedCompareExchangePointer) +#else +// There's no _InterlockedCompareExchangePointer intrinsic on x86, +// so call _InterlockedCompareExchange instead. +extern "C" +long __cdecl _InterlockedCompareExchange( // NOLINT + long volatile *Destination, // NOLINT + long Exchange, long Comparand); // NOLINT +#pragma intrinsic(_InterlockedCompareExchange) + +inline static void *_InterlockedCompareExchangePointer( + void *volatile *Destination, + void *Exchange, void *Comparand) { + return reinterpret_cast( + _InterlockedCompareExchange( + reinterpret_cast(Destination), // NOLINT + reinterpret_cast(Exchange), // NOLINT + reinterpret_cast(Comparand))); // NOLINT +} +#endif + +namespace __sanitizer { + +INLINE void atomic_signal_fence(memory_order) { + _ReadWriteBarrier(); +} + +INLINE void atomic_thread_fence(memory_order) { + _mm_mfence(); +} + +INLINE void proc_yield(int cnt) { + for (int i = 0; i < cnt; i++) + _mm_pause(); +} + +template +INLINE typename T::Type atomic_load( + const volatile T *a, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_consume + | memory_order_acquire | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + typename T::Type v; + // FIXME(dvyukov): 64-bit load is not atomic on 32-bits. + if (mo == memory_order_relaxed) { + v = a->val_dont_use; + } else { + atomic_signal_fence(memory_order_seq_cst); + v = a->val_dont_use; + atomic_signal_fence(memory_order_seq_cst); + } + return v; +} + +template +INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) { + DCHECK(mo & (memory_order_relaxed | memory_order_release + | memory_order_seq_cst)); + DCHECK(!((uptr)a % sizeof(*a))); + // FIXME(dvyukov): 64-bit store is not atomic on 32-bits. + if (mo == memory_order_relaxed) { + a->val_dont_use = v; + } else { + atomic_signal_fence(memory_order_seq_cst); + a->val_dont_use = v; + atomic_signal_fence(memory_order_seq_cst); + } + if (mo == memory_order_seq_cst) + atomic_thread_fence(memory_order_seq_cst); +} + +INLINE u32 atomic_fetch_add(volatile atomic_uint32_t *a, + u32 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return (u32)_InterlockedExchangeAdd( + (volatile long*)&a->val_dont_use, (long)v); // NOLINT +} + +INLINE u8 atomic_exchange(volatile atomic_uint8_t *a, + u8 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + __asm { + mov eax, a + mov cl, v + xchg [eax], cl // NOLINT + mov v, cl + } + return v; +} + +INLINE u16 atomic_exchange(volatile atomic_uint16_t *a, + u16 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + __asm { + mov eax, a + mov cx, v + xchg [eax], cx // NOLINT + mov v, cx + } + return v; +} + +INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a, + u8 *cmp, + u8 xchgv, + memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + u8 cmpv = *cmp; + u8 prev; + __asm { + mov al, cmpv + mov ecx, a + mov dl, xchgv + lock cmpxchg [ecx], dl + mov prev, al + } + if (prev == cmpv) + return true; + *cmp = prev; + return false; +} + +INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a, + uptr *cmp, + uptr xchg, + memory_order mo) { + uptr cmpv = *cmp; + uptr prev = (uptr)_InterlockedCompareExchangePointer( + (void*volatile*)&a->val_dont_use, (void*)xchg, (void*)cmpv); + if (prev == cmpv) + return true; + *cmp = prev; + return false; +} + +template +INLINE bool atomic_compare_exchange_weak(volatile T *a, + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { + return atomic_compare_exchange_strong(a, cmp, xchg, mo); +} + +} // namespace __sanitizer + +#endif // SANITIZER_ATOMIC_CLANG_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_common.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common.cc new file mode 100644 index 00000000..7e870ff6 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common.cc @@ -0,0 +1,280 @@ +//===-- sanitizer_common.cc -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_libc.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +const char *SanitizerToolName = "SanitizerTool"; + +uptr GetPageSizeCached() { + static uptr PageSize; + if (!PageSize) + PageSize = GetPageSize(); + return PageSize; +} + + +// By default, dump to stderr. If |log_to_file| is true and |report_fd_pid| +// isn't equal to the current PID, try to obtain file descriptor by opening +// file "report_path_prefix.". +fd_t report_fd = kStderrFd; + +// Set via __sanitizer_set_report_path. +bool log_to_file = false; +char report_path_prefix[sizeof(report_path_prefix)]; + +// PID of process that opened |report_fd|. If a fork() occurs, the PID of the +// child thread will be different from |report_fd_pid|. +uptr report_fd_pid = 0; + +static DieCallbackType DieCallback; +void SetDieCallback(DieCallbackType callback) { + DieCallback = callback; +} + +DieCallbackType GetDieCallback() { + return DieCallback; +} + +void NORETURN Die() { + if (DieCallback) { + DieCallback(); + } + internal__exit(1); +} + +static CheckFailedCallbackType CheckFailedCallback; +void SetCheckFailedCallback(CheckFailedCallbackType callback) { + CheckFailedCallback = callback; +} + +void NORETURN CheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + if (CheckFailedCallback) { + CheckFailedCallback(file, line, cond, v1, v2); + } + Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond, + v1, v2); + Die(); +} + +uptr ReadFileToBuffer(const char *file_name, char **buff, + uptr *buff_size, uptr max_len) { + uptr PageSize = GetPageSizeCached(); + uptr kMinFileLen = PageSize; + uptr read_len = 0; + *buff = 0; + *buff_size = 0; + // The files we usually open are not seekable, so try different buffer sizes. + for (uptr size = kMinFileLen; size <= max_len; size *= 2) { + uptr openrv = OpenFile(file_name, /*write*/ false); + if (internal_iserror(openrv)) return 0; + fd_t fd = openrv; + UnmapOrDie(*buff, *buff_size); + *buff = (char*)MmapOrDie(size, __FUNCTION__); + *buff_size = size; + // Read up to one page at a time. + read_len = 0; + bool reached_eof = false; + while (read_len + PageSize <= size) { + uptr just_read = internal_read(fd, *buff + read_len, PageSize); + if (just_read == 0) { + reached_eof = true; + break; + } + read_len += just_read; + } + internal_close(fd); + if (reached_eof) // We've read the whole file. + break; + } + return read_len; +} + +typedef bool UptrComparisonFunction(const uptr &a, const uptr &b); + +template +static inline bool CompareLess(const T &a, const T &b) { + return a < b; +} + +void SortArray(uptr *array, uptr size) { + InternalSort(&array, size, CompareLess); +} + +// We want to map a chunk of address space aligned to 'alignment'. +// We do it by maping a bit more and then unmaping redundant pieces. +// We probably can do it with fewer syscalls in some OS-dependent way. +void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { +// uptr PageSize = GetPageSizeCached(); + CHECK(IsPowerOfTwo(size)); + CHECK(IsPowerOfTwo(alignment)); + uptr map_size = size + alignment; + uptr map_res = (uptr)MmapOrDie(map_size, mem_type); + uptr map_end = map_res + map_size; + uptr res = map_res; + if (res & (alignment - 1)) // Not aligned. + res = (map_res + alignment) & ~(alignment - 1); + uptr end = res + size; + if (res != map_res) + UnmapOrDie((void*)map_res, res - map_res); + if (end != map_end) + UnmapOrDie((void*)end, map_end - end); + return (void*)res; +} + +const char *StripPathPrefix(const char *filepath, + const char *strip_path_prefix) { + if (filepath == 0) return 0; + if (strip_path_prefix == 0) return filepath; + const char *pos = internal_strstr(filepath, strip_path_prefix); + if (pos == 0) return filepath; + pos += internal_strlen(strip_path_prefix); + if (pos[0] == '.' && pos[1] == '/') + pos += 2; + return pos; +} + +void PrintSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column) { + CHECK(file); + buffer->append("%s", + StripPathPrefix(file, common_flags()->strip_path_prefix)); + if (line > 0) { + buffer->append(":%d", line); + if (column > 0) + buffer->append(":%d", column); + } +} + +void PrintModuleAndOffset(InternalScopedString *buffer, const char *module, + uptr offset) { + buffer->append("(%s+0x%zx)", + StripPathPrefix(module, common_flags()->strip_path_prefix), + offset); +} + +void ReportErrorSummary(const char *error_message) { + if (!common_flags()->print_summary) + return; + InternalScopedBuffer buff(kMaxSummaryLength); + internal_snprintf(buff.data(), buff.size(), + "SUMMARY: %s: %s", SanitizerToolName, error_message); + __sanitizer_report_error_summary(buff.data()); +} + +void ReportErrorSummary(const char *error_type, const char *file, + int line, const char *function) { + if (!common_flags()->print_summary) + return; + InternalScopedBuffer buff(kMaxSummaryLength); + internal_snprintf( + buff.data(), buff.size(), "%s %s:%d %s", error_type, + file ? StripPathPrefix(file, common_flags()->strip_path_prefix) : "??", + line, function ? function : "??"); + ReportErrorSummary(buff.data()); +} + +void ReportErrorSummary(const char *error_type, StackTrace *stack) { + if (!common_flags()->print_summary) + return; + AddressInfo ai; +#if !SANITIZER_GO + if (stack->size > 0 && Symbolizer::Get()->IsAvailable()) { + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + Symbolizer::Get()->SymbolizeCode(pc, &ai, 1); + } +#endif + ReportErrorSummary(error_type, ai.file, ai.line, ai.function); +} + +LoadedModule::LoadedModule(const char *module_name, uptr base_address) { + full_name_ = internal_strdup(module_name); + base_address_ = base_address; + n_ranges_ = 0; +} + +void LoadedModule::addAddressRange(uptr beg, uptr end) { + CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); + ranges_[n_ranges_].beg = beg; + ranges_[n_ranges_].end = end; + n_ranges_++; +} + +bool LoadedModule::containsAddress(uptr address) const { + for (uptr i = 0; i < n_ranges_; i++) { + if (ranges_[i].beg <= address && address < ranges_[i].end) + return true; + } + return false; +} + +char *StripModuleName(const char *module) { + if (module == 0) + return 0; + const char *short_module_name = internal_strrchr(module, '/'); + if (short_module_name) + short_module_name += 1; + else + short_module_name = module; + return internal_strdup(short_module_name); +} + +} // namespace __sanitizer + +using namespace __sanitizer; // NOLINT + +extern "C" { +void __sanitizer_set_report_path(const char *path) { + if (!path) + return; + uptr len = internal_strlen(path); + if (len > sizeof(report_path_prefix) - 100) { + Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n", + path[0], path[1], path[2], path[3], + path[4], path[5], path[6], path[7]); + Die(); + } + if (report_fd != kStdoutFd && + report_fd != kStderrFd && + report_fd != kInvalidFd) + internal_close(report_fd); + report_fd = kInvalidFd; + log_to_file = false; + if (internal_strcmp(path, "stdout") == 0) { + report_fd = kStdoutFd; + } else if (internal_strcmp(path, "stderr") == 0) { + report_fd = kStderrFd; + } else { + internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix)); + report_path_prefix[len] = '\0'; + log_to_file = true; + } +} + +void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { + (void)reserved; + PrepareForSandboxing(); +} + +void __sanitizer_report_error_summary(const char *error_summary) { + Printf("%s\n", error_summary); +} +} // extern "C" diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common.h new file mode 100644 index 00000000..cf8a12d6 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -0,0 +1,488 @@ +//===-- sanitizer_common.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// It declares common functions and classes that are used in both runtimes. +// Implementation of some functions are provided in sanitizer_common, while +// others must be defined by run-time library itself. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_COMMON_H +#define SANITIZER_COMMON_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { +struct StackTrace; + +// Constants. +const uptr kWordSize = SANITIZER_WORDSIZE / 8; +const uptr kWordSizeInBits = 8 * kWordSize; + +#if defined(__powerpc__) || defined(__powerpc64__) +const uptr kCacheLineSize = 128; +#else +const uptr kCacheLineSize = 64; +#endif + +const uptr kMaxPathLength = 512; + +extern const char *SanitizerToolName; // Can be changed by the tool. + +uptr GetPageSize(); +uptr GetPageSizeCached(); +uptr GetMmapGranularity(); +uptr GetMaxVirtualAddress(); +// Threads +uptr GetTid(); +uptr GetThreadSelf(); +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom); +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size); + +// Memory management +void *MmapOrDie(uptr size, const char *mem_type); +void UnmapOrDie(void *addr, uptr size); +void *MmapFixedNoReserve(uptr fixed_addr, uptr size); +void *MmapFixedOrDie(uptr fixed_addr, uptr size); +void *Mprotect(uptr fixed_addr, uptr size); +// Map aligned chunk of address space; size and alignment are powers of two. +void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type); +// Used to check if we can map shadow memory to a fixed location. +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); +void FlushUnneededShadowMemory(uptr addr, uptr size); + +// InternalScopedBuffer can be used instead of large stack arrays to +// keep frame size low. +// FIXME: use InternalAlloc instead of MmapOrDie once +// InternalAlloc is made libc-free. +template +class InternalScopedBuffer { + public: + explicit InternalScopedBuffer(uptr cnt) { + cnt_ = cnt; + ptr_ = (T*)MmapOrDie(cnt * sizeof(T), "InternalScopedBuffer"); + } + ~InternalScopedBuffer() { + UnmapOrDie(ptr_, cnt_ * sizeof(T)); + } + T &operator[](uptr i) { return ptr_[i]; } + T *data() { return ptr_; } + uptr size() { return cnt_ * sizeof(T); } + + private: + T *ptr_; + uptr cnt_; + // Disallow evil constructors. + InternalScopedBuffer(const InternalScopedBuffer&); + void operator=(const InternalScopedBuffer&); +}; + +class InternalScopedString : public InternalScopedBuffer { + public: + explicit InternalScopedString(uptr max_length) + : InternalScopedBuffer(max_length), length_(0) { + (*this)[0] = '\0'; + } + uptr length() { return length_; } + void clear() { + (*this)[0] = '\0'; + length_ = 0; + } + void append(const char *format, ...); + + private: + uptr length_; +}; + +// Simple low-level (mmap-based) allocator for internal use. Doesn't have +// constructor, so all instances of LowLevelAllocator should be +// linker initialized. +class LowLevelAllocator { + public: + // Requires an external lock. + void *Allocate(uptr size); + private: + char *allocated_end_; + char *allocated_current_; +}; +typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size); +// Allows to register tool-specific callbacks for LowLevelAllocator. +// Passing NULL removes the callback. +void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); + +// IO +void RawWrite(const char *buffer); +bool PrintsToTty(); +// Caching version of PrintsToTty(). Not thread-safe. +bool PrintsToTtyCached(); +void Printf(const char *format, ...); +void Report(const char *format, ...); +void SetPrintfAndReportCallback(void (*callback)(const char *)); + +// Can be used to prevent mixing error reports from different sanitizers. +extern StaticSpinMutex CommonSanitizerReportMutex; +void MaybeOpenReportFile(); +extern fd_t report_fd; +extern bool log_to_file; +extern char report_path_prefix[4096]; +extern uptr report_fd_pid; + +uptr OpenFile(const char *filename, bool write); +// Opens the file 'file_name" and reads up to 'max_len' bytes. +// The resulting buffer is mmaped and stored in '*buff'. +// The size of the mmaped region is stored in '*buff_size', +// Returns the number of read bytes or 0 if file can not be opened. +uptr ReadFileToBuffer(const char *file_name, char **buff, + uptr *buff_size, uptr max_len); +// Maps given file to virtual memory, and returns pointer to it +// (or NULL if the mapping failes). Stores the size of mmaped region +// in '*buff_size'. +void *MapFileToMemory(const char *file_name, uptr *buff_size); + +// Error report formatting. +const char *StripPathPrefix(const char *filepath, + const char *strip_file_prefix); +void PrintSourceLocation(InternalScopedString *buffer, const char *file, + int line, int column); +void PrintModuleAndOffset(InternalScopedString *buffer, + const char *module, uptr offset); + +// OS +void DisableCoreDumper(); +void DumpProcessMap(); +bool FileExists(const char *filename); +const char *GetEnv(const char *name); +bool SetEnv(const char *name, const char *value); +const char *GetPwd(); +char *FindPathToBinary(const char *name); +u32 GetUid(); +void ReExec(); +bool StackSizeIsUnlimited(); +void SetStackSizeLimitInBytes(uptr limit); +void PrepareForSandboxing(); + +void InitTlsSize(); +uptr GetTlsSize(); + +// Other +void SleepForSeconds(int seconds); +void SleepForMillis(int millis); +u64 NanoTime(); +int Atexit(void (*function)(void)); +void SortArray(uptr *array, uptr size); +// Strip the directories from the module name, return a new string allocated +// with internal_strdup. +char *StripModuleName(const char *module); + +// Exit +void NORETURN Abort(); +void NORETURN Die(); +void NORETURN +CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); + +// Set the name of the current thread to 'name', return true on succees. +// The name may be truncated to a system-dependent limit. +bool SanitizerSetThreadName(const char *name); +// Get the name of the current thread (no more than max_len bytes), +// return true on succees. name should have space for at least max_len+1 bytes. +bool SanitizerGetThreadName(char *name, int max_len); + +// Specific tools may override behavior of "Die" and "CheckFailed" functions +// to do tool-specific job. +typedef void (*DieCallbackType)(void); +void SetDieCallback(DieCallbackType); +DieCallbackType GetDieCallback(); +typedef void (*CheckFailedCallbackType)(const char *, int, const char *, + u64, u64); +void SetCheckFailedCallback(CheckFailedCallbackType callback); + +// We don't want a summary too long. +const int kMaxSummaryLength = 1024; +// Construct a one-line string: +// SUMMARY: SanitizerToolName: error_message +// and pass it to __sanitizer_report_error_summary. +void ReportErrorSummary(const char *error_message); +// Same as above, but construct error_message as: +// error_type: file:line function +void ReportErrorSummary(const char *error_type, const char *file, + int line, const char *function); +void ReportErrorSummary(const char *error_type, StackTrace *trace); + +// Math +#if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__) +extern "C" { +unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT +unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT +#if defined(_WIN64) +unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT +unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT +#endif +} +#endif + +INLINE uptr MostSignificantSetBitIndex(uptr x) { + CHECK_NE(x, 0U); + unsigned long up; // NOLINT +#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) + up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); +#elif defined(_WIN64) + _BitScanReverse64(&up, x); +#else + _BitScanReverse(&up, x); +#endif + return up; +} + +INLINE bool IsPowerOfTwo(uptr x) { + return (x & (x - 1)) == 0; +} + +INLINE uptr RoundUpToPowerOfTwo(uptr size) { + CHECK(size); + if (IsPowerOfTwo(size)) return size; + + uptr up = MostSignificantSetBitIndex(size); + CHECK(size < (1ULL << (up + 1))); + CHECK(size > (1ULL << up)); + return 1UL << (up + 1); +} + +INLINE uptr RoundUpTo(uptr size, uptr boundary) { + CHECK(IsPowerOfTwo(boundary)); + return (size + boundary - 1) & ~(boundary - 1); +} + +INLINE uptr RoundDownTo(uptr x, uptr boundary) { + return x & ~(boundary - 1); +} + +INLINE bool IsAligned(uptr a, uptr alignment) { + return (a & (alignment - 1)) == 0; +} + +INLINE uptr Log2(uptr x) { + CHECK(IsPowerOfTwo(x)); +#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) + return __builtin_ctzl(x); +#elif defined(_WIN64) + unsigned long ret; // NOLINT + _BitScanForward64(&ret, x); + return ret; +#else + unsigned long ret; // NOLINT + _BitScanForward(&ret, x); + return ret; +#endif +} + +// Don't use std::min, std::max or std::swap, to minimize dependency +// on libstdc++. +template T Min(T a, T b) { return a < b ? a : b; } +template T Max(T a, T b) { return a > b ? a : b; } +template void Swap(T& a, T& b) { + T tmp = a; + a = b; + b = tmp; +} + +// Char handling +INLINE bool IsSpace(int c) { + return (c == ' ') || (c == '\n') || (c == '\t') || + (c == '\f') || (c == '\r') || (c == '\v'); +} +INLINE bool IsDigit(int c) { + return (c >= '0') && (c <= '9'); +} +INLINE int ToLower(int c) { + return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c; +} + +#if SANITIZER_WORDSIZE == 64 +# define FIRST_32_SECOND_64(a, b) (b) +#else +# define FIRST_32_SECOND_64(a, b) (a) +#endif + +// A low-level vector based on mmap. May incur a significant memory overhead for +// small vectors. +// WARNING: The current implementation supports only POD types. +template +class InternalMmapVector { + public: + explicit InternalMmapVector(uptr initial_capacity) { + CHECK_GT(initial_capacity, 0); + capacity_ = initial_capacity; + size_ = 0; + data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVector"); + } + ~InternalMmapVector() { + UnmapOrDie(data_, capacity_ * sizeof(T)); + } + T &operator[](uptr i) { + CHECK_LT(i, size_); + return data_[i]; + } + const T &operator[](uptr i) const { + CHECK_LT(i, size_); + return data_[i]; + } + void push_back(const T &element) { + CHECK_LE(size_, capacity_); + if (size_ == capacity_) { + uptr new_capacity = RoundUpToPowerOfTwo(size_ + 1); + Resize(new_capacity); + } + data_[size_++] = element; + } + T &back() { + CHECK_GT(size_, 0); + return data_[size_ - 1]; + } + void pop_back() { + CHECK_GT(size_, 0); + size_--; + } + uptr size() const { + return size_; + } + const T *data() const { + return data_; + } + uptr capacity() const { + return capacity_; + } + + void clear() { size_ = 0; } + + private: + void Resize(uptr new_capacity) { + CHECK_GT(new_capacity, 0); + CHECK_LE(size_, new_capacity); + T *new_data = (T *)MmapOrDie(new_capacity * sizeof(T), + "InternalMmapVector"); + internal_memcpy(new_data, data_, size_ * sizeof(T)); + T *old_data = data_; + data_ = new_data; + UnmapOrDie(old_data, capacity_ * sizeof(T)); + capacity_ = new_capacity; + } + // Disallow evil constructors. + InternalMmapVector(const InternalMmapVector&); + void operator=(const InternalMmapVector&); + + T *data_; + uptr capacity_; + uptr size_; +}; + +// HeapSort for arrays and InternalMmapVector. +template +void InternalSort(Container *v, uptr size, Compare comp) { + if (size < 2) + return; + // Stage 1: insert elements to the heap. + for (uptr i = 1; i < size; i++) { + uptr j, p; + for (j = i; j > 0; j = p) { + p = (j - 1) / 2; + if (comp((*v)[p], (*v)[j])) + Swap((*v)[j], (*v)[p]); + else + break; + } + } + // Stage 2: swap largest element with the last one, + // and sink the new top. + for (uptr i = size - 1; i > 0; i--) { + Swap((*v)[0], (*v)[i]); + uptr j, max_ind; + for (j = 0; j < i; j = max_ind) { + uptr left = 2 * j + 1; + uptr right = 2 * j + 2; + max_ind = j; + if (left < i && comp((*v)[max_ind], (*v)[left])) + max_ind = left; + if (right < i && comp((*v)[max_ind], (*v)[right])) + max_ind = right; + if (max_ind != j) + Swap((*v)[j], (*v)[max_ind]); + else + break; + } + } +} + +template +uptr InternalBinarySearch(const Container &v, uptr first, uptr last, + const Value &val, Compare comp) { + uptr not_found = last + 1; + while (last >= first) { + uptr mid = (first + last) / 2; + if (comp(v[mid], val)) + first = mid + 1; + else if (comp(val, v[mid])) + last = mid - 1; + else + return mid; + } + return not_found; +} + +// Represents a binary loaded into virtual memory (e.g. this can be an +// executable or a shared object). +class LoadedModule { + public: + LoadedModule(const char *module_name, uptr base_address); + void addAddressRange(uptr beg, uptr end); + bool containsAddress(uptr address) const; + + const char *full_name() const { return full_name_; } + uptr base_address() const { return base_address_; } + + private: + struct AddressRange { + uptr beg; + uptr end; + }; + char *full_name_; + uptr base_address_; + static const uptr kMaxNumberOfAddressRanges = 6; + AddressRange ranges_[kMaxNumberOfAddressRanges]; + uptr n_ranges_; +}; + +// OS-dependent function that fills array with descriptions of at most +// "max_modules" currently loaded modules. Returns the number of +// initialized modules. If filter is nonzero, ignores modules for which +// filter(full_name) is false. +typedef bool (*string_predicate_t)(const char *); +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter); + +#if SANITIZER_POSIX +const uptr kPthreadDestructorIterations = 4; +#else +// Unused on Windows. +const uptr kPthreadDestructorIterations = 0; +#endif + +// Callback type for iterating over a set of memory ranges. +typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg); +} // namespace __sanitizer + +inline void *operator new(__sanitizer::operator_new_size_type size, + __sanitizer::LowLevelAllocator &alloc) { + return alloc.Allocate(size); +} + +#endif // SANITIZER_COMMON_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc new file mode 100644 index 00000000..d1c89767 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -0,0 +1,2935 @@ +//===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Common function interceptors for tools like AddressSanitizer, +// ThreadSanitizer, MemorySanitizer, etc. +// +// This file should be included into the tool's interceptor file, +// which has to define it's own macros: +// COMMON_INTERCEPTOR_ENTER +// COMMON_INTERCEPTOR_READ_RANGE +// COMMON_INTERCEPTOR_WRITE_RANGE +// COMMON_INTERCEPTOR_INITIALIZE_RANGE +// COMMON_INTERCEPTOR_FD_ACQUIRE +// COMMON_INTERCEPTOR_FD_RELEASE +// COMMON_INTERCEPTOR_FD_ACCESS +// COMMON_INTERCEPTOR_SET_THREAD_NAME +// COMMON_INTERCEPTOR_ON_EXIT +// COMMON_INTERCEPTOR_MUTEX_LOCK +// COMMON_INTERCEPTOR_MUTEX_UNLOCK +// COMMON_INTERCEPTOR_MUTEX_REPAIR +// COMMON_INTERCEPTOR_SET_PTHREAD_NAME +//===----------------------------------------------------------------------===// +#include "interception/interception.h" +#include "sanitizer_platform_interceptors.h" + +#include + +#if SANITIZER_WINDOWS +#define va_copy(dst, src) ((dst) = (src)) +#endif // _WIN32 + +#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, p, size) {} +#endif + +#ifndef COMMON_INTERCEPTOR_FD_ACCESS +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_LOCK +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {} +#endif + +#if SANITIZER_INTERCEPT_STRCMP +static inline int CharCmpX(unsigned char c1, unsigned char c2) { + return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; +} + +INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); + unsigned char c1, c2; + uptr i; + for (i = 0;; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (c1 != c2 || c1 == '\0') break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); + return CharCmpX(c1, c2); +} + +INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; i < size; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (c1 != c2 || c1 == '\0') break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); + return CharCmpX(c1, c2); +} + +#define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp) +#define INIT_STRNCMP COMMON_INTERCEPT_FUNCTION(strncmp) +#else +#define INIT_STRCMP +#define INIT_STRNCMP +#endif + +#if SANITIZER_INTERCEPT_STRCASECMP +static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { + int c1_low = ToLower(c1); + int c2_low = ToLower(c2); + return c1_low - c2_low; +} + +INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0;; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); + return CharCaseCmp(c1, c2); +} + +INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strncasecmp, s1, s2, n); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; i < n; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, n)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, n)); + return CharCaseCmp(c1, c2); +} + +#define INIT_STRCASECMP COMMON_INTERCEPT_FUNCTION(strcasecmp) +#define INIT_STRNCASECMP COMMON_INTERCEPT_FUNCTION(strncasecmp) +#else +#define INIT_STRCASECMP +#define INIT_STRNCASECMP +#endif + +#if SANITIZER_INTERCEPT_FREXP +INTERCEPTOR(double, frexp, double x, int *exp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, frexp, x, exp); + double res = REAL(frexp)(x, exp); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); + return res; +} + +#define INIT_FREXP COMMON_INTERCEPT_FUNCTION(frexp); +#else +#define INIT_FREXP +#endif // SANITIZER_INTERCEPT_FREXP + +#if SANITIZER_INTERCEPT_FREXPF_FREXPL +INTERCEPTOR(float, frexpf, float x, int *exp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp); + float res = REAL(frexpf)(x, exp); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); + return res; +} + +INTERCEPTOR(long double, frexpl, long double x, int *exp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp); + long double res = REAL(frexpl)(x, exp); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); + return res; +} + +#define INIT_FREXPF_FREXPL \ + COMMON_INTERCEPT_FUNCTION(frexpf); \ + COMMON_INTERCEPT_FUNCTION(frexpl) +#else +#define INIT_FREXPF_FREXPL +#endif // SANITIZER_INTERCEPT_FREXPF_FREXPL + +#if SI_NOT_WINDOWS +static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec) * iovlen); + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec[i].iov_base, sz); + maxlen -= sz; + } +} +#endif + +#if SANITIZER_INTERCEPT_READ +INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(read)(fd, ptr, count); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_READ COMMON_INTERCEPT_FUNCTION(read) +#else +#define INIT_READ +#endif + +#if SANITIZER_INTERCEPT_PREAD +INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(pread)(fd, ptr, count, offset); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREAD COMMON_INTERCEPT_FUNCTION(pread) +#else +#define INIT_PREAD +#endif + +#if SANITIZER_INTERCEPT_PREAD64 +INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREAD64 COMMON_INTERCEPT_FUNCTION(pread64) +#else +#define INIT_PREAD64 +#endif + +#if SANITIZER_INTERCEPT_READV +INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, + int iovcnt) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(readv)(fd, iov, iovcnt); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_READV COMMON_INTERCEPT_FUNCTION(readv) +#else +#define INIT_READV +#endif + +#if SANITIZER_INTERCEPT_PREADV +INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREADV COMMON_INTERCEPT_FUNCTION(preadv) +#else +#define INIT_PREADV +#endif + +#if SANITIZER_INTERCEPT_PREADV64 +INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREADV64 COMMON_INTERCEPT_FUNCTION(preadv64) +#else +#define INIT_PREADV64 +#endif + +#if SANITIZER_INTERCEPT_WRITE +INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(write)(fd, ptr, count); + // FIXME: this check should be _before_ the call to REAL(write), not after + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + return res; +} +#define INIT_WRITE COMMON_INTERCEPT_FUNCTION(write) +#else +#define INIT_WRITE +#endif + +#if SANITIZER_INTERCEPT_PWRITE +INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + return res; +} +#define INIT_PWRITE COMMON_INTERCEPT_FUNCTION(pwrite) +#else +#define INIT_PWRITE +#endif + +#if SANITIZER_INTERCEPT_PWRITE64 +INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + return res; +} +#define INIT_PWRITE64 COMMON_INTERCEPT_FUNCTION(pwrite64) +#else +#define INIT_PWRITE64 +#endif + +#if SANITIZER_INTERCEPT_WRITEV +INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, + int iovcnt) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(writev)(fd, iov, iovcnt); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_WRITEV COMMON_INTERCEPT_FUNCTION(writev) +#else +#define INIT_WRITEV +#endif + +#if SANITIZER_INTERCEPT_PWRITEV +INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_PWRITEV COMMON_INTERCEPT_FUNCTION(pwritev) +#else +#define INIT_PWRITEV +#endif + +#if SANITIZER_INTERCEPT_PWRITEV64 +INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_PWRITEV64 COMMON_INTERCEPT_FUNCTION(pwritev64) +#else +#define INIT_PWRITEV64 +#endif + +#if SANITIZER_INTERCEPT_PRCTL +INTERCEPTOR(int, prctl, int option, unsigned long arg2, + unsigned long arg3, // NOLINT + unsigned long arg4, unsigned long arg5) { // NOLINT + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); + static const int PR_SET_NAME = 15; + int res = REAL(prctl(option, arg2, arg3, arg4, arg5)); + if (option == PR_SET_NAME) { + char buff[16]; + internal_strncpy(buff, (char *)arg2, 15); + buff[15] = 0; + COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff); + } + return res; +} +#define INIT_PRCTL COMMON_INTERCEPT_FUNCTION(prctl) +#else +#define INIT_PRCTL +#endif // SANITIZER_INTERCEPT_PRCTL + +#if SANITIZER_INTERCEPT_TIME +INTERCEPTOR(unsigned long, time, unsigned long *t) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, time, t); + unsigned long res = REAL(time)(t); + if (t && res != (unsigned long)-1) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, t, sizeof(*t)); + } + return res; +} +#define INIT_TIME COMMON_INTERCEPT_FUNCTION(time); +#else +#define INIT_TIME +#endif // SANITIZER_INTERCEPT_TIME + +#if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS +static void unpoison_tm(void *ctx, __sanitizer_tm *tm) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + if (tm->tm_zone) { + // Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone + // can point to shared memory and tsan would report a data race. + COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, tm->tm_zone, + REAL(strlen(tm->tm_zone)) + 1); + } +} +INTERCEPTOR(__sanitizer_tm *, localtime, unsigned long *timep) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, localtime, timep); + __sanitizer_tm *res = REAL(localtime)(timep); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); + unpoison_tm(ctx, res); + } + return res; +} +INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result); + __sanitizer_tm *res = REAL(localtime_r)(timep, result); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); + unpoison_tm(ctx, res); + } + return res; +} +INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep); + __sanitizer_tm *res = REAL(gmtime)(timep); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); + unpoison_tm(ctx, res); + } + return res; +} +INTERCEPTOR(__sanitizer_tm *, gmtime_r, unsigned long *timep, void *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gmtime_r, timep, result); + __sanitizer_tm *res = REAL(gmtime_r)(timep, result); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); + unpoison_tm(ctx, res); + } + return res; +} +INTERCEPTOR(char *, ctime, unsigned long *timep) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep); + char *res = REAL(ctime)(timep); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result); + char *res = REAL(ctime_r)(timep, result); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm); + char *res = REAL(asctime)(tm); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result); + char *res = REAL(asctime_r)(tm, result); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_LOCALTIME_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(localtime); \ + COMMON_INTERCEPT_FUNCTION(localtime_r); \ + COMMON_INTERCEPT_FUNCTION(gmtime); \ + COMMON_INTERCEPT_FUNCTION(gmtime_r); \ + COMMON_INTERCEPT_FUNCTION(ctime); \ + COMMON_INTERCEPT_FUNCTION(ctime_r); \ + COMMON_INTERCEPT_FUNCTION(asctime); \ + COMMON_INTERCEPT_FUNCTION(asctime_r); +#else +#define INIT_LOCALTIME_AND_FRIENDS +#endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS + +#if SANITIZER_INTERCEPT_STRPTIME +INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm); + if (format) + COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1); + char *res = REAL(strptime)(s, format, tm); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s); + // Do not call unpoison_tm here, because strptime does not, in fact, + // initialize the entire struct tm. For example, tm_zone pointer is left + // uninitialized. + if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + } + return res; +} +#define INIT_STRPTIME COMMON_INTERCEPT_FUNCTION(strptime); +#else +#define INIT_STRPTIME +#endif + +#if SANITIZER_INTERCEPT_SCANF + +#include "sanitizer_common_interceptors_scanf.inc" + +#define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \ + { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ + va_list aq; \ + va_copy(aq, ap); \ + int res = REAL(vname)(__VA_ARGS__); \ + if (res > 0) \ + scanf_common(ctx, res, allowGnuMalloc, format, aq); \ + va_end(aq); \ + return res; \ + } + +INTERCEPTOR(int, vscanf, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vscanf, true, format, ap) + +INTERCEPTOR(int, vsscanf, const char *str, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vsscanf, true, str, format, ap) + +INTERCEPTOR(int, vfscanf, void *stream, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vfscanf, true, stream, format, ap) + +#if SANITIZER_INTERCEPT_ISOC99_SCANF +INTERCEPTOR(int, __isoc99_vscanf, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vscanf, false, format, ap) + +INTERCEPTOR(int, __isoc99_vsscanf, const char *str, const char *format, + va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap) + +INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) +#endif // SANITIZER_INTERCEPT_ISOC99_SCANF + +#define SCANF_INTERCEPTOR_IMPL(name, vname, ...) \ + { \ + void *ctx; \ + va_list ap; \ + va_start(ap, format); \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \ + int res = vname(__VA_ARGS__, ap); \ + va_end(ap); \ + return res; \ + } + +INTERCEPTOR(int, scanf, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(scanf, vscanf, format) + +INTERCEPTOR(int, fscanf, void *stream, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) + +INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) + +#if SANITIZER_INTERCEPT_ISOC99_SCANF +INTERCEPTOR(int, __isoc99_scanf, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) + +INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) + +INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) +#endif + +#endif + +#if SANITIZER_INTERCEPT_SCANF +#define INIT_SCANF \ + COMMON_INTERCEPT_FUNCTION(scanf); \ + COMMON_INTERCEPT_FUNCTION(sscanf); \ + COMMON_INTERCEPT_FUNCTION(fscanf); \ + COMMON_INTERCEPT_FUNCTION(vscanf); \ + COMMON_INTERCEPT_FUNCTION(vsscanf); \ + COMMON_INTERCEPT_FUNCTION(vfscanf); +#else +#define INIT_SCANF +#endif + +#if SANITIZER_INTERCEPT_ISOC99_SCANF +#define INIT_ISOC99_SCANF \ + COMMON_INTERCEPT_FUNCTION(__isoc99_scanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_sscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf); +#else +#define INIT_ISOC99_SCANF +#endif + +#if SANITIZER_INTERCEPT_IOCTL +#include "sanitizer_common_interceptors_ioctl.inc" +INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg); + + CHECK(ioctl_initialized); + + // Note: TSan does not use common flags, and they are zero-initialized. + // This effectively disables ioctl handling in TSan. + if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg); + + const ioctl_desc *desc = ioctl_lookup(request); + if (!desc) Printf("WARNING: unknown ioctl %x\n", request); + + if (desc) ioctl_common_pre(ctx, desc, d, request, arg); + int res = REAL(ioctl)(d, request, arg); + // FIXME: some ioctls have different return values for success and failure. + if (desc && res != -1) ioctl_common_post(ctx, desc, res, d, request, arg); + return res; +} +#define INIT_IOCTL \ + ioctl_init(); \ + COMMON_INTERCEPT_FUNCTION(ioctl); +#else +#define INIT_IOCTL +#endif + +#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS +INTERCEPTOR(void *, getpwnam, const char *name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name); + COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + void *res = REAL(getpwnam)(name); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); + return res; +} +INTERCEPTOR(void *, getpwuid, u32 uid) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid); + void *res = REAL(getpwuid)(uid); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); + return res; +} +INTERCEPTOR(void *, getgrnam, const char *name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name); + COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + void *res = REAL(getgrnam)(name); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); + return res; +} +INTERCEPTOR(void *, getgrgid, u32 gid) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid); + void *res = REAL(getgrgid)(gid); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); + return res; +} +#define INIT_GETPWNAM_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(getpwnam); \ + COMMON_INTERCEPT_FUNCTION(getpwuid); \ + COMMON_INTERCEPT_FUNCTION(getgrnam); \ + COMMON_INTERCEPT_FUNCTION(getgrgid); +#else +#define INIT_GETPWNAM_AND_FRIENDS +#endif + +#if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS +INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, char *buf, + SIZE_T buflen, void **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result); + COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, struct_passwd_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, char *buf, SIZE_T buflen, + void **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); + int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, struct_passwd_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, char *buf, + SIZE_T buflen, void **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result); + COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + int res = REAL(getgrnam_r)(name, grp, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, struct_group_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, char *buf, SIZE_T buflen, + void **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); + int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, struct_group_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +#define INIT_GETPWNAM_R_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(getpwnam_r); \ + COMMON_INTERCEPT_FUNCTION(getpwuid_r); \ + COMMON_INTERCEPT_FUNCTION(getgrnam_r); \ + COMMON_INTERCEPT_FUNCTION(getgrgid_r); +#else +#define INIT_GETPWNAM_R_AND_FRIENDS +#endif + +#if SANITIZER_INTERCEPT_CLOCK_GETTIME +INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp); + int res = REAL(clock_getres)(clk_id, tp); + if (!res && tp) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); + } + return res; +} +INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp); + int res = REAL(clock_gettime)(clk_id, tp); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); + } + return res; +} +INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, clock_settime, clk_id, tp); + COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz); + return REAL(clock_settime)(clk_id, tp); +} +#define INIT_CLOCK_GETTIME \ + COMMON_INTERCEPT_FUNCTION(clock_getres); \ + COMMON_INTERCEPT_FUNCTION(clock_gettime); \ + COMMON_INTERCEPT_FUNCTION(clock_settime); +#else +#define INIT_CLOCK_GETTIME +#endif + +#if SANITIZER_INTERCEPT_GETITIMER +INTERCEPTOR(int, getitimer, int which, void *curr_value) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value); + int res = REAL(getitimer)(which, curr_value); + if (!res && curr_value) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz); + } + return res; +} +INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value); + if (new_value) + COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); + int res = REAL(setitimer)(which, new_value, old_value); + if (!res && old_value) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz); + } + return res; +} +#define INIT_GETITIMER \ + COMMON_INTERCEPT_FUNCTION(getitimer); \ + COMMON_INTERCEPT_FUNCTION(setitimer); +#else +#define INIT_GETITIMER +#endif + +#if SANITIZER_INTERCEPT_GLOB +static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob)); + // +1 for NULL pointer at the end. + if (pglob->gl_pathv) + COMMON_INTERCEPTOR_WRITE_RANGE( + ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv)); + for (SIZE_T i = 0; i < pglob->gl_pathc; ++i) { + char *p = pglob->gl_pathv[i]; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, REAL(strlen)(p) + 1); + } +} + +static THREADLOCAL __sanitizer_glob_t *pglob_copy; +static THREADLOCAL void *glob_ctx; + +static void wrapped_gl_closedir(void *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1); + pglob_copy->gl_closedir(dir); +} + +static void *wrapped_gl_readdir(void *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1); + return pglob_copy->gl_readdir(dir); +} + +static void *wrapped_gl_opendir(const char *s) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1); + COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1); + return pglob_copy->gl_opendir(s); +} + +static int wrapped_gl_lstat(const char *s, void *st) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1); + return pglob_copy->gl_lstat(s, st); +} + +static int wrapped_gl_stat(const char *s, void *st) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1); + return pglob_copy->gl_stat(s, st); +} + +INTERCEPTOR(int, glob, const char *pattern, int flags, + int (*errfunc)(const char *epath, int eerrno), + __sanitizer_glob_t *pglob) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); + __sanitizer_glob_t glob_copy = { + 0, 0, 0, + 0, wrapped_gl_closedir, wrapped_gl_readdir, + wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + pglob_copy = &glob_copy; + glob_ctx = ctx; + } + int res = REAL(glob)(pattern, flags, errfunc, pglob); + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + } + pglob_copy = 0; + glob_ctx = 0; + if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); + return res; +} + +INTERCEPTOR(int, glob64, const char *pattern, int flags, + int (*errfunc)(const char *epath, int eerrno), + __sanitizer_glob_t *pglob) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); + __sanitizer_glob_t glob_copy = { + 0, 0, 0, + 0, wrapped_gl_closedir, wrapped_gl_readdir, + wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + pglob_copy = &glob_copy; + glob_ctx = ctx; + } + int res = REAL(glob64)(pattern, flags, errfunc, pglob); + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + Swap(pglob->gl_stat, glob_copy.gl_stat); + } + pglob_copy = 0; + glob_ctx = 0; + if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); + return res; +} +#define INIT_GLOB \ + COMMON_INTERCEPT_FUNCTION(glob); \ + COMMON_INTERCEPT_FUNCTION(glob64); +#else // SANITIZER_INTERCEPT_GLOB +#define INIT_GLOB +#endif // SANITIZER_INTERCEPT_GLOB + +#if SANITIZER_INTERCEPT_WAIT +// According to sys/wait.h, wait(), waitid(), waitpid() may have symbol version +// suffixes on Darwin. See the declaration of INTERCEPTOR_WITH_SUFFIX for +// details. +INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wait, status); + int res = REAL(wait)(status); + if (res != -1 && status) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + return res; +} +INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, + int options) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); + int res = REAL(waitid)(idtype, id, infop, options); + if (res != -1 && infop) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz); + return res; +} +INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options); + int res = REAL(waitpid)(pid, status, options); + if (res != -1 && status) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + return res; +} +INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); + int res = REAL(wait3)(status, options, rusage); + if (res != -1) { + if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); + } + return res; +} +INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); + int res = REAL(wait4)(pid, status, options, rusage); + if (res != -1) { + if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); + } + return res; +} +#define INIT_WAIT \ + COMMON_INTERCEPT_FUNCTION(wait); \ + COMMON_INTERCEPT_FUNCTION(waitid); \ + COMMON_INTERCEPT_FUNCTION(waitpid); \ + COMMON_INTERCEPT_FUNCTION(wait3); \ + COMMON_INTERCEPT_FUNCTION(wait4); +#else +#define INIT_WAIT +#endif + +#if SANITIZER_INTERCEPT_INET +INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, inet_ntop, af, src, dst, size); + uptr sz = __sanitizer_in_addr_sz(af); + if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz); + // FIXME: figure out read size based on the address family. + char *res = REAL(inet_ntop)(af, src, dst, size); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst); + // FIXME: figure out read size based on the address family. + int res = REAL(inet_pton)(af, src, dst); + if (res == 1) { + uptr sz = __sanitizer_in_addr_sz(af); + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); + } + return res; +} +#define INIT_INET \ + COMMON_INTERCEPT_FUNCTION(inet_ntop); \ + COMMON_INTERCEPT_FUNCTION(inet_pton); +#else +#define INIT_INET +#endif + +#if SANITIZER_INTERCEPT_INET +INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst); + if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1); + int res = REAL(inet_aton)(cp, dst); + if (res != 0) { + uptr sz = __sanitizer_in_addr_sz(af_inet); + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); + } + return res; +} +#define INIT_INET_ATON COMMON_INTERCEPT_FUNCTION(inet_aton); +#else +#define INIT_INET_ATON +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM +INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param); + int res = REAL(pthread_getschedparam)(thread, policy, param); + if (res == 0) { + if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy)); + if (param) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, sizeof(*param)); + } + return res; +} +#define INIT_PTHREAD_GETSCHEDPARAM \ + COMMON_INTERCEPT_FUNCTION(pthread_getschedparam); +#else +#define INIT_PTHREAD_GETSCHEDPARAM +#endif + +#if SANITIZER_INTERCEPT_GETADDRINFO +INTERCEPTOR(int, getaddrinfo, char *node, char *service, + struct __sanitizer_addrinfo *hints, + struct __sanitizer_addrinfo **out) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getaddrinfo, node, service, hints, out); + if (node) COMMON_INTERCEPTOR_READ_RANGE(ctx, node, REAL(strlen)(node) + 1); + if (service) + COMMON_INTERCEPTOR_READ_RANGE(ctx, service, REAL(strlen)(service) + 1); + if (hints) + COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo)); + int res = REAL(getaddrinfo)(node, service, hints, out); + if (res == 0 && out) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out)); + struct __sanitizer_addrinfo *p = *out; + while (p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); + if (p->ai_addr) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, p->ai_addrlen); + if (p->ai_canonname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_canonname, + REAL(strlen)(p->ai_canonname) + 1); + p = p->ai_next; + } + } + return res; +} +#define INIT_GETADDRINFO COMMON_INTERCEPT_FUNCTION(getaddrinfo); +#else +#define INIT_GETADDRINFO +#endif + +#if SANITIZER_INTERCEPT_GETNAMEINFO +INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, + unsigned hostlen, char *serv, unsigned servlen, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getnameinfo, sockaddr, salen, host, hostlen, + serv, servlen, flags); + // FIXME: consider adding READ_RANGE(sockaddr, salen) + // There is padding in in_addr that may make this too noisy + int res = + REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags); + if (res == 0) { + if (host && hostlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, host, REAL(strlen)(host) + 1); + if (serv && servlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, serv, REAL(strlen)(serv) + 1); + } + return res; +} +#define INIT_GETNAMEINFO COMMON_INTERCEPT_FUNCTION(getnameinfo); +#else +#define INIT_GETNAMEINFO +#endif + +#if SANITIZER_INTERCEPT_GETSOCKNAME +INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen); + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + int addrlen_in = *addrlen; + int res = REAL(getsockname)(sock_fd, addr, addrlen); + if (res == 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen)); + } + return res; +} +#define INIT_GETSOCKNAME COMMON_INTERCEPT_FUNCTION(getsockname); +#else +#define INIT_GETSOCKNAME +#endif + +#if SANITIZER_INTERCEPT_GETHOSTBYNAME || SANITIZER_INTERCEPT_GETHOSTBYNAME_R +static void write_hostent(void *ctx, struct __sanitizer_hostent *h) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h, sizeof(__sanitizer_hostent)); + if (h->h_name) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h->h_name, REAL(strlen)(h->h_name) + 1); + char **p = h->h_aliases; + while (*p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); + ++p; + } + COMMON_INTERCEPTOR_WRITE_RANGE( + ctx, h->h_aliases, (p - h->h_aliases + 1) * sizeof(*h->h_aliases)); + p = h->h_addr_list; + while (*p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, h->h_length); + ++p; + } + COMMON_INTERCEPTOR_WRITE_RANGE( + ctx, h->h_addr_list, (p - h->h_addr_list + 1) * sizeof(*h->h_addr_list)); +} +#endif + +#if SANITIZER_INTERCEPT_GETHOSTBYNAME +INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname, char *name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname, name); + struct __sanitizer_hostent *res = REAL(gethostbyname)(name); + if (res) write_hostent(ctx, res); + return res; +} + +INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len, + int type) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr, addr, len, type); + COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); + struct __sanitizer_hostent *res = REAL(gethostbyaddr)(addr, len, type); + if (res) write_hostent(ctx, res); + return res; +} + +INTERCEPTOR(struct __sanitizer_hostent *, gethostent, int fake) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostent, fake); + struct __sanitizer_hostent *res = REAL(gethostent)(fake); + if (res) write_hostent(ctx, res); + return res; +} + +INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2, name, af); + struct __sanitizer_hostent *res = REAL(gethostbyname2)(name, af); + if (res) write_hostent(ctx, res); + return res; +} +#define INIT_GETHOSTBYNAME \ + COMMON_INTERCEPT_FUNCTION(gethostent); \ + COMMON_INTERCEPT_FUNCTION(gethostbyaddr); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname2); +#else +#define INIT_GETHOSTBYNAME +#endif + +#if SANITIZER_INTERCEPT_GETHOSTBYNAME_R +INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf, + SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostent_r, ret, buf, buflen, result, + h_errnop); + int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop); + if (res == 0) { + if (result) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) write_hostent(ctx, *result); + } + if (h_errnop) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); + } + return res; +} + +INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type, + struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, + __sanitizer_hostent **result, int *h_errnop) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr_r, addr, len, type, ret, buf, + buflen, result, h_errnop); + COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); + int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result, + h_errnop); + if (res == 0) { + if (result) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) write_hostent(ctx, *result); + } + if (h_errnop) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); + } + return res; +} + +INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret, + char *buf, SIZE_T buflen, __sanitizer_hostent **result, + int *h_errnop) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname_r, name, ret, buf, buflen, result, + h_errnop); + int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop); + if (res == 0) { + if (result) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) write_hostent(ctx, *result); + } + if (h_errnop) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); + } + return res; +} + +INTERCEPTOR(int, gethostbyname2_r, char *name, int af, + struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, + __sanitizer_hostent **result, int *h_errnop) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2_r, name, af, ret, buf, buflen, + result, h_errnop); + int res = + REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop); + if (res == 0) { + if (result) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) write_hostent(ctx, *result); + } + if (h_errnop) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); + } + return res; +} +#define INIT_GETHOSTBYNAME_R \ + COMMON_INTERCEPT_FUNCTION(gethostent_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname2_r); +#else +#define INIT_GETHOSTBYNAME_R +#endif + +#if SANITIZER_INTERCEPT_GETSOCKOPT +INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, + int *optlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getsockopt, sockfd, level, optname, optval, + optlen); + if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen)); + int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen); + if (res == 0) + if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); + return res; +} +#define INIT_GETSOCKOPT COMMON_INTERCEPT_FUNCTION(getsockopt); +#else +#define INIT_GETSOCKOPT +#endif + +#if SANITIZER_INTERCEPT_ACCEPT +INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen); + unsigned addrlen0; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addrlen0 = *addrlen; + } + int fd2 = REAL(accept)(fd, addr, addrlen); + if (fd2 >= 0) { + if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); + } + return fd2; +} +#define INIT_ACCEPT COMMON_INTERCEPT_FUNCTION(accept); +#else +#define INIT_ACCEPT +#endif + +#if SANITIZER_INTERCEPT_ACCEPT4 +INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f); + unsigned addrlen0; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addrlen0 = *addrlen; + } + int fd2 = REAL(accept4)(fd, addr, addrlen, f); + if (fd2 >= 0) { + if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); + } + return fd2; +} +#define INIT_ACCEPT4 COMMON_INTERCEPT_FUNCTION(accept4); +#else +#define INIT_ACCEPT4 +#endif + +#if SANITIZER_INTERCEPT_MODF +INTERCEPTOR(double, modf, double x, double *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr); + double res = REAL(modf)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +INTERCEPTOR(float, modff, float x, float *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr); + float res = REAL(modff)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +INTERCEPTOR(long double, modfl, long double x, long double *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr); + long double res = REAL(modfl)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +#define INIT_MODF \ + COMMON_INTERCEPT_FUNCTION(modf); \ + COMMON_INTERCEPT_FUNCTION(modff); \ + COMMON_INTERCEPT_FUNCTION(modfl); +#else +#define INIT_MODF +#endif + +#if SANITIZER_INTERCEPT_RECVMSG +static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg, + SSIZE_T maxlen) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg)); + if (msg->msg_name && msg->msg_namelen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, msg->msg_namelen); + if (msg->msg_iov && msg->msg_iovlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov, + sizeof(*msg->msg_iov) * msg->msg_iovlen); + write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); + if (msg->msg_control && msg->msg_controllen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen); +} + +INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, + int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags); + SSIZE_T res = REAL(recvmsg)(fd, msg, flags); + if (res >= 0) { + if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (msg) write_msghdr(ctx, msg, res); + } + return res; +} +#define INIT_RECVMSG COMMON_INTERCEPT_FUNCTION(recvmsg); +#else +#define INIT_RECVMSG +#endif + +#if SANITIZER_INTERCEPT_GETPEERNAME +INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen); + unsigned addr_sz; + if (addrlen) addr_sz = *addrlen; + int res = REAL(getpeername)(sockfd, addr, addrlen); + if (!res && addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); + return res; +} +#define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername); +#else +#define INIT_GETPEERNAME +#endif + +#if SANITIZER_INTERCEPT_SYSINFO +INTERCEPTOR(int, sysinfo, void *info) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info); + int res = REAL(sysinfo)(info); + if (!res && info) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz); + return res; +} +#define INIT_SYSINFO COMMON_INTERCEPT_FUNCTION(sysinfo); +#else +#define INIT_SYSINFO +#endif + +#if SANITIZER_INTERCEPT_READDIR +INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); + __sanitizer_dirent *res = REAL(readdir)(dirp); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + return res; +} + +INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, + __sanitizer_dirent **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result); + int res = REAL(readdir_r)(dirp, entry, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + } + return res; +} + +#define INIT_READDIR \ + COMMON_INTERCEPT_FUNCTION(readdir); \ + COMMON_INTERCEPT_FUNCTION(readdir_r); +#else +#define INIT_READDIR +#endif + +#if SANITIZER_INTERCEPT_READDIR64 +INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); + __sanitizer_dirent64 *res = REAL(readdir64)(dirp); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + return res; +} + +INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, + __sanitizer_dirent64 **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result); + int res = REAL(readdir64_r)(dirp, entry, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + } + return res; +} +#define INIT_READDIR64 \ + COMMON_INTERCEPT_FUNCTION(readdir64); \ + COMMON_INTERCEPT_FUNCTION(readdir64_r); +#else +#define INIT_READDIR64 +#endif + +#if SANITIZER_INTERCEPT_PTRACE +INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); + + if (data) { + if (request == ptrace_setregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz); + else if (request == ptrace_setfpregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); + else if (request == ptrace_setfpxregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + else if (request == ptrace_setsiginfo) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); + else if (request == ptrace_setregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + COMMON_INTERCEPTOR_READ_RANGE(ctx, iov->iov_base, iov->iov_len); + } + } + + uptr res = REAL(ptrace)(request, pid, addr, data); + + if (!res && data) { + // Note that PEEK* requests assing different meaning to the return value. + // This function does not handle them (nor does it need to). + if (request == ptrace_getregs) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz); + else if (request == ptrace_getfpregs) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); + else if (request == ptrace_getfpxregs) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); + else if (request == ptrace_getsiginfo) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); + else if (request == ptrace_getregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iov->iov_base, iov->iov_len); + } + } + return res; +} + +#define INIT_PTRACE COMMON_INTERCEPT_FUNCTION(ptrace); +#else +#define INIT_PTRACE +#endif + +#if SANITIZER_INTERCEPT_SETLOCALE +INTERCEPTOR(char *, setlocale, int category, char *locale) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, setlocale, category, locale); + if (locale) + COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); + char *res = REAL(setlocale)(category, locale); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} + +#define INIT_SETLOCALE COMMON_INTERCEPT_FUNCTION(setlocale); +#else +#define INIT_SETLOCALE +#endif + +#if SANITIZER_INTERCEPT_GETCWD +INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); + char *res = REAL(getcwd)(buf, size); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_GETCWD COMMON_INTERCEPT_FUNCTION(getcwd); +#else +#define INIT_GETCWD +#endif + +#if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME +INTERCEPTOR(char *, get_current_dir_name, int fake) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake); + char *res = REAL(get_current_dir_name)(fake); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} + +#define INIT_GET_CURRENT_DIR_NAME \ + COMMON_INTERCEPT_FUNCTION(get_current_dir_name); +#else +#define INIT_GET_CURRENT_DIR_NAME +#endif + +#if SANITIZER_INTERCEPT_STRTOIMAX +INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base); + INTMAX_T res = REAL(strtoimax)(nptr, endptr, base); + if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + return res; +} + +INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base); + INTMAX_T res = REAL(strtoumax)(nptr, endptr, base); + if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + return res; +} + +#define INIT_STRTOIMAX \ + COMMON_INTERCEPT_FUNCTION(strtoimax); \ + COMMON_INTERCEPT_FUNCTION(strtoumax); +#else +#define INIT_STRTOIMAX +#endif + +#if SANITIZER_INTERCEPT_MBSTOWCS +INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len); + SIZE_T res = REAL(mbstowcs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, + void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbsrtowcs, dest, src, len, ps); + if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps); + if (res != (SIZE_T)(-1) && dest && src) { + // This function, and several others, may or may not write the terminating + // \0 character. They write it iff they clear *src. + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT_MBSTOWCS \ + COMMON_INTERCEPT_FUNCTION(mbstowcs); \ + COMMON_INTERCEPT_FUNCTION(mbsrtowcs); +#else +#define INIT_MBSTOWCS +#endif + +#if SANITIZER_INTERCEPT_MBSNRTOWCS +INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, + SIZE_T len, void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbsnrtowcs, dest, src, nms, len, ps); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); + if (res != (SIZE_T)(-1) && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT_MBSNRTOWCS COMMON_INTERCEPT_FUNCTION(mbsnrtowcs); +#else +#define INIT_MBSNRTOWCS +#endif + +#if SANITIZER_INTERCEPT_WCSTOMBS +INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len); + SIZE_T res = REAL(wcstombs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, + void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcsrtombs, dest, src, len, ps); + if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); + if (res != (SIZE_T) - 1 && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT_WCSTOMBS \ + COMMON_INTERCEPT_FUNCTION(wcstombs); \ + COMMON_INTERCEPT_FUNCTION(wcsrtombs); +#else +#define INIT_WCSTOMBS +#endif + +#if SANITIZER_INTERCEPT_WCSNRTOMBS +INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, + SIZE_T len, void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcsnrtombs, dest, src, nms, len, ps); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); + if (res != (SIZE_T) - 1 && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT_WCSNRTOMBS COMMON_INTERCEPT_FUNCTION(wcsnrtombs); +#else +#define INIT_WCSNRTOMBS +#endif + +#if SANITIZER_INTERCEPT_TCGETATTR +INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p); + int res = REAL(tcgetattr)(fd, termios_p); + if (!res && termios_p) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz); + return res; +} + +#define INIT_TCGETATTR COMMON_INTERCEPT_FUNCTION(tcgetattr); +#else +#define INIT_TCGETATTR +#endif + +#if SANITIZER_INTERCEPT_REALPATH +INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + + // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest + // version of a versioned symbol. For realpath(), this gives us something + // (called __old_realpath) that does not handle NULL in the second argument. + // Handle it as part of the interceptor. + char *allocated_path = 0; + if (!resolved_path) + allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); + + char *res = REAL(realpath)(path, resolved_path); + if (allocated_path && !res) WRAP(free)(allocated_path); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_REALPATH COMMON_INTERCEPT_FUNCTION(realpath); +#else +#define INIT_REALPATH +#endif + +#if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME +INTERCEPTOR(char *, canonicalize_file_name, const char *path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + char *res = REAL(canonicalize_file_name)(path); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_CANONICALIZE_FILE_NAME \ + COMMON_INTERCEPT_FUNCTION(canonicalize_file_name); +#else +#define INIT_CANONICALIZE_FILE_NAME +#endif + +#if SANITIZER_INTERCEPT_CONFSTR +INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len); + SIZE_T res = REAL(confstr)(name, buf, len); + if (buf && res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); + return res; +} +#define INIT_CONFSTR COMMON_INTERCEPT_FUNCTION(confstr); +#else +#define INIT_CONFSTR +#endif + +#if SANITIZER_INTERCEPT_SCHED_GETAFFINITY +INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); + int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); + if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); + return res; +} +#define INIT_SCHED_GETAFFINITY COMMON_INTERCEPT_FUNCTION(sched_getaffinity); +#else +#define INIT_SCHED_GETAFFINITY +#endif + +#if SANITIZER_INTERCEPT_STRERROR +INTERCEPTOR(char *, strerror, int errnum) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum); + char *res = REAL(strerror)(errnum); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror); +#else +#define INIT_STRERROR +#endif + +#if SANITIZER_INTERCEPT_STRERROR_R +INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); + char *res = REAL(strerror_r)(errnum, buf, buflen); + // There are 2 versions of strerror_r: + // * POSIX version returns 0 on success, negative error code on failure, + // writes message to buf. + // * GNU version returns message pointer, which points to either buf or some + // static storage. + SIZE_T posix_res = (SIZE_T)res; + if (posix_res < 1024 || posix_res > (SIZE_T) - 1024) { + // POSIX version. Spec is not clear on whether buf is NULL-terminated. + // At least on OSX, buf contents are valid even when the call fails. + SIZE_T sz = internal_strnlen(buf, buflen); + if (sz < buflen) ++sz; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); + } else { + // GNU version. + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_STRERROR_R COMMON_INTERCEPT_FUNCTION(strerror_r); +#else +#define INIT_STRERROR_R +#endif + +#if SANITIZER_INTERCEPT_SCANDIR +typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *); +typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **, + const struct __sanitizer_dirent **); + +static THREADLOCAL void *scandir_ctx; +static THREADLOCAL scandir_filter_f scandir_filter; +static THREADLOCAL scandir_compar_f scandir_compar; + +static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir_ctx, 1); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, dir, dir->d_reclen); + return scandir_filter(dir); +} + +static int wrapped_scandir_compar(const struct __sanitizer_dirent **a, + const struct __sanitizer_dirent **b) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, a, sizeof(*a)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, *a, (*a)->d_reclen); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, b, sizeof(*b)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, *b, (*b)->d_reclen); + return scandir_compar(a, b); +} + +INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, + scandir_filter_f filter, scandir_compar_f compar) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, scandir, dirp, namelist, filter, compar); + if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); + CHECK_EQ(0, scandir_ctx); + scandir_ctx = ctx; + scandir_filter = filter; + scandir_compar = compar; + int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : 0, + compar ? wrapped_scandir_compar : 0); + scandir_ctx = 0; + scandir_filter = 0; + scandir_compar = 0; + if (namelist && res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); + for (int i = 0; i < res; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], + (*namelist)[i]->d_reclen); + } + return res; +} +#define INIT_SCANDIR COMMON_INTERCEPT_FUNCTION(scandir); +#else +#define INIT_SCANDIR +#endif + +#if SANITIZER_INTERCEPT_SCANDIR64 +typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *); +typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **, + const struct __sanitizer_dirent64 **); + +static THREADLOCAL void *scandir64_ctx; +static THREADLOCAL scandir64_filter_f scandir64_filter; +static THREADLOCAL scandir64_compar_f scandir64_compar; + +static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir64_ctx, 1); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, dir, dir->d_reclen); + return scandir64_filter(dir); +} + +static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a, + const struct __sanitizer_dirent64 **b) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir64_ctx, 2); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, a, sizeof(*a)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, *a, (*a)->d_reclen); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, b, sizeof(*b)); + COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, *b, (*b)->d_reclen); + return scandir64_compar(a, b); +} + +INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, + scandir64_filter_f filter, scandir64_compar_f compar) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, scandir64, dirp, namelist, filter, compar); + if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); + CHECK_EQ(0, scandir64_ctx); + scandir64_ctx = ctx; + scandir64_filter = filter; + scandir64_compar = compar; + int res = + REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : 0, + compar ? wrapped_scandir64_compar : 0); + scandir64_ctx = 0; + scandir64_filter = 0; + scandir64_compar = 0; + if (namelist && res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); + for (int i = 0; i < res; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], + (*namelist)[i]->d_reclen); + } + return res; +} +#define INIT_SCANDIR64 COMMON_INTERCEPT_FUNCTION(scandir64); +#else +#define INIT_SCANDIR64 +#endif + +#if SANITIZER_INTERCEPT_GETGROUPS +INTERCEPTOR(int, getgroups, int size, u32 *lst) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); + int res = REAL(getgroups)(size, lst); + if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); + return res; +} +#define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups); +#else +#define INIT_GETGROUPS +#endif + +#if SANITIZER_INTERCEPT_POLL +static void read_pollfd(void *ctx, __sanitizer_pollfd *fds, + __sanitizer_nfds_t nfds) { + for (unsigned i = 0; i < nfds; ++i) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].fd, sizeof(fds[i].fd)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].events, sizeof(fds[i].events)); + } +} + +static void write_pollfd(void *ctx, __sanitizer_pollfd *fds, + __sanitizer_nfds_t nfds) { + for (unsigned i = 0; i < nfds; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &fds[i].revents, + sizeof(fds[i].revents)); +} + +INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, + int timeout) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, poll, fds, nfds, timeout); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT_POLL COMMON_INTERCEPT_FUNCTION(poll); +#else +#define INIT_POLL +#endif + +#if SANITIZER_INTERCEPT_PPOLL +INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, + void *timeout_ts, __sanitizer_sigset_t *sigmask) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ppoll, fds, nfds, timeout_ts, sigmask); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + if (timeout_ts) + COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz); + // FIXME: read sigmask when all of sigemptyset, etc are intercepted. + int res = + COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT_PPOLL COMMON_INTERCEPT_FUNCTION(ppoll); +#else +#define INIT_PPOLL +#endif + +#if SANITIZER_INTERCEPT_WORDEXP +INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags); + if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); + int res = REAL(wordexp)(s, p, flags); + if (!res && p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); + if (p->we_wordc) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->we_wordv, + sizeof(*p->we_wordv) * p->we_wordc); + for (uptr i = 0; i < p->we_wordc; ++i) { + char *w = p->we_wordv[i]; + if (w) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, w, REAL(strlen)(w) + 1); + } + } + return res; +} +#define INIT_WORDEXP COMMON_INTERCEPT_FUNCTION(wordexp); +#else +#define INIT_WORDEXP +#endif + +#if SANITIZER_INTERCEPT_SIGWAIT +INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigwait)(set, sig); + if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); + return res; +} +#define INIT_SIGWAIT COMMON_INTERCEPT_FUNCTION(sigwait); +#else +#define INIT_SIGWAIT +#endif + +#if SANITIZER_INTERCEPT_SIGWAITINFO +INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigwaitinfo)(set, info); + if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); + return res; +} +#define INIT_SIGWAITINFO COMMON_INTERCEPT_FUNCTION(sigwaitinfo); +#else +#define INIT_SIGWAITINFO +#endif + +#if SANITIZER_INTERCEPT_SIGTIMEDWAIT +INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, + void *timeout) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigtimedwait, set, info, timeout); + if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigtimedwait)(set, info, timeout); + if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); + return res; +} +#define INIT_SIGTIMEDWAIT COMMON_INTERCEPT_FUNCTION(sigtimedwait); +#else +#define INIT_SIGTIMEDWAIT +#endif + +#if SANITIZER_INTERCEPT_SIGSETOPS +INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set); + int res = REAL(sigemptyset)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} + +INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set); + int res = REAL(sigfillset)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} +#define INIT_SIGSETOPS \ + COMMON_INTERCEPT_FUNCTION(sigemptyset); \ + COMMON_INTERCEPT_FUNCTION(sigfillset); +#else +#define INIT_SIGSETOPS +#endif + +#if SANITIZER_INTERCEPT_SIGPENDING +INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set); + int res = REAL(sigpending)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} +#define INIT_SIGPENDING COMMON_INTERCEPT_FUNCTION(sigpending); +#else +#define INIT_SIGPENDING +#endif + +#if SANITIZER_INTERCEPT_SIGPROCMASK +INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigprocmask)(how, set, oldset); + if (!res && oldset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); + return res; +} +#define INIT_SIGPROCMASK COMMON_INTERCEPT_FUNCTION(sigprocmask); +#else +#define INIT_SIGPROCMASK +#endif + +#if SANITIZER_INTERCEPT_BACKTRACE +INTERCEPTOR(int, backtrace, void **buffer, int size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); + int res = REAL(backtrace)(buffer, size); + if (res && buffer) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); + return res; +} + +INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size); + if (buffer && size) + COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); + char **res = REAL(backtrace_symbols)(buffer, size); + if (res && size) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); + for (int i = 0; i < size; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res[i], REAL(strlen(res[i])) + 1); + } + return res; +} +#define INIT_BACKTRACE \ + COMMON_INTERCEPT_FUNCTION(backtrace); \ + COMMON_INTERCEPT_FUNCTION(backtrace_symbols); +#else +#define INIT_BACKTRACE +#endif + +#if SANITIZER_INTERCEPT__EXIT +INTERCEPTOR(void, _exit, int status) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, _exit, status); + int status1 = COMMON_INTERCEPTOR_ON_EXIT(ctx); + if (status == 0) status = status1; + REAL(_exit)(status); +} +#define INIT__EXIT COMMON_INTERCEPT_FUNCTION(_exit); +#else +#define INIT__EXIT +#endif + +#if SANITIZER_INTERCEPT_PHTREAD_MUTEX +INTERCEPTOR(int, pthread_mutex_lock, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m); + int res = REAL(pthread_mutex_lock)(m); + if (res == errno_EOWNERDEAD) + COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); + if (res == 0 || res == errno_EOWNERDEAD) + COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); + return res; +} + +INTERCEPTOR(int, pthread_mutex_unlock, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m); + COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); + return REAL(pthread_mutex_unlock)(m); +} + +#define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock) +#define INIT_PTHREAD_MUTEX_UNLOCK \ + COMMON_INTERCEPT_FUNCTION(pthread_mutex_unlock) +#else +#define INIT_PTHREAD_MUTEX_LOCK +#define INIT_PTHREAD_MUTEX_UNLOCK +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_COND +INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, c, m); + COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + int res = REAL(pthread_cond_wait)(c, m); + COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); + return res; +} + +INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, c, a); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_init)(c, a); +} + +INTERCEPTOR(int, pthread_cond_signal, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, c); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_signal)(c); +} + +INTERCEPTOR(int, pthread_cond_broadcast, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, c); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_broadcast)(c); +} + +#define INIT_PTHREAD_COND_WAIT \ + INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_INIT \ + INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_SIGNAL \ + INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_BROADCAST \ + INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2") +#else +#define INIT_PTHREAD_COND_WAIT +#define INIT_PTHREAD_COND_INIT +#define INIT_PTHREAD_COND_SIGNAL +#define INIT_PTHREAD_COND_BROADCAST +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R +static void write_mntent(void *ctx, __sanitizer_mntent *mnt) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt)); + if (mnt->mnt_fsname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_fsname, + REAL(strlen)(mnt->mnt_fsname) + 1); + if (mnt->mnt_dir) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_dir, + REAL(strlen)(mnt->mnt_dir) + 1); + if (mnt->mnt_type) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_type, + REAL(strlen)(mnt->mnt_type) + 1); + if (mnt->mnt_opts) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_opts, + REAL(strlen)(mnt->mnt_opts) + 1); +} +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT +INTERCEPTOR(__sanitizer_mntent *, getmntent, void *fp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getmntent, fp); + __sanitizer_mntent *res = REAL(getmntent)(fp); + if (res) write_mntent(ctx, res); + return res; +} +#define INIT_GETMNTENT COMMON_INTERCEPT_FUNCTION(getmntent); +#else +#define INIT_GETMNTENT +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT_R +INTERCEPTOR(__sanitizer_mntent *, getmntent_r, void *fp, + __sanitizer_mntent *mntbuf, char *buf, int buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getmntent_r, fp, mntbuf, buf, buflen); + __sanitizer_mntent *res = REAL(getmntent_r)(fp, mntbuf, buf, buflen); + if (res) write_mntent(ctx, res); + return res; +} +#define INIT_GETMNTENT_R COMMON_INTERCEPT_FUNCTION(getmntent_r); +#else +#define INIT_GETMNTENT_R +#endif + +#if SANITIZER_INTERCEPT_STATFS +INTERCEPTOR(int, statfs, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statfs)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); + return res; +} +INTERCEPTOR(int, fstatfs, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf); + int res = REAL(fstatfs)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); + return res; +} +#define INIT_STATFS \ + COMMON_INTERCEPT_FUNCTION(statfs); \ + COMMON_INTERCEPT_FUNCTION(fstatfs); +#else +#define INIT_STATFS +#endif + +#if SANITIZER_INTERCEPT_STATFS64 +INTERCEPTOR(int, statfs64, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statfs64)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); + return res; +} +INTERCEPTOR(int, fstatfs64, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf); + int res = REAL(fstatfs64)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); + return res; +} +#define INIT_STATFS64 \ + COMMON_INTERCEPT_FUNCTION(statfs64); \ + COMMON_INTERCEPT_FUNCTION(fstatfs64); +#else +#define INIT_STATFS64 +#endif + +#if SANITIZER_INTERCEPT_STATVFS +INTERCEPTOR(int, statvfs, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statvfs)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); + return res; +} +INTERCEPTOR(int, fstatvfs, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf); + int res = REAL(fstatvfs)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); + return res; +} +#define INIT_STATVFS \ + COMMON_INTERCEPT_FUNCTION(statvfs); \ + COMMON_INTERCEPT_FUNCTION(fstatvfs); +#else +#define INIT_STATVFS +#endif + +#if SANITIZER_INTERCEPT_STATVFS64 +INTERCEPTOR(int, statvfs64, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statvfs64)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); + return res; +} +INTERCEPTOR(int, fstatvfs64, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf); + int res = REAL(fstatvfs64)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); + return res; +} +#define INIT_STATVFS64 \ + COMMON_INTERCEPT_FUNCTION(statvfs64); \ + COMMON_INTERCEPT_FUNCTION(fstatvfs64); +#else +#define INIT_STATVFS64 +#endif + +#if SANITIZER_INTERCEPT_INITGROUPS +INTERCEPTOR(int, initgroups, char *user, u32 group) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, initgroups, user, group); + if (user) COMMON_INTERCEPTOR_READ_RANGE(ctx, user, REAL(strlen)(user) + 1); + int res = REAL(initgroups)(user, group); + return res; +} +#define INIT_INITGROUPS COMMON_INTERCEPT_FUNCTION(initgroups); +#else +#define INIT_INITGROUPS +#endif + +#if SANITIZER_INTERCEPT_ETHER +INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + char *res = REAL(ether_ntoa)(addr); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf); + if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); + __sanitizer_ether_addr *res = REAL(ether_aton)(buf); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, sizeof(*res)); + return res; +} +INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + int res = REAL(ether_ntohost)(hostname, addr); + if (!res && hostname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + return res; +} +INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr); + if (hostname) + COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + int res = REAL(ether_hostton)(hostname, addr); + if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + return res; +} +INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr, + char *hostname) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname); + if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1); + int res = REAL(ether_line)(line, addr, hostname); + if (!res) { + if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + if (hostname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + } + return res; +} +#define INIT_ETHER \ + COMMON_INTERCEPT_FUNCTION(ether_ntoa); \ + COMMON_INTERCEPT_FUNCTION(ether_aton); \ + COMMON_INTERCEPT_FUNCTION(ether_ntohost); \ + COMMON_INTERCEPT_FUNCTION(ether_hostton); \ + COMMON_INTERCEPT_FUNCTION(ether_line); +#else +#define INIT_ETHER +#endif + +#if SANITIZER_INTERCEPT_ETHER_R +INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + char *res = REAL(ether_ntoa_r)(addr, buf); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf, + __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr); + if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); + __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res)); + return res; +} +#define INIT_ETHER_R \ + COMMON_INTERCEPT_FUNCTION(ether_ntoa_r); \ + COMMON_INTERCEPT_FUNCTION(ether_aton_r); +#else +#define INIT_ETHER_R +#endif + +#if SANITIZER_INTERCEPT_SHMCTL +INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf); + int res = REAL(shmctl)(shmid, cmd, buf); + if (res >= 0) { + unsigned sz = 0; + if (cmd == shmctl_ipc_stat || cmd == shmctl_shm_stat) + sz = sizeof(__sanitizer_shmid_ds); + else if (cmd == shmctl_ipc_info) + sz = struct_shminfo_sz; + else if (cmd == shmctl_shm_info) + sz = struct_shm_info_sz; + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); + } + return res; +} +#define INIT_SHMCTL COMMON_INTERCEPT_FUNCTION(shmctl); +#else +#define INIT_SHMCTL +#endif + +#if SANITIZER_INTERCEPT_RANDOM_R +INTERCEPTOR(int, random_r, void *buf, u32 *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result); + int res = REAL(random_r)(buf, result); + if (!res && result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +#define INIT_RANDOM_R COMMON_INTERCEPT_FUNCTION(random_r); +#else +#define INIT_RANDOM_R +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \ + SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED +#define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \ + INTERCEPTOR(int, pthread_attr_get##what, void *attr, void *r) { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_get##what, attr, r); \ + int res = REAL(pthread_attr_get##what)(attr, r); \ + if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \ + return res; \ + } +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET +INTERCEPTOR_PTHREAD_ATTR_GET(detachstate, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(guardsize, sizeof(SIZE_T)) +INTERCEPTOR_PTHREAD_ATTR_GET(schedparam, struct_sched_param_sz) +INTERCEPTOR_PTHREAD_ATTR_GET(schedpolicy, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(scope, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(stacksize, sizeof(SIZE_T)) +INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size); + int res = REAL(pthread_attr_getstack)(attr, addr, size); + if (!res) { + if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + if (size) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, size, sizeof(*size)); + } + return res; +} + +#define INIT_PTHREAD_ATTR_GET \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getguardsize); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedparam); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedpolicy); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getscope); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getstacksize); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getstack); +#else +#define INIT_PTHREAD_ATTR_GET +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED +INTERCEPTOR_PTHREAD_ATTR_GET(inheritsched, sizeof(int)) + +#define INIT_PTHREAD_ATTR_GETINHERITSCHED \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getinheritsched); +#else +#define INIT_PTHREAD_ATTR_GETINHERITSCHED +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP +INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize, + void *cpuset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize, + cpuset); + int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset); + if (!res && cpusetsize && cpuset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize); + return res; +} + +#define INIT_PTHREAD_ATTR_GETAFFINITY_NP \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getaffinity_np); +#else +#define INIT_PTHREAD_ATTR_GETAFFINITY_NP +#endif + +#if SANITIZER_INTERCEPT_TMPNAM +INTERCEPTOR(char *, tmpnam, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tmpnam, s); + char *res = REAL(tmpnam)(s); + if (res) { + if (s) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); + else + COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_TMPNAM COMMON_INTERCEPT_FUNCTION(tmpnam); +#else +#define INIT_TMPNAM +#endif + +#if SANITIZER_INTERCEPT_TMPNAM_R +INTERCEPTOR(char *, tmpnam_r, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s); + char *res = REAL(tmpnam_r)(s); + if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); + return res; +} +#define INIT_TMPNAM_R COMMON_INTERCEPT_FUNCTION(tmpnam_r); +#else +#define INIT_TMPNAM_R +#endif + +#if SANITIZER_INTERCEPT_TEMPNAM +INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tempnam, dir, pfx); + if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1); + if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1); + char *res = REAL(tempnam)(dir, pfx); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam); +#else +#define INIT_TEMPNAM +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP +INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); + COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); + return REAL(pthread_setname_np)(thread, name); +} +#define INIT_PTHREAD_SETNAME_NP COMMON_INTERCEPT_FUNCTION(pthread_setname_np); +#else +#define INIT_PTHREAD_SETNAME_NP +#endif + +#if SANITIZER_INTERCEPT_SINCOS +INTERCEPTOR(void, sincos, double x, double *sin, double *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos); + REAL(sincos)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos); + REAL(sincosf)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos); + REAL(sincosl)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +#define INIT_SINCOS \ + COMMON_INTERCEPT_FUNCTION(sincos); \ + COMMON_INTERCEPT_FUNCTION(sincosf); \ + COMMON_INTERCEPT_FUNCTION(sincosl); +#else +#define INIT_SINCOS +#endif + +#if SANITIZER_INTERCEPT_REMQUO +INTERCEPTOR(double, remquo, double x, double y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo); + double res = REAL(remquo)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +INTERCEPTOR(float, remquof, float x, float y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo); + float res = REAL(remquof)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo); + long double res = REAL(remquol)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +#define INIT_REMQUO \ + COMMON_INTERCEPT_FUNCTION(remquo); \ + COMMON_INTERCEPT_FUNCTION(remquof); \ + COMMON_INTERCEPT_FUNCTION(remquol); +#else +#define INIT_REMQUO +#endif + +#if SANITIZER_INTERCEPT_LGAMMA +extern int signgam; +INTERCEPTOR(double, lgamma, double x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgamma, x); + double res = REAL(lgamma)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +INTERCEPTOR(float, lgammaf, float x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammaf, x); + float res = REAL(lgammaf)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +INTERCEPTOR(long double, lgammal, long double x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x); + long double res = REAL(lgammal)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +#define INIT_LGAMMA \ + COMMON_INTERCEPT_FUNCTION(lgamma); \ + COMMON_INTERCEPT_FUNCTION(lgammaf); \ + COMMON_INTERCEPT_FUNCTION(lgammal); +#else +#define INIT_LGAMMA +#endif + +#if SANITIZER_INTERCEPT_LGAMMA_R +INTERCEPTOR(double, lgamma_r, double x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp); + double res = REAL(lgamma_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +INTERCEPTOR(float, lgammaf_r, float x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp); + float res = REAL(lgammaf_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +INTERCEPTOR(long double, lgammal_r, long double x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp); + long double res = REAL(lgammal_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +#define INIT_LGAMMA_R \ + COMMON_INTERCEPT_FUNCTION(lgamma_r); \ + COMMON_INTERCEPT_FUNCTION(lgammaf_r); \ + COMMON_INTERCEPT_FUNCTION(lgammal_r); +#else +#define INIT_LGAMMA_R +#endif + +#if SANITIZER_INTERCEPT_DRAND48_R +INTERCEPTOR(int, drand48_r, void *buffer, double *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result); + int res = REAL(drand48_r)(buffer, result); + if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +INTERCEPTOR(int, lrand48_r, void *buffer, long *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result); + int res = REAL(lrand48_r)(buffer, result); + if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +#define INIT_DRAND48_R \ + COMMON_INTERCEPT_FUNCTION(drand48_r); \ + COMMON_INTERCEPT_FUNCTION(lrand48_r); +#else +#define INIT_DRAND48_R +#endif + +#if SANITIZER_INTERCEPT_GETLINE +INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream); + SSIZE_T res = REAL(getline)(lineptr, n, stream); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + } + return res; +} +INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, + void *stream) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getdelim, lineptr, n, delim, stream); + SSIZE_T res = REAL(getdelim)(lineptr, n, delim, stream); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + } + return res; +} +#define INIT_GETLINE \ + COMMON_INTERCEPT_FUNCTION(getline); \ + COMMON_INTERCEPT_FUNCTION(getdelim); +#else +#define INIT_GETLINE +#endif + +#define SANITIZER_COMMON_INTERCEPTORS_INIT \ + INIT_STRCMP; \ + INIT_STRNCMP; \ + INIT_STRCASECMP; \ + INIT_STRNCASECMP; \ + INIT_READ; \ + INIT_PREAD; \ + INIT_PREAD64; \ + INIT_READV; \ + INIT_PREADV; \ + INIT_PREADV64; \ + INIT_WRITE; \ + INIT_PWRITE; \ + INIT_PWRITE64; \ + INIT_WRITEV; \ + INIT_PWRITEV; \ + INIT_PWRITEV64; \ + INIT_PRCTL; \ + INIT_LOCALTIME_AND_FRIENDS; \ + INIT_STRPTIME; \ + INIT_SCANF; \ + INIT_ISOC99_SCANF; \ + INIT_FREXP; \ + INIT_FREXPF_FREXPL; \ + INIT_GETPWNAM_AND_FRIENDS; \ + INIT_GETPWNAM_R_AND_FRIENDS; \ + INIT_CLOCK_GETTIME; \ + INIT_GETITIMER; \ + INIT_TIME; \ + INIT_GLOB; \ + INIT_WAIT; \ + INIT_INET; \ + INIT_PTHREAD_GETSCHEDPARAM; \ + INIT_GETADDRINFO; \ + INIT_GETNAMEINFO; \ + INIT_GETSOCKNAME; \ + INIT_GETHOSTBYNAME; \ + INIT_GETHOSTBYNAME_R; \ + INIT_GETSOCKOPT; \ + INIT_ACCEPT; \ + INIT_ACCEPT4; \ + INIT_MODF; \ + INIT_RECVMSG; \ + INIT_GETPEERNAME; \ + INIT_IOCTL; \ + INIT_INET_ATON; \ + INIT_SYSINFO; \ + INIT_READDIR; \ + INIT_READDIR64; \ + INIT_PTRACE; \ + INIT_SETLOCALE; \ + INIT_GETCWD; \ + INIT_GET_CURRENT_DIR_NAME; \ + INIT_STRTOIMAX; \ + INIT_MBSTOWCS; \ + INIT_MBSNRTOWCS; \ + INIT_WCSTOMBS; \ + INIT_WCSNRTOMBS; \ + INIT_TCGETATTR; \ + INIT_REALPATH; \ + INIT_CANONICALIZE_FILE_NAME; \ + INIT_CONFSTR; \ + INIT_SCHED_GETAFFINITY; \ + INIT_STRERROR; \ + INIT_STRERROR_R; \ + INIT_SCANDIR; \ + INIT_SCANDIR64; \ + INIT_GETGROUPS; \ + INIT_POLL; \ + INIT_PPOLL; \ + INIT_WORDEXP; \ + INIT_SIGWAIT; \ + INIT_SIGWAITINFO; \ + INIT_SIGTIMEDWAIT; \ + INIT_SIGSETOPS; \ + INIT_SIGPENDING; \ + INIT_SIGPROCMASK; \ + INIT_BACKTRACE; \ + INIT__EXIT; \ + INIT_PTHREAD_MUTEX_LOCK; \ + INIT_PTHREAD_MUTEX_UNLOCK; \ + INIT_PTHREAD_COND_WAIT; \ + INIT_PTHREAD_COND_INIT; \ + INIT_PTHREAD_COND_SIGNAL; \ + INIT_PTHREAD_COND_BROADCAST; \ + INIT_GETMNTENT; \ + INIT_GETMNTENT_R; \ + INIT_STATFS; \ + INIT_STATFS64; \ + INIT_STATVFS; \ + INIT_STATVFS64; \ + INIT_INITGROUPS; \ + INIT_ETHER; \ + INIT_ETHER_R; \ + INIT_SHMCTL; \ + INIT_RANDOM_R; \ + INIT_PTHREAD_ATTR_GET; \ + INIT_PTHREAD_ATTR_GETINHERITSCHED; \ + INIT_PTHREAD_ATTR_GETAFFINITY_NP; \ + INIT_TMPNAM; \ + INIT_TMPNAM_R; \ + INIT_TEMPNAM; \ + INIT_PTHREAD_SETNAME_NP; \ + INIT_SINCOS; \ + INIT_REMQUO; \ + INIT_LGAMMA; \ + INIT_LGAMMA_R; \ + INIT_DRAND48_R; \ + INIT_GETLINE; \ +/**/ diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc new file mode 100755 index 00000000..4b90f8ca --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -0,0 +1,568 @@ +//===-- sanitizer_common_interceptors_ioctl.inc -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Ioctl handling in common sanitizer interceptors. +//===----------------------------------------------------------------------===// + +#include "sanitizer_flags.h" + +struct ioctl_desc { + unsigned req; + // FIXME: support read+write arguments. Those are currently marked as WRITE. + enum { + NONE, + READ, + WRITE, + CUSTOM + } type : 2; + unsigned size : 30; + const char* name; +}; + +const unsigned ioctl_table_max = 500; +static ioctl_desc ioctl_table[ioctl_table_max]; +static unsigned ioctl_table_size = 0; + +// This can not be declared as a global, because references to struct_*_sz +// require a global initializer. And this table must be available before global +// initializers are run. +static void ioctl_table_fill() { +#define _(rq, tp, sz) \ + if (IOCTL_##rq != IOCTL_NOT_PRESENT) { \ + CHECK(ioctl_table_size < ioctl_table_max); \ + ioctl_table[ioctl_table_size].req = IOCTL_##rq; \ + ioctl_table[ioctl_table_size].type = ioctl_desc::tp; \ + ioctl_table[ioctl_table_size].size = sz; \ + ioctl_table[ioctl_table_size].name = #rq; \ + ++ioctl_table_size; \ + } + + _(FIOASYNC, READ, sizeof(int)); + _(FIOCLEX, NONE, 0); + _(FIOGETOWN, WRITE, sizeof(int)); + _(FIONBIO, READ, sizeof(int)); + _(FIONCLEX, NONE, 0); + _(FIOSETOWN, READ, sizeof(int)); + _(SIOCADDMULTI, READ, struct_ifreq_sz); + _(SIOCATMARK, WRITE, sizeof(int)); + _(SIOCDELMULTI, READ, struct_ifreq_sz); + _(SIOCGIFADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFBRDADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFCONF, CUSTOM, 0); + _(SIOCGIFDSTADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFFLAGS, WRITE, struct_ifreq_sz); + _(SIOCGIFMETRIC, WRITE, struct_ifreq_sz); + _(SIOCGIFMTU, WRITE, struct_ifreq_sz); + _(SIOCGIFNETMASK, WRITE, struct_ifreq_sz); + _(SIOCGPGRP, WRITE, sizeof(int)); + _(SIOCSIFADDR, READ, struct_ifreq_sz); + _(SIOCSIFBRDADDR, READ, struct_ifreq_sz); + _(SIOCSIFDSTADDR, READ, struct_ifreq_sz); + _(SIOCSIFFLAGS, READ, struct_ifreq_sz); + _(SIOCSIFMETRIC, READ, struct_ifreq_sz); + _(SIOCSIFMTU, READ, struct_ifreq_sz); + _(SIOCSIFNETMASK, READ, struct_ifreq_sz); + _(SIOCSPGRP, READ, sizeof(int)); + _(TIOCCONS, NONE, 0); + _(TIOCEXCL, NONE, 0); + _(TIOCGETD, WRITE, sizeof(int)); + _(TIOCGPGRP, WRITE, pid_t_sz); + _(TIOCGWINSZ, WRITE, struct_winsize_sz); + _(TIOCMBIC, READ, sizeof(int)); + _(TIOCMBIS, READ, sizeof(int)); + _(TIOCMGET, WRITE, sizeof(int)); + _(TIOCMSET, READ, sizeof(int)); + _(TIOCNOTTY, NONE, 0); + _(TIOCNXCL, NONE, 0); + _(TIOCOUTQ, WRITE, sizeof(int)); + _(TIOCPKT, READ, sizeof(int)); + _(TIOCSCTTY, NONE, 0); + _(TIOCSETD, READ, sizeof(int)); + _(TIOCSPGRP, READ, pid_t_sz); + _(TIOCSTI, READ, sizeof(char)); + _(TIOCSWINSZ, READ, struct_winsize_sz); + +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) + _(SIOCGETSGCNT, WRITE, struct_sioc_sg_req_sz); + _(SIOCGETVIFCNT, WRITE, struct_sioc_vif_req_sz); +#endif + +#if SANITIZER_LINUX + // Conflicting request ids. + // _(CDROMAUDIOBUFSIZ, NONE, 0); + // _(SNDCTL_TMR_CONTINUE, NONE, 0); + // _(SNDCTL_TMR_START, NONE, 0); + // _(SNDCTL_TMR_STOP, NONE, 0); + // _(SOUND_MIXER_READ_LOUD, WRITE, sizeof(int)); // same as ...READ_ENHANCE + // _(SOUND_MIXER_READ_MUTE, WRITE, sizeof(int)); // same as ...READ_ENHANCE + // _(SOUND_MIXER_WRITE_LOUD, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE + // _(SOUND_MIXER_WRITE_MUTE, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE + _(BLKFLSBUF, NONE, 0); + _(BLKGETSIZE, WRITE, sizeof(uptr)); + _(BLKRAGET, WRITE, sizeof(int)); + _(BLKRASET, NONE, 0); + _(BLKROGET, WRITE, sizeof(int)); + _(BLKROSET, READ, sizeof(int)); + _(BLKRRPART, NONE, 0); + _(CDROMEJECT, NONE, 0); + _(CDROMEJECT_SW, NONE, 0); + _(CDROMMULTISESSION, WRITE, struct_cdrom_multisession_sz); + _(CDROMPAUSE, NONE, 0); + _(CDROMPLAYMSF, READ, struct_cdrom_msf_sz); + _(CDROMPLAYTRKIND, READ, struct_cdrom_ti_sz); + _(CDROMREADAUDIO, READ, struct_cdrom_read_audio_sz); + _(CDROMREADCOOKED, READ, struct_cdrom_msf_sz); + _(CDROMREADMODE1, READ, struct_cdrom_msf_sz); + _(CDROMREADMODE2, READ, struct_cdrom_msf_sz); + _(CDROMREADRAW, READ, struct_cdrom_msf_sz); + _(CDROMREADTOCENTRY, WRITE, struct_cdrom_tocentry_sz); + _(CDROMREADTOCHDR, WRITE, struct_cdrom_tochdr_sz); + _(CDROMRESET, NONE, 0); + _(CDROMRESUME, NONE, 0); + _(CDROMSEEK, READ, struct_cdrom_msf_sz); + _(CDROMSTART, NONE, 0); + _(CDROMSTOP, NONE, 0); + _(CDROMSUBCHNL, WRITE, struct_cdrom_subchnl_sz); + _(CDROMVOLCTRL, READ, struct_cdrom_volctrl_sz); + _(CDROMVOLREAD, WRITE, struct_cdrom_volctrl_sz); + _(CDROM_GET_UPC, WRITE, 8); + _(EVIOCGABS, WRITE, struct_input_absinfo_sz); // fixup + _(EVIOCGBIT, WRITE, struct_input_id_sz); // fixup + _(EVIOCGEFFECTS, WRITE, sizeof(int)); + _(EVIOCGID, WRITE, struct_input_id_sz); + _(EVIOCGKEY, WRITE, 0); + _(EVIOCGKEYCODE, WRITE, sizeof(int) * 2); + _(EVIOCGLED, WRITE, 0); + _(EVIOCGNAME, WRITE, 0); + _(EVIOCGPHYS, WRITE, 0); + _(EVIOCGRAB, READ, sizeof(int)); + _(EVIOCGREP, WRITE, sizeof(int) * 2); + _(EVIOCGSND, WRITE, 0); + _(EVIOCGSW, WRITE, 0); + _(EVIOCGUNIQ, WRITE, 0); + _(EVIOCGVERSION, WRITE, sizeof(int)); + _(EVIOCRMFF, READ, sizeof(int)); + _(EVIOCSABS, READ, struct_input_absinfo_sz); // fixup + _(EVIOCSFF, READ, struct_ff_effect_sz); + _(EVIOCSKEYCODE, READ, sizeof(int) * 2); + _(EVIOCSREP, READ, sizeof(int) * 2); + _(FDCLRPRM, NONE, 0); + _(FDDEFPRM, READ, struct_floppy_struct_sz); + _(FDFLUSH, NONE, 0); + _(FDFMTBEG, NONE, 0); + _(FDFMTEND, NONE, 0); + _(FDFMTTRK, READ, struct_format_descr_sz); + _(FDGETDRVPRM, WRITE, struct_floppy_drive_params_sz); + _(FDGETDRVSTAT, WRITE, struct_floppy_drive_struct_sz); + _(FDGETDRVTYP, WRITE, 16); + _(FDGETFDCSTAT, WRITE, struct_floppy_fdc_state_sz); + _(FDGETMAXERRS, WRITE, struct_floppy_max_errors_sz); + _(FDGETPRM, WRITE, struct_floppy_struct_sz); + _(FDMSGOFF, NONE, 0); + _(FDMSGON, NONE, 0); + _(FDPOLLDRVSTAT, WRITE, struct_floppy_drive_struct_sz); + _(FDRAWCMD, WRITE, struct_floppy_raw_cmd_sz); + _(FDRESET, NONE, 0); + _(FDSETDRVPRM, READ, struct_floppy_drive_params_sz); + _(FDSETEMSGTRESH, NONE, 0); + _(FDSETMAXERRS, READ, struct_floppy_max_errors_sz); + _(FDSETPRM, READ, struct_floppy_struct_sz); + _(FDTWADDLE, NONE, 0); + _(FDWERRORCLR, NONE, 0); + _(FDWERRORGET, WRITE, struct_floppy_write_errors_sz); + _(HDIO_DRIVE_CMD, WRITE, sizeof(int)); + _(HDIO_GETGEO, WRITE, struct_hd_geometry_sz); + _(HDIO_GET_32BIT, WRITE, sizeof(int)); + _(HDIO_GET_DMA, WRITE, sizeof(int)); + _(HDIO_GET_IDENTITY, WRITE, struct_hd_driveid_sz); + _(HDIO_GET_KEEPSETTINGS, WRITE, sizeof(int)); + _(HDIO_GET_MULTCOUNT, WRITE, sizeof(int)); + _(HDIO_GET_NOWERR, WRITE, sizeof(int)); + _(HDIO_GET_UNMASKINTR, WRITE, sizeof(int)); + _(HDIO_SET_32BIT, NONE, 0); + _(HDIO_SET_DMA, NONE, 0); + _(HDIO_SET_KEEPSETTINGS, NONE, 0); + _(HDIO_SET_MULTCOUNT, NONE, 0); + _(HDIO_SET_NOWERR, NONE, 0); + _(HDIO_SET_UNMASKINTR, NONE, 0); + _(MTIOCGET, WRITE, struct_mtget_sz); + _(MTIOCPOS, WRITE, struct_mtpos_sz); + _(MTIOCTOP, READ, struct_mtop_sz); + _(PPPIOCGASYNCMAP, WRITE, sizeof(int)); + _(PPPIOCGDEBUG, WRITE, sizeof(int)); + _(PPPIOCGFLAGS, WRITE, sizeof(int)); + _(PPPIOCGUNIT, WRITE, sizeof(int)); + _(PPPIOCGXASYNCMAP, WRITE, sizeof(int) * 8); + _(PPPIOCSASYNCMAP, READ, sizeof(int)); + _(PPPIOCSDEBUG, READ, sizeof(int)); + _(PPPIOCSFLAGS, READ, sizeof(int)); + _(PPPIOCSMAXCID, READ, sizeof(int)); + _(PPPIOCSMRU, READ, sizeof(int)); + _(PPPIOCSXASYNCMAP, READ, sizeof(int) * 8); + _(SIOCADDRT, READ, struct_rtentry_sz); + _(SIOCDARP, READ, struct_arpreq_sz); + _(SIOCDELRT, READ, struct_rtentry_sz); + _(SIOCDRARP, READ, struct_arpreq_sz); + _(SIOCGARP, WRITE, struct_arpreq_sz); + _(SIOCGIFENCAP, WRITE, sizeof(int)); + _(SIOCGIFHWADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFMAP, WRITE, struct_ifreq_sz); + _(SIOCGIFMEM, WRITE, struct_ifreq_sz); + _(SIOCGIFNAME, NONE, 0); + _(SIOCGIFSLAVE, NONE, 0); + _(SIOCGRARP, WRITE, struct_arpreq_sz); + _(SIOCGSTAMP, WRITE, timeval_sz); + _(SIOCSARP, READ, struct_arpreq_sz); + _(SIOCSIFENCAP, READ, sizeof(int)); + _(SIOCSIFHWADDR, READ, struct_ifreq_sz); + _(SIOCSIFLINK, NONE, 0); + _(SIOCSIFMAP, READ, struct_ifreq_sz); + _(SIOCSIFMEM, READ, struct_ifreq_sz); + _(SIOCSIFSLAVE, NONE, 0); + _(SIOCSRARP, READ, struct_arpreq_sz); + _(SNDCTL_COPR_HALT, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_LOAD, READ, struct_copr_buffer_sz); + _(SNDCTL_COPR_RCODE, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_RCVMSG, WRITE, struct_copr_msg_sz); + _(SNDCTL_COPR_RDATA, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_RESET, NONE, 0); + _(SNDCTL_COPR_RUN, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_SENDMSG, READ, struct_copr_msg_sz); + _(SNDCTL_COPR_WCODE, READ, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_WDATA, READ, struct_copr_debug_buf_sz); + _(SNDCTL_DSP_GETBLKSIZE, WRITE, sizeof(int)); + _(SNDCTL_DSP_GETFMTS, WRITE, sizeof(int)); + _(SNDCTL_DSP_NONBLOCK, NONE, 0); + _(SNDCTL_DSP_POST, NONE, 0); + _(SNDCTL_DSP_RESET, NONE, 0); + _(SNDCTL_DSP_SETFMT, WRITE, sizeof(int)); + _(SNDCTL_DSP_SETFRAGMENT, WRITE, sizeof(int)); + _(SNDCTL_DSP_SPEED, WRITE, sizeof(int)); + _(SNDCTL_DSP_STEREO, WRITE, sizeof(int)); + _(SNDCTL_DSP_SUBDIVIDE, WRITE, sizeof(int)); + _(SNDCTL_DSP_SYNC, NONE, 0); + _(SNDCTL_FM_4OP_ENABLE, READ, sizeof(int)); + _(SNDCTL_FM_LOAD_INSTR, READ, struct_sbi_instrument_sz); + _(SNDCTL_MIDI_INFO, WRITE, struct_midi_info_sz); + _(SNDCTL_MIDI_PRETIME, WRITE, sizeof(int)); + _(SNDCTL_SEQ_CTRLRATE, WRITE, sizeof(int)); + _(SNDCTL_SEQ_GETINCOUNT, WRITE, sizeof(int)); + _(SNDCTL_SEQ_GETOUTCOUNT, WRITE, sizeof(int)); + _(SNDCTL_SEQ_NRMIDIS, WRITE, sizeof(int)); + _(SNDCTL_SEQ_NRSYNTHS, WRITE, sizeof(int)); + _(SNDCTL_SEQ_OUTOFBAND, READ, struct_seq_event_rec_sz); + _(SNDCTL_SEQ_PANIC, NONE, 0); + _(SNDCTL_SEQ_PERCMODE, NONE, 0); + _(SNDCTL_SEQ_RESET, NONE, 0); + _(SNDCTL_SEQ_RESETSAMPLES, READ, sizeof(int)); + _(SNDCTL_SEQ_SYNC, NONE, 0); + _(SNDCTL_SEQ_TESTMIDI, READ, sizeof(int)); + _(SNDCTL_SEQ_THRESHOLD, READ, sizeof(int)); + _(SNDCTL_SYNTH_INFO, WRITE, struct_synth_info_sz); + _(SNDCTL_SYNTH_MEMAVL, WRITE, sizeof(int)); + _(SNDCTL_TMR_METRONOME, READ, sizeof(int)); + _(SNDCTL_TMR_SELECT, WRITE, sizeof(int)); + _(SNDCTL_TMR_SOURCE, WRITE, sizeof(int)); + _(SNDCTL_TMR_TEMPO, WRITE, sizeof(int)); + _(SNDCTL_TMR_TIMEBASE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_ALTPCM, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_BASS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_CAPS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_CD, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_DEVMASK, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_ENHANCE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_IGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_IMIX, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE1, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE2, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE3, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_MIC, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_OGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_PCM, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECLEV, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECMASK, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECSRC, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_SPEAKER, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_STEREODEVS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_SYNTH, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_TREBLE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_VOLUME, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_ALTPCM, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_BASS, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_CD, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_ENHANCE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_IGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_IMIX, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE1, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE2, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE3, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_MIC, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_OGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_PCM, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_RECLEV, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_RECSRC, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_SPEAKER, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_SYNTH, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_TREBLE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_VOLUME, WRITE, sizeof(int)); + _(SOUND_PCM_READ_BITS, WRITE, sizeof(int)); + _(SOUND_PCM_READ_CHANNELS, WRITE, sizeof(int)); + _(SOUND_PCM_READ_FILTER, WRITE, sizeof(int)); + _(SOUND_PCM_READ_RATE, WRITE, sizeof(int)); + _(SOUND_PCM_WRITE_CHANNELS, WRITE, sizeof(int)); + _(SOUND_PCM_WRITE_FILTER, WRITE, sizeof(int)); + _(TCFLSH, NONE, 0); + _(TCGETA, WRITE, struct_termio_sz); + _(TCGETS, WRITE, struct_termios_sz); + _(TCSBRK, NONE, 0); + _(TCSBRKP, NONE, 0); + _(TCSETA, READ, struct_termio_sz); + _(TCSETAF, READ, struct_termio_sz); + _(TCSETAW, READ, struct_termio_sz); + _(TCSETS, READ, struct_termios_sz); + _(TCSETSF, READ, struct_termios_sz); + _(TCSETSW, READ, struct_termios_sz); + _(TCXONC, NONE, 0); + _(TIOCGLCKTRMIOS, WRITE, struct_termios_sz); + _(TIOCGSOFTCAR, WRITE, sizeof(int)); + _(TIOCINQ, WRITE, sizeof(int)); + _(TIOCLINUX, READ, sizeof(char)); + _(TIOCSERCONFIG, NONE, 0); + _(TIOCSERGETLSR, WRITE, sizeof(int)); + _(TIOCSERGWILD, WRITE, sizeof(int)); + _(TIOCSERSWILD, READ, sizeof(int)); + _(TIOCSLCKTRMIOS, READ, struct_termios_sz); + _(TIOCSSOFTCAR, READ, sizeof(int)); + _(VT_ACTIVATE, NONE, 0); + _(VT_DISALLOCATE, NONE, 0); + _(VT_GETMODE, WRITE, struct_vt_mode_sz); + _(VT_GETSTATE, WRITE, struct_vt_stat_sz); + _(VT_OPENQRY, WRITE, sizeof(int)); + _(VT_RELDISP, NONE, 0); + _(VT_RESIZE, READ, struct_vt_sizes_sz); + _(VT_RESIZEX, READ, struct_vt_consize_sz); + _(VT_SENDSIG, NONE, 0); + _(VT_SETMODE, READ, struct_vt_mode_sz); + _(VT_WAITACTIVE, NONE, 0); +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + // _(SIOCDEVPLIP, WRITE, struct_ifreq_sz); // the same as EQL_ENSLAVE + _(CYGETDEFTHRESH, WRITE, sizeof(int)); + _(CYGETDEFTIMEOUT, WRITE, sizeof(int)); + _(CYGETMON, WRITE, struct_cyclades_monitor_sz); + _(CYGETTHRESH, WRITE, sizeof(int)); + _(CYGETTIMEOUT, WRITE, sizeof(int)); + _(CYSETDEFTHRESH, NONE, 0); + _(CYSETDEFTIMEOUT, NONE, 0); + _(CYSETTHRESH, NONE, 0); + _(CYSETTIMEOUT, NONE, 0); + _(EQL_EMANCIPATE, WRITE, struct_ifreq_sz); + _(EQL_ENSLAVE, WRITE, struct_ifreq_sz); + _(EQL_GETMASTRCFG, WRITE, struct_ifreq_sz); + _(EQL_GETSLAVECFG, WRITE, struct_ifreq_sz); + _(EQL_SETMASTRCFG, WRITE, struct_ifreq_sz); + _(EQL_SETSLAVECFG, WRITE, struct_ifreq_sz); + _(EVIOCGKEYCODE_V2, WRITE, struct_input_keymap_entry_sz); + _(EVIOCGPROP, WRITE, 0); + _(EVIOCSKEYCODE_V2, READ, struct_input_keymap_entry_sz); + _(FS_IOC_GETFLAGS, WRITE, sizeof(int)); + _(FS_IOC_GETVERSION, WRITE, sizeof(int)); + _(FS_IOC_SETFLAGS, READ, sizeof(int)); + _(FS_IOC_SETVERSION, READ, sizeof(int)); + _(GIO_CMAP, WRITE, 48); + _(GIO_FONT, WRITE, 8192); + _(GIO_SCRNMAP, WRITE, e_tabsz); + _(GIO_UNIMAP, WRITE, struct_unimapdesc_sz); + _(GIO_UNISCRNMAP, WRITE, sizeof(short) * e_tabsz); + _(KDADDIO, NONE, 0); + _(KDDELIO, NONE, 0); + _(KDDISABIO, NONE, 0); + _(KDENABIO, NONE, 0); + _(KDGETKEYCODE, WRITE, struct_kbkeycode_sz); + _(KDGETLED, WRITE, 1); + _(KDGETMODE, WRITE, sizeof(int)); + _(KDGKBDIACR, WRITE, struct_kbdiacrs_sz); + _(KDGKBENT, WRITE, struct_kbentry_sz); + _(KDGKBLED, WRITE, sizeof(int)); + _(KDGKBMETA, WRITE, sizeof(int)); + _(KDGKBMODE, WRITE, sizeof(int)); + _(KDGKBSENT, WRITE, struct_kbsentry_sz); + _(KDGKBTYPE, WRITE, 1); + _(KDMAPDISP, NONE, 0); + _(KDMKTONE, NONE, 0); + _(KDSETKEYCODE, READ, struct_kbkeycode_sz); + _(KDSETLED, NONE, 0); + _(KDSETMODE, NONE, 0); + _(KDSIGACCEPT, NONE, 0); + _(KDSKBDIACR, READ, struct_kbdiacrs_sz); + _(KDSKBENT, READ, struct_kbentry_sz); + _(KDSKBLED, NONE, 0); + _(KDSKBMETA, NONE, 0); + _(KDSKBMODE, NONE, 0); + _(KDSKBSENT, READ, struct_kbsentry_sz); + _(KDUNMAPDISP, NONE, 0); + _(KIOCSOUND, NONE, 0); + _(LPABORT, NONE, 0); + _(LPABORTOPEN, NONE, 0); + _(LPCAREFUL, NONE, 0); + _(LPCHAR, NONE, 0); + _(LPGETIRQ, WRITE, sizeof(int)); + _(LPGETSTATUS, WRITE, sizeof(int)); + _(LPRESET, NONE, 0); + _(LPSETIRQ, NONE, 0); + _(LPTIME, NONE, 0); + _(LPWAIT, NONE, 0); + _(MTIOCGETCONFIG, WRITE, struct_mtconfiginfo_sz); + _(MTIOCSETCONFIG, READ, struct_mtconfiginfo_sz); + _(PIO_CMAP, NONE, 0); + _(PIO_FONT, READ, 8192); + _(PIO_SCRNMAP, READ, e_tabsz); + _(PIO_UNIMAP, READ, struct_unimapdesc_sz); + _(PIO_UNIMAPCLR, READ, struct_unimapinit_sz); + _(PIO_UNISCRNMAP, READ, sizeof(short) * e_tabsz); + _(SCSI_IOCTL_PROBE_HOST, READ, sizeof(int)); + _(SCSI_IOCTL_TAGGED_DISABLE, NONE, 0); + _(SCSI_IOCTL_TAGGED_ENABLE, NONE, 0); + _(SNDCTL_DSP_GETISPACE, WRITE, struct_audio_buf_info_sz); + _(SNDCTL_DSP_GETOSPACE, WRITE, struct_audio_buf_info_sz); + _(TIOCGSERIAL, WRITE, struct_serial_struct_sz); + _(TIOCSERGETMULTI, WRITE, struct_serial_multiport_struct_sz); + _(TIOCSERSETMULTI, READ, struct_serial_multiport_struct_sz); + _(TIOCSSERIAL, READ, struct_serial_struct_sz); + + // The following ioctl requests are shared between AX25, IPX, netrom and + // mrouted. + // _(SIOCAIPXITFCRT, READ, sizeof(char)); + // _(SIOCAX25GETUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCNRGETPARMS, WRITE, struct_nr_parms_struct_sz); + // _(SIOCAIPXPRISLT, READ, sizeof(char)); + // _(SIOCNRSETPARMS, READ, struct_nr_parms_struct_sz); + // _(SIOCAX25ADDUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCNRDECOBS, NONE, 0); + // _(SIOCAX25DELUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCIPXCFGDATA, WRITE, struct_ipx_config_data_sz); + // _(SIOCAX25NOUID, READ, sizeof(int)); + // _(SIOCNRRTCTL, READ, sizeof(int)); + // _(SIOCAX25DIGCTL, READ, sizeof(int)); + // _(SIOCAX25GETPARMS, WRITE, struct_ax25_parms_struct_sz); + // _(SIOCAX25SETPARMS, READ, struct_ax25_parms_struct_sz); +#endif +#undef _ +} + +static bool ioctl_initialized = false; + +struct ioctl_desc_compare { + bool operator()(const ioctl_desc& left, const ioctl_desc& right) const { + return left.req < right.req; + } +}; + +static void ioctl_init() { + ioctl_table_fill(); + InternalSort(&ioctl_table, ioctl_table_size, ioctl_desc_compare()); + + bool bad = false; + for (unsigned i = 0; i < ioctl_table_size - 1; ++i) { + if (ioctl_table[i].req >= ioctl_table[i + 1].req) { + Printf("Duplicate or unsorted ioctl request id %x >= %x (%s vs %s)\n", + ioctl_table[i].req, ioctl_table[i + 1].req, ioctl_table[i].name, + ioctl_table[i + 1].name); + bad = true; + } + } + + if (bad) Die(); + + ioctl_initialized = true; +} + +// Handle the most evil ioctls that encode argument value as part of request id. +static unsigned ioctl_request_fixup(unsigned req) { +#if SANITIZER_LINUX + if ((req & ~0x3fff001fU) == IOCTL_EVIOCGBIT) + return IOCTL_EVIOCGBIT; + if ((req & ~0x3fU) == IOCTL_EVIOCGABS) + return IOCTL_EVIOCGABS; + if ((req & ~0x3fU) == IOCTL_EVIOCSABS) + return IOCTL_EVIOCSABS; +#endif + return req; +} + +static const ioctl_desc *ioctl_table_lookup(unsigned req) { + int left = 0; + int right = ioctl_table_size; + while (left < right) { + int mid = (left + right) / 2; + if (ioctl_table[mid].req < req) + left = mid + 1; + else + right = mid; + } + if (left == right && ioctl_table[left].req == req) + return ioctl_table + left; + else + return 0; +} + +static const ioctl_desc *ioctl_lookup(unsigned req) { + req = ioctl_request_fixup(req); + const ioctl_desc *desc = ioctl_table_lookup(req); + if (desc) return desc; + + // Try stripping access size from the request id. + desc = ioctl_table_lookup(req & ~0x3fff0000U); + // Sanity check: requests that encode access size are either read or write and + // have size of 0 in the table. + if (desc && desc->size == 0 && + (desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READ)) + return desc; + return 0; +} + +static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d, + unsigned request, void *arg) { + if (desc->type == ioctl_desc::READ) { + unsigned size = desc->size ? desc->size : IOC_SIZE(request); + COMMON_INTERCEPTOR_READ_RANGE(ctx, arg, size); + } + if (desc->type != ioctl_desc::CUSTOM) + return; + switch (request) { + case 0x00008912: { // SIOCGIFCONF + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len)); + break; + } + } + return; +} + +static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d, + unsigned request, void *arg) { + if (desc->type == ioctl_desc::WRITE) { + // FIXME: add verbose output + unsigned size = desc->size ? desc->size : IOC_SIZE(request); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg, size); + } + if (desc->type != ioctl_desc::CUSTOM) + return; + switch (request) { + case 0x00008912: { // SIOCGIFCONF + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len); + break; + } + } + return; +} diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc new file mode 100644 index 00000000..08752e6a --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -0,0 +1,311 @@ +//===-- sanitizer_common_interceptors_scanf.inc -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Scanf implementation for use in *Sanitizer interceptors. +// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html +// with a few common GNU extensions. +// +//===----------------------------------------------------------------------===// +#include + +struct ScanfDirective { + int argIdx; // argument index, or -1 of not specified ("%n$") + int fieldWidth; + bool suppressed; // suppress assignment ("*") + bool allocate; // allocate space ("m") + char lengthModifier[2]; + char convSpecifier; + bool maybeGnuMalloc; +}; + +static const char *parse_number(const char *p, int *out) { + *out = internal_atoll(p); + while (*p >= '0' && *p <= '9') + ++p; + return p; +} + +static bool char_is_one_of(char c, const char *s) { + return !!internal_strchr(s, c); +} + +// Parse scanf format string. If a valid directive in encountered, it is +// returned in dir. This function returns the pointer to the first +// unprocessed character, or 0 in case of error. +// In case of the end-of-string, a pointer to the closing \0 is returned. +static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, + ScanfDirective *dir) { + internal_memset(dir, 0, sizeof(*dir)); + dir->argIdx = -1; + + while (*p) { + if (*p != '%') { + ++p; + continue; + } + ++p; + // %% + if (*p == '%') { + ++p; + continue; + } + if (*p == '\0') { + return 0; + } + // %n$ + if (*p >= '0' && *p <= '9') { + int number; + const char *q = parse_number(p, &number); + if (*q == '$') { + dir->argIdx = number; + p = q + 1; + } + // Otherwise, do not change p. This will be re-parsed later as the field + // width. + } + // * + if (*p == '*') { + dir->suppressed = true; + ++p; + } + // Field width. + if (*p >= '0' && *p <= '9') { + p = parse_number(p, &dir->fieldWidth); + if (dir->fieldWidth <= 0) + return 0; + } + // m + if (*p == 'm') { + dir->allocate = true; + ++p; + } + // Length modifier. + if (char_is_one_of(*p, "jztLq")) { + dir->lengthModifier[0] = *p; + ++p; + } else if (*p == 'h') { + dir->lengthModifier[0] = 'h'; + ++p; + if (*p == 'h') { + dir->lengthModifier[1] = 'h'; + ++p; + } + } else if (*p == 'l') { + dir->lengthModifier[0] = 'l'; + ++p; + if (*p == 'l') { + dir->lengthModifier[1] = 'l'; + ++p; + } + } + // Conversion specifier. + dir->convSpecifier = *p++; + // Consume %[...] expression. + if (dir->convSpecifier == '[') { + if (*p == '^') + ++p; + if (*p == ']') + ++p; + while (*p && *p != ']') + ++p; + if (*p == 0) + return 0; // unexpected end of string + // Consume the closing ']'. + ++p; + } + // This is unfortunately ambiguous between old GNU extension + // of %as, %aS and %a[...] and newer POSIX %a followed by + // letters s, S or [. + if (allowGnuMalloc && dir->convSpecifier == 'a' && + !dir->lengthModifier[0]) { + if (*p == 's' || *p == 'S') { + dir->maybeGnuMalloc = true; + ++p; + } else if (*p == '[') { + // Watch for %a[h-j%d], if % appears in the + // [...] range, then we need to give up, we don't know + // if scanf will parse it as POSIX %a [h-j %d ] or + // GNU allocation of string with range dh-j plus %. + const char *q = p + 1; + if (*q == '^') + ++q; + if (*q == ']') + ++q; + while (*q && *q != ']' && *q != '%') + ++q; + if (*q == 0 || *q == '%') + return 0; + p = q + 1; // Consume the closing ']'. + dir->maybeGnuMalloc = true; + } + } + break; + } + return p; +} + +// Returns true if the character is an integer conversion specifier. +static bool scanf_is_integer_conv(char c) { + return char_is_one_of(c, "diouxXn"); +} + +// Returns true if the character is an floating point conversion specifier. +static bool scanf_is_float_conv(char c) { + return char_is_one_of(c, "aAeEfFgG"); +} + +// Returns string output character size for string-like conversions, +// or 0 if the conversion is invalid. +static int scanf_get_char_size(ScanfDirective *dir) { + if (char_is_one_of(dir->convSpecifier, "CS")) { + // wchar_t + return 0; + } + + if (char_is_one_of(dir->convSpecifier, "cs[")) { + if (dir->lengthModifier[0] == 'l') + // wchar_t + return 0; + else if (dir->lengthModifier[0] == 0) + return sizeof(char); + else + return 0; + } + + return 0; +} + +enum ScanfStoreSize { + // Store size not known in advance; can be calculated as strlen() of the + // destination buffer. + SSS_STRLEN = -1, + // Invalid conversion specifier. + SSS_INVALID = 0 +}; + +// Returns the store size of a scanf directive (if >0), or a value of +// ScanfStoreSize. +static int scanf_get_store_size(ScanfDirective *dir) { + if (dir->allocate) { + if (!char_is_one_of(dir->convSpecifier, "cCsS[")) + return SSS_INVALID; + return sizeof(char *); + } + + if (dir->maybeGnuMalloc) { + if (dir->convSpecifier != 'a' || dir->lengthModifier[0]) + return SSS_INVALID; + // This is ambiguous, so check the smaller size of char * (if it is + // a GNU extension of %as, %aS or %a[...]) and float (if it is + // POSIX %a followed by s, S or [ letters). + return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float); + } + + if (scanf_is_integer_conv(dir->convSpecifier)) { + switch (dir->lengthModifier[0]) { + case 'h': + return dir->lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short); + case 'l': + return dir->lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long); + case 'L': + return sizeof(long long); + case 'j': + return sizeof(INTMAX_T); + case 'z': + return sizeof(SIZE_T); + case 't': + return sizeof(PTRDIFF_T); + case 0: + return sizeof(int); + default: + return SSS_INVALID; + } + } + + if (scanf_is_float_conv(dir->convSpecifier)) { + switch (dir->lengthModifier[0]) { + case 'L': + case 'q': + return sizeof(long double); + case 'l': + return dir->lengthModifier[1] == 'l' ? sizeof(long double) + : sizeof(double); + case 0: + return sizeof(float); + default: + return SSS_INVALID; + } + } + + if (char_is_one_of(dir->convSpecifier, "sS[")) { + unsigned charSize = scanf_get_char_size(dir); + if (charSize == 0) + return SSS_INVALID; + if (dir->fieldWidth == 0) + return SSS_STRLEN; + return (dir->fieldWidth + 1) * charSize; + } + + if (char_is_one_of(dir->convSpecifier, "cC")) { + unsigned charSize = scanf_get_char_size(dir); + if (charSize == 0) + return SSS_INVALID; + if (dir->fieldWidth == 0) + return charSize; + return dir->fieldWidth * charSize; + } + + if (dir->convSpecifier == 'p') { + if (dir->lengthModifier[1] != 0) + return SSS_INVALID; + return sizeof(void *); + } + + return SSS_INVALID; +} + +// Common part of *scanf interceptors. +// Process format string and va_list, and report all store ranges. +// Stops when "consuming" n_inputs input items. +static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, + const char *format, va_list aq) { + CHECK_GT(n_inputs, 0); + const char *p = format; + + while (*p) { + ScanfDirective dir; + p = scanf_parse_next(p, allowGnuMalloc, &dir); + if (!p) + break; + if (dir.convSpecifier == 0) { + // This can only happen at the end of the format string. + CHECK_EQ(*p, 0); + break; + } + // Here the directive is valid. Do what it says. + if (dir.argIdx != -1) { + // Unsupported. + break; + } + if (dir.suppressed) + continue; + int size = scanf_get_store_size(&dir); + if (size == SSS_INVALID) + break; + void *argp = va_arg(aq, void *); + if (dir.convSpecifier != 'n') + --n_inputs; + if (n_inputs < 0) + break; + if (size == SSS_STRLEN) { + size = internal_strlen((const char *)argp) + 1; + } + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); + } +} diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc new file mode 100644 index 00000000..f3430074 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -0,0 +1,37 @@ +//===-- sanitizer_common_libcdep.cc ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" + +namespace __sanitizer { + +bool PrintsToTty() { + MaybeOpenReportFile(); + return internal_isatty(report_fd) != 0; +} + +bool PrintsToTtyCached() { + // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color + // printing on Windows. + if (SANITIZER_WINDOWS) + return 0; + + static int cached = 0; + static bool prints_to_tty; + if (!cached) { // Not thread-safe. + prints_to_tty = PrintsToTty(); + cached = 1; + } + return prints_to_tty; +} +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc new file mode 100644 index 00000000..958f12f8 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -0,0 +1,2787 @@ +//===-- sanitizer_common_syscalls.inc ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Common syscalls handlers for tools like AddressSanitizer, +// ThreadSanitizer, MemorySanitizer, etc. +// +// This file should be included into the tool's interceptor file, +// which has to define it's own macros: +// COMMON_SYSCALL_PRE_READ_RANGE +// Called in prehook for regions that will be read by the kernel and +// must be initialized. +// COMMON_SYSCALL_PRE_WRITE_RANGE +// Called in prehook for regions that will be written to by the kernel +// and must be addressable. The actual write range may be smaller than +// reported in the prehook. See POST_WRITE_RANGE. +// COMMON_SYSCALL_POST_READ_RANGE +// Called in posthook for regions that were read by the kernel. Does +// not make much sense. +// COMMON_SYSCALL_POST_WRITE_RANGE +// Called in posthook for regions that were written to by the kernel +// and are now initialized. +// COMMON_SYSCALL_FD_CLOSE(fd) +// Called before closing file descriptor fd. +// COMMON_SYSCALL_PRE_FORK() +// Called before fork syscall. +// COMMON_SYSCALL_POST_FORK(long res) +// Called after fork syscall. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_libc.h" + +#define PRE_SYSCALL(name) \ + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name +#define PRE_READ(p, s) COMMON_SYSCALL_PRE_READ_RANGE(p, s) +#define PRE_WRITE(p, s) COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) + +#define POST_SYSCALL(name) \ + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_post_impl_##name +#define POST_READ(p, s) COMMON_SYSCALL_POST_READ_RANGE(p, s) +#define POST_WRITE(p, s) COMMON_SYSCALL_POST_WRITE_RANGE(p, s) + +#ifndef COMMON_SYSCALL_FD_CLOSE +# define COMMON_SYSCALL_FD_CLOSE(fd) +#endif + +#ifndef COMMON_SYSCALL_PRE_FORK +# define COMMON_SYSCALL_PRE_FORK() +#endif + +#ifndef COMMON_SYSCALL_POST_FORK +# define COMMON_SYSCALL_POST_FORK(res) +#endif + +// FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such). + +extern "C" { +struct sanitizer_kernel_iovec { + void *iov_base; + unsigned long iov_len; +}; + +struct sanitizer_kernel_msghdr { + void *msg_name; + int msg_namelen; + struct sanitizer_kernel_iovec *msg_iov; + unsigned long msg_iovlen; + void *msg_control; + unsigned long msg_controllen; + unsigned msg_flags; +}; + +struct sanitizer_kernel_mmsghdr { + struct sanitizer_kernel_msghdr msg_hdr; + unsigned msg_len; +}; + +struct sanitizer_kernel_timespec { + long tv_sec; + long tv_nsec; +}; + +struct sanitizer_kernel_timeval { + long tv_sec; + long tv_usec; +}; + +struct sanitizer_kernel_rusage { + struct sanitizer_kernel_timeval ru_timeval[2]; + long ru_long[14]; +}; + +struct sanitizer_kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +// Real sigset size is always passed as a syscall argument. +// Declare it "void" to catch sizeof(kernel_sigset_t). +typedef void kernel_sigset_t; + +static void kernel_write_iovec(const __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + POST_WRITE(iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +// This functions uses POST_READ, because it needs to run after syscall to know +// the real read range. +static void kernel_read_iovec(const __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + POST_READ(iovec, sizeof(*iovec) * iovlen); + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + POST_READ(iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +PRE_SYSCALL(recvmsg)(long sockfd, sanitizer_kernel_msghdr *msg, long flags) { + PRE_READ(msg, sizeof(*msg)); +} + +POST_SYSCALL(recvmsg)(long res, long sockfd, sanitizer_kernel_msghdr *msg, + long flags) { + if (res >= 0) { + if (msg) { + for (unsigned long i = 0; i < msg->msg_iovlen; ++i) { + POST_WRITE(msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); + } + POST_WRITE(msg->msg_control, msg->msg_controllen); + } + } +} + +PRE_SYSCALL(recvmmsg)(long fd, sanitizer_kernel_mmsghdr *msg, long vlen, + long flags, void *timeout) { + PRE_READ(msg, vlen * sizeof(*msg)); +} + +POST_SYSCALL(recvmmsg)(long res, long fd, sanitizer_kernel_mmsghdr *msg, + long vlen, long flags, void *timeout) { + if (res >= 0) { + if (msg) { + for (unsigned long i = 0; i < msg->msg_hdr.msg_iovlen; ++i) { + POST_WRITE(msg->msg_hdr.msg_iov[i].iov_base, + msg->msg_hdr.msg_iov[i].iov_len); + } + POST_WRITE(msg->msg_hdr.msg_control, msg->msg_hdr.msg_controllen); + POST_WRITE(&msg->msg_len, sizeof(msg->msg_len)); + } + if (timeout) POST_WRITE(timeout, struct_timespec_sz); + } +} + +PRE_SYSCALL(read)(long fd, void *buf, uptr count) { + if (buf) { + PRE_WRITE(buf, count); + } +} + +POST_SYSCALL(read)(long res, long fd, void *buf, uptr count) { + if (res > 0 && buf) { + POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(time)(void *tloc) {} + +POST_SYSCALL(time)(long res, void *tloc) { + if (res >= 0) { + if (tloc) POST_WRITE(tloc, sizeof(long)); + } +} + +PRE_SYSCALL(stime)(void *tptr) {} + +POST_SYSCALL(stime)(long res, void *tptr) { + if (res >= 0) { + if (tptr) POST_WRITE(tptr, sizeof(long)); + } +} + +PRE_SYSCALL(gettimeofday)(void *tv, void *tz) {} + +POST_SYSCALL(gettimeofday)(long res, void *tv, void *tz) { + if (res >= 0) { + if (tv) POST_WRITE(tv, timeval_sz); + if (tz) POST_WRITE(tz, struct_timezone_sz); + } +} + +PRE_SYSCALL(settimeofday)(void *tv, void *tz) {} + +POST_SYSCALL(settimeofday)(long res, void *tv, void *tz) { + if (res >= 0) { + if (tv) POST_WRITE(tv, timeval_sz); + if (tz) POST_WRITE(tz, struct_timezone_sz); + } +} + +PRE_SYSCALL(adjtimex)(void *txc_p) {} + +POST_SYSCALL(adjtimex)(long res, void *txc_p) { + if (res >= 0) { + if (txc_p) POST_WRITE(txc_p, struct_timex_sz); + } +} + +PRE_SYSCALL(times)(void *tbuf) {} + +POST_SYSCALL(times)(long res, void *tbuf) { + if (res >= 0) { + if (tbuf) POST_WRITE(tbuf, struct_tms_sz); + } +} + +PRE_SYSCALL(gettid)() {} + +POST_SYSCALL(gettid)(long res) {} + +PRE_SYSCALL(nanosleep)(void *rqtp, void *rmtp) {} + +POST_SYSCALL(nanosleep)(long res, void *rqtp, void *rmtp) { + if (res >= 0) { + if (rqtp) POST_WRITE(rqtp, struct_timespec_sz); + if (rmtp) POST_WRITE(rmtp, struct_timespec_sz); + } +} + +PRE_SYSCALL(alarm)(long seconds) {} + +POST_SYSCALL(alarm)(long res, long seconds) {} + +PRE_SYSCALL(getpid)() {} + +POST_SYSCALL(getpid)(long res) {} + +PRE_SYSCALL(getppid)() {} + +POST_SYSCALL(getppid)(long res) {} + +PRE_SYSCALL(getuid)() {} + +POST_SYSCALL(getuid)(long res) {} + +PRE_SYSCALL(geteuid)() {} + +POST_SYSCALL(geteuid)(long res) {} + +PRE_SYSCALL(getgid)() {} + +POST_SYSCALL(getgid)(long res) {} + +PRE_SYSCALL(getegid)() {} + +POST_SYSCALL(getegid)(long res) {} + +PRE_SYSCALL(getresuid)(void *ruid, void *euid, void *suid) {} + +POST_SYSCALL(getresuid)(long res, void *ruid, void *euid, void *suid) { + if (res >= 0) { + if (ruid) POST_WRITE(ruid, sizeof(unsigned)); + if (euid) POST_WRITE(euid, sizeof(unsigned)); + if (suid) POST_WRITE(suid, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getresgid)(void *rgid, void *egid, void *sgid) {} + +POST_SYSCALL(getresgid)(long res, void *rgid, void *egid, void *sgid) { + if (res >= 0) { + if (rgid) POST_WRITE(rgid, sizeof(unsigned)); + if (egid) POST_WRITE(egid, sizeof(unsigned)); + if (sgid) POST_WRITE(sgid, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getpgid)(long pid) {} + +POST_SYSCALL(getpgid)(long res, long pid) {} + +PRE_SYSCALL(getpgrp)() {} + +POST_SYSCALL(getpgrp)(long res) {} + +PRE_SYSCALL(getsid)(long pid) {} + +POST_SYSCALL(getsid)(long res, long pid) {} + +PRE_SYSCALL(getgroups)(long gidsetsize, void *grouplist) {} + +POST_SYSCALL(getgroups)(long res, long gidsetsize, + __sanitizer___kernel_gid_t *grouplist) { + if (res >= 0) { + if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist)); + } +} + +PRE_SYSCALL(setregid)(long rgid, long egid) {} + +POST_SYSCALL(setregid)(long res, long rgid, long egid) {} + +PRE_SYSCALL(setgid)(long gid) {} + +POST_SYSCALL(setgid)(long res, long gid) {} + +PRE_SYSCALL(setreuid)(long ruid, long euid) {} + +POST_SYSCALL(setreuid)(long res, long ruid, long euid) {} + +PRE_SYSCALL(setuid)(long uid) {} + +POST_SYSCALL(setuid)(long res, long uid) {} + +PRE_SYSCALL(setresuid)(long ruid, long euid, long suid) {} + +POST_SYSCALL(setresuid)(long res, long ruid, long euid, long suid) {} + +PRE_SYSCALL(setresgid)(long rgid, long egid, long sgid) {} + +POST_SYSCALL(setresgid)(long res, long rgid, long egid, long sgid) {} + +PRE_SYSCALL(setfsuid)(long uid) {} + +POST_SYSCALL(setfsuid)(long res, long uid) {} + +PRE_SYSCALL(setfsgid)(long gid) {} + +POST_SYSCALL(setfsgid)(long res, long gid) {} + +PRE_SYSCALL(setpgid)(long pid, long pgid) {} + +POST_SYSCALL(setpgid)(long res, long pid, long pgid) {} + +PRE_SYSCALL(setsid)() {} + +POST_SYSCALL(setsid)(long res) {} + +PRE_SYSCALL(setgroups)(long gidsetsize, __sanitizer___kernel_gid_t *grouplist) { + if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist)); +} + +POST_SYSCALL(setgroups)(long res, long gidsetsize, + __sanitizer___kernel_gid_t *grouplist) {} + +PRE_SYSCALL(acct)(const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(acct)(long res, const void *name) {} + +PRE_SYSCALL(capget)(void *header, void *dataptr) {} + +POST_SYSCALL(capget)(long res, void *header, void *dataptr) { + if (res >= 0) { + if (header) POST_WRITE(header, __user_cap_header_struct_sz); + if (dataptr) POST_WRITE(dataptr, __user_cap_data_struct_sz); + } +} + +PRE_SYSCALL(capset)(void *header, const void *data) { + if (data) PRE_READ(data, __user_cap_data_struct_sz); +} + +POST_SYSCALL(capset)(long res, void *header, const void *data) { + if (res >= 0) { + if (header) POST_WRITE(header, __user_cap_header_struct_sz); + } +} + +PRE_SYSCALL(personality)(long personality) {} + +POST_SYSCALL(personality)(long res, long personality) {} + +PRE_SYSCALL(sigpending)(void *set) {} + +POST_SYSCALL(sigpending)(long res, void *set) { + if (res >= 0) { + if (set) POST_WRITE(set, old_sigset_t_sz); + } +} + +PRE_SYSCALL(sigprocmask)(long how, void *set, void *oset) {} + +POST_SYSCALL(sigprocmask)(long res, long how, void *set, void *oset) { + if (res >= 0) { + if (set) POST_WRITE(set, old_sigset_t_sz); + if (oset) POST_WRITE(oset, old_sigset_t_sz); + } +} + +PRE_SYSCALL(getitimer)(long which, void *value) {} + +POST_SYSCALL(getitimer)(long res, long which, void *value) { + if (res >= 0) { + if (value) POST_WRITE(value, struct_itimerval_sz); + } +} + +PRE_SYSCALL(setitimer)(long which, void *value, void *ovalue) {} + +POST_SYSCALL(setitimer)(long res, long which, void *value, void *ovalue) { + if (res >= 0) { + if (value) POST_WRITE(value, struct_itimerval_sz); + if (ovalue) POST_WRITE(ovalue, struct_itimerval_sz); + } +} + +PRE_SYSCALL(timer_create)(long which_clock, void *timer_event_spec, + void *created_timer_id) {} + +POST_SYSCALL(timer_create)(long res, long which_clock, void *timer_event_spec, + void *created_timer_id) { + if (res >= 0) { + if (timer_event_spec) POST_WRITE(timer_event_spec, struct_sigevent_sz); + if (created_timer_id) POST_WRITE(created_timer_id, sizeof(long)); + } +} + +PRE_SYSCALL(timer_gettime)(long timer_id, void *setting) {} + +POST_SYSCALL(timer_gettime)(long res, long timer_id, void *setting) { + if (res >= 0) { + if (setting) POST_WRITE(setting, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(timer_getoverrun)(long timer_id) {} + +POST_SYSCALL(timer_getoverrun)(long res, long timer_id) {} + +PRE_SYSCALL(timer_settime)(long timer_id, long flags, const void *new_setting, + void *old_setting) { + if (new_setting) PRE_READ(new_setting, struct_itimerspec_sz); +} + +POST_SYSCALL(timer_settime)(long res, long timer_id, long flags, + const void *new_setting, void *old_setting) { + if (res >= 0) { + if (old_setting) POST_WRITE(old_setting, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(timer_delete)(long timer_id) {} + +POST_SYSCALL(timer_delete)(long res, long timer_id) {} + +PRE_SYSCALL(clock_settime)(long which_clock, const void *tp) { + if (tp) PRE_READ(tp, struct_timespec_sz); +} + +POST_SYSCALL(clock_settime)(long res, long which_clock, const void *tp) {} + +PRE_SYSCALL(clock_gettime)(long which_clock, void *tp) {} + +POST_SYSCALL(clock_gettime)(long res, long which_clock, void *tp) { + if (res >= 0) { + if (tp) POST_WRITE(tp, struct_timespec_sz); + } +} + +PRE_SYSCALL(clock_adjtime)(long which_clock, void *tx) {} + +POST_SYSCALL(clock_adjtime)(long res, long which_clock, void *tx) { + if (res >= 0) { + if (tx) POST_WRITE(tx, struct_timex_sz); + } +} + +PRE_SYSCALL(clock_getres)(long which_clock, void *tp) {} + +POST_SYSCALL(clock_getres)(long res, long which_clock, void *tp) { + if (res >= 0) { + if (tp) POST_WRITE(tp, struct_timespec_sz); + } +} + +PRE_SYSCALL(clock_nanosleep)(long which_clock, long flags, const void *rqtp, + void *rmtp) { + if (rqtp) PRE_READ(rqtp, struct_timespec_sz); +} + +POST_SYSCALL(clock_nanosleep)(long res, long which_clock, long flags, + const void *rqtp, void *rmtp) { + if (res >= 0) { + if (rmtp) POST_WRITE(rmtp, struct_timespec_sz); + } +} + +PRE_SYSCALL(nice)(long increment) {} + +POST_SYSCALL(nice)(long res, long increment) {} + +PRE_SYSCALL(sched_setscheduler)(long pid, long policy, void *param) {} + +POST_SYSCALL(sched_setscheduler)(long res, long pid, long policy, void *param) { + if (res >= 0) { + if (param) POST_WRITE(param, struct_sched_param_sz); + } +} + +PRE_SYSCALL(sched_setparam)(long pid, void *param) { + if (param) PRE_READ(param, struct_sched_param_sz); +} + +POST_SYSCALL(sched_setparam)(long res, long pid, void *param) {} + +PRE_SYSCALL(sched_getscheduler)(long pid) {} + +POST_SYSCALL(sched_getscheduler)(long res, long pid) {} + +PRE_SYSCALL(sched_getparam)(long pid, void *param) {} + +POST_SYSCALL(sched_getparam)(long res, long pid, void *param) { + if (res >= 0) { + if (param) POST_WRITE(param, struct_sched_param_sz); + } +} + +PRE_SYSCALL(sched_setaffinity)(long pid, long len, void *user_mask_ptr) { + if (user_mask_ptr) PRE_READ(user_mask_ptr, len); +} + +POST_SYSCALL(sched_setaffinity)(long res, long pid, long len, + void *user_mask_ptr) {} + +PRE_SYSCALL(sched_getaffinity)(long pid, long len, void *user_mask_ptr) {} + +POST_SYSCALL(sched_getaffinity)(long res, long pid, long len, + void *user_mask_ptr) { + if (res >= 0) { + if (user_mask_ptr) POST_WRITE(user_mask_ptr, len); + } +} + +PRE_SYSCALL(sched_yield)() {} + +POST_SYSCALL(sched_yield)(long res) {} + +PRE_SYSCALL(sched_get_priority_max)(long policy) {} + +POST_SYSCALL(sched_get_priority_max)(long res, long policy) {} + +PRE_SYSCALL(sched_get_priority_min)(long policy) {} + +POST_SYSCALL(sched_get_priority_min)(long res, long policy) {} + +PRE_SYSCALL(sched_rr_get_interval)(long pid, void *interval) {} + +POST_SYSCALL(sched_rr_get_interval)(long res, long pid, void *interval) { + if (res >= 0) { + if (interval) POST_WRITE(interval, struct_timespec_sz); + } +} + +PRE_SYSCALL(setpriority)(long which, long who, long niceval) {} + +POST_SYSCALL(setpriority)(long res, long which, long who, long niceval) {} + +PRE_SYSCALL(getpriority)(long which, long who) {} + +POST_SYSCALL(getpriority)(long res, long which, long who) {} + +PRE_SYSCALL(shutdown)(long arg0, long arg1) {} + +POST_SYSCALL(shutdown)(long res, long arg0, long arg1) {} + +PRE_SYSCALL(reboot)(long magic1, long magic2, long cmd, void *arg) {} + +POST_SYSCALL(reboot)(long res, long magic1, long magic2, long cmd, void *arg) {} + +PRE_SYSCALL(restart_syscall)() {} + +POST_SYSCALL(restart_syscall)(long res) {} + +PRE_SYSCALL(kexec_load)(long entry, long nr_segments, void *segments, + long flags) {} + +POST_SYSCALL(kexec_load)(long res, long entry, long nr_segments, void *segments, + long flags) { + if (res >= 0) { + if (segments) POST_WRITE(segments, struct_kexec_segment_sz); + } +} + +PRE_SYSCALL(exit)(long error_code) {} + +POST_SYSCALL(exit)(long res, long error_code) {} + +PRE_SYSCALL(exit_group)(long error_code) {} + +POST_SYSCALL(exit_group)(long res, long error_code) {} + +PRE_SYSCALL(wait4)(long pid, void *stat_addr, long options, void *ru) {} + +POST_SYSCALL(wait4)(long res, long pid, void *stat_addr, long options, + void *ru) { + if (res >= 0) { + if (stat_addr) POST_WRITE(stat_addr, sizeof(int)); + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(waitid)(long which, long pid, void *infop, long options, void *ru) { +} + +POST_SYSCALL(waitid)(long res, long which, long pid, void *infop, long options, + void *ru) { + if (res >= 0) { + if (infop) POST_WRITE(infop, siginfo_t_sz); + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(waitpid)(long pid, void *stat_addr, long options) {} + +POST_SYSCALL(waitpid)(long res, long pid, void *stat_addr, long options) { + if (res >= 0) { + if (stat_addr) POST_WRITE(stat_addr, sizeof(int)); + } +} + +PRE_SYSCALL(set_tid_address)(void *tidptr) {} + +POST_SYSCALL(set_tid_address)(long res, void *tidptr) { + if (res >= 0) { + if (tidptr) POST_WRITE(tidptr, sizeof(int)); + } +} + +PRE_SYSCALL(init_module)(void *umod, long len, const void *uargs) { + if (uargs) + PRE_READ(uargs, __sanitizer::internal_strlen((const char *)uargs) + 1); +} + +POST_SYSCALL(init_module)(long res, void *umod, long len, const void *uargs) {} + +PRE_SYSCALL(delete_module)(const void *name_user, long flags) { + if (name_user) + PRE_READ(name_user, + __sanitizer::internal_strlen((const char *)name_user) + 1); +} + +POST_SYSCALL(delete_module)(long res, const void *name_user, long flags) {} + +PRE_SYSCALL(rt_sigprocmask)(long how, void *set, void *oset, long sigsetsize) {} + +POST_SYSCALL(rt_sigprocmask)(long res, long how, kernel_sigset_t *set, + kernel_sigset_t *oset, long sigsetsize) { + if (res >= 0) { + if (set) POST_WRITE(set, sigsetsize); + if (oset) POST_WRITE(oset, sigsetsize); + } +} + +PRE_SYSCALL(rt_sigpending)(void *set, long sigsetsize) {} + +POST_SYSCALL(rt_sigpending)(long res, kernel_sigset_t *set, long sigsetsize) { + if (res >= 0) { + if (set) POST_WRITE(set, sigsetsize); + } +} + +PRE_SYSCALL(rt_sigtimedwait)(const kernel_sigset_t *uthese, void *uinfo, + const void *uts, long sigsetsize) { + if (uthese) PRE_READ(uthese, sigsetsize); + if (uts) PRE_READ(uts, struct_timespec_sz); +} + +POST_SYSCALL(rt_sigtimedwait)(long res, const void *uthese, void *uinfo, + const void *uts, long sigsetsize) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(rt_tgsigqueueinfo)(long tgid, long pid, long sig, void *uinfo) {} + +POST_SYSCALL(rt_tgsigqueueinfo)(long res, long tgid, long pid, long sig, + void *uinfo) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(kill)(long pid, long sig) {} + +POST_SYSCALL(kill)(long res, long pid, long sig) {} + +PRE_SYSCALL(tgkill)(long tgid, long pid, long sig) {} + +POST_SYSCALL(tgkill)(long res, long tgid, long pid, long sig) {} + +PRE_SYSCALL(tkill)(long pid, long sig) {} + +POST_SYSCALL(tkill)(long res, long pid, long sig) {} + +PRE_SYSCALL(rt_sigqueueinfo)(long pid, long sig, void *uinfo) {} + +POST_SYSCALL(rt_sigqueueinfo)(long res, long pid, long sig, void *uinfo) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(sgetmask)() {} + +POST_SYSCALL(sgetmask)(long res) {} + +PRE_SYSCALL(ssetmask)(long newmask) {} + +POST_SYSCALL(ssetmask)(long res, long newmask) {} + +PRE_SYSCALL(signal)(long sig, long handler) {} + +POST_SYSCALL(signal)(long res, long sig, long handler) {} + +PRE_SYSCALL(pause)() {} + +POST_SYSCALL(pause)(long res) {} + +PRE_SYSCALL(sync)() {} + +POST_SYSCALL(sync)(long res) {} + +PRE_SYSCALL(fsync)(long fd) {} + +POST_SYSCALL(fsync)(long res, long fd) {} + +PRE_SYSCALL(fdatasync)(long fd) {} + +POST_SYSCALL(fdatasync)(long res, long fd) {} + +PRE_SYSCALL(bdflush)(long func, long data) {} + +POST_SYSCALL(bdflush)(long res, long func, long data) {} + +PRE_SYSCALL(mount)(void *dev_name, void *dir_name, void *type, long flags, + void *data) {} + +POST_SYSCALL(mount)(long res, void *dev_name, void *dir_name, void *type, + long flags, void *data) { + if (res >= 0) { + if (dev_name) + POST_WRITE(dev_name, + __sanitizer::internal_strlen((const char *)dev_name) + 1); + if (dir_name) + POST_WRITE(dir_name, + __sanitizer::internal_strlen((const char *)dir_name) + 1); + if (type) + POST_WRITE(type, __sanitizer::internal_strlen((const char *)type) + 1); + } +} + +PRE_SYSCALL(umount)(void *name, long flags) {} + +POST_SYSCALL(umount)(long res, void *name, long flags) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(oldumount)(void *name) {} + +POST_SYSCALL(oldumount)(long res, void *name) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(truncate)(const void *path, long length) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(truncate)(long res, const void *path, long length) {} + +PRE_SYSCALL(ftruncate)(long fd, long length) {} + +POST_SYSCALL(ftruncate)(long res, long fd, long length) {} + +PRE_SYSCALL(stat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(stat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(statfs)(const void *path, void *buf) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(statfs)(long res, const void *path, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs_sz); + } +} + +PRE_SYSCALL(statfs64)(const void *path, long sz, void *buf) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(statfs64)(long res, const void *path, long sz, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs64_sz); + } +} + +PRE_SYSCALL(fstatfs)(long fd, void *buf) {} + +POST_SYSCALL(fstatfs)(long res, long fd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs_sz); + } +} + +PRE_SYSCALL(fstatfs64)(long fd, long sz, void *buf) {} + +POST_SYSCALL(fstatfs64)(long res, long fd, long sz, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs64_sz); + } +} + +PRE_SYSCALL(lstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(fstat)(long fd, void *statbuf) {} + +POST_SYSCALL(fstat)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(newstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(newlstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newlstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(newfstat)(long fd, void *statbuf) {} + +POST_SYSCALL(newfstat)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(ustat)(long dev, void *ubuf) {} + +POST_SYSCALL(ustat)(long res, long dev, void *ubuf) { + if (res >= 0) { + if (ubuf) POST_WRITE(ubuf, struct_ustat_sz); + } +} + +PRE_SYSCALL(stat64)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(stat64)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(fstat64)(long fd, void *statbuf) {} + +POST_SYSCALL(fstat64)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(lstat64)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lstat64)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(setxattr)(const void *path, const void *name, const void *value, + long size, long flags) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(setxattr)(long res, const void *path, const void *name, + const void *value, long size, long flags) {} + +PRE_SYSCALL(lsetxattr)(const void *path, const void *name, const void *value, + long size, long flags) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(lsetxattr)(long res, const void *path, const void *name, + const void *value, long size, long flags) {} + +PRE_SYSCALL(fsetxattr)(long fd, const void *name, const void *value, long size, + long flags) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(fsetxattr)(long res, long fd, const void *name, const void *value, + long size, long flags) {} + +PRE_SYSCALL(getxattr)(const void *path, const void *name, void *value, + long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(getxattr)(long res, const void *path, const void *name, + void *value, long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(lgetxattr)(const void *path, const void *name, void *value, + long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(lgetxattr)(long res, const void *path, const void *name, + void *value, long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(fgetxattr)(long fd, const void *name, void *value, long size) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(fgetxattr)(long res, long fd, const void *name, void *value, + long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(listxattr)(const void *path, void *list, long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(listxattr)(long res, const void *path, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +PRE_SYSCALL(llistxattr)(const void *path, void *list, long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(llistxattr)(long res, const void *path, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +PRE_SYSCALL(flistxattr)(long fd, void *list, long size) {} + +POST_SYSCALL(flistxattr)(long res, long fd, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +PRE_SYSCALL(removexattr)(const void *path, const void *name) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(removexattr)(long res, const void *path, const void *name) {} + +PRE_SYSCALL(lremovexattr)(const void *path, const void *name) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(lremovexattr)(long res, const void *path, const void *name) {} + +PRE_SYSCALL(fremovexattr)(long fd, const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(fremovexattr)(long res, long fd, const void *name) {} + +PRE_SYSCALL(brk)(long brk) {} + +POST_SYSCALL(brk)(long res, long brk) {} + +PRE_SYSCALL(mprotect)(long start, long len, long prot) {} + +POST_SYSCALL(mprotect)(long res, long start, long len, long prot) {} + +PRE_SYSCALL(mremap)(long addr, long old_len, long new_len, long flags, + long new_addr) {} + +POST_SYSCALL(mremap)(long res, long addr, long old_len, long new_len, + long flags, long new_addr) {} + +PRE_SYSCALL(remap_file_pages)(long start, long size, long prot, long pgoff, + long flags) {} + +POST_SYSCALL(remap_file_pages)(long res, long start, long size, long prot, + long pgoff, long flags) {} + +PRE_SYSCALL(msync)(long start, long len, long flags) {} + +POST_SYSCALL(msync)(long res, long start, long len, long flags) {} + +PRE_SYSCALL(munmap)(long addr, long len) {} + +POST_SYSCALL(munmap)(long res, long addr, long len) {} + +PRE_SYSCALL(mlock)(long start, long len) {} + +POST_SYSCALL(mlock)(long res, long start, long len) {} + +PRE_SYSCALL(munlock)(long start, long len) {} + +POST_SYSCALL(munlock)(long res, long start, long len) {} + +PRE_SYSCALL(mlockall)(long flags) {} + +POST_SYSCALL(mlockall)(long res, long flags) {} + +PRE_SYSCALL(munlockall)() {} + +POST_SYSCALL(munlockall)(long res) {} + +PRE_SYSCALL(madvise)(long start, long len, long behavior) {} + +POST_SYSCALL(madvise)(long res, long start, long len, long behavior) {} + +PRE_SYSCALL(mincore)(long start, long len, void *vec) {} + +POST_SYSCALL(mincore)(long res, long start, long len, void *vec) { + if (res >= 0) { + if (vec) { + POST_WRITE(vec, (len + GetPageSizeCached() - 1) / GetPageSizeCached()); + } + } +} + +PRE_SYSCALL(pivot_root)(const void *new_root, const void *put_old) { + if (new_root) + PRE_READ(new_root, + __sanitizer::internal_strlen((const char *)new_root) + 1); + if (put_old) + PRE_READ(put_old, __sanitizer::internal_strlen((const char *)put_old) + 1); +} + +POST_SYSCALL(pivot_root)(long res, const void *new_root, const void *put_old) {} + +PRE_SYSCALL(chroot)(const void *filename) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chroot)(long res, const void *filename) {} + +PRE_SYSCALL(mknod)(const void *filename, long mode, long dev) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(mknod)(long res, const void *filename, long mode, long dev) {} + +PRE_SYSCALL(link)(const void *oldname, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(link)(long res, const void *oldname, const void *newname) {} + +PRE_SYSCALL(symlink)(const void *old, const void *new_) { + if (old) PRE_READ(old, __sanitizer::internal_strlen((const char *)old) + 1); + if (new_) + PRE_READ(new_, __sanitizer::internal_strlen((const char *)new_) + 1); +} + +POST_SYSCALL(symlink)(long res, const void *old, const void *new_) {} + +PRE_SYSCALL(unlink)(const void *pathname) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(unlink)(long res, const void *pathname) {} + +PRE_SYSCALL(rename)(const void *oldname, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(rename)(long res, const void *oldname, const void *newname) {} + +PRE_SYSCALL(chmod)(const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chmod)(long res, const void *filename, long mode) {} + +PRE_SYSCALL(fchmod)(long fd, long mode) {} + +POST_SYSCALL(fchmod)(long res, long fd, long mode) {} + +PRE_SYSCALL(fcntl)(long fd, long cmd, long arg) {} + +POST_SYSCALL(fcntl)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(fcntl64)(long fd, long cmd, long arg) {} + +POST_SYSCALL(fcntl64)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(pipe)(void *fildes) {} + +POST_SYSCALL(pipe)(long res, void *fildes) { + if (res >= 0) { + if (fildes) POST_WRITE(fildes, sizeof(int)); + } +} + +PRE_SYSCALL(pipe2)(void *fildes, long flags) {} + +POST_SYSCALL(pipe2)(long res, void *fildes, long flags) { + if (res >= 0) { + if (fildes) POST_WRITE(fildes, sizeof(int)); + } +} + +PRE_SYSCALL(dup)(long fildes) {} + +POST_SYSCALL(dup)(long res, long fildes) {} + +PRE_SYSCALL(dup2)(long oldfd, long newfd) {} + +POST_SYSCALL(dup2)(long res, long oldfd, long newfd) {} + +PRE_SYSCALL(dup3)(long oldfd, long newfd, long flags) {} + +POST_SYSCALL(dup3)(long res, long oldfd, long newfd, long flags) {} + +PRE_SYSCALL(ioperm)(long from, long num, long on) {} + +POST_SYSCALL(ioperm)(long res, long from, long num, long on) {} + +PRE_SYSCALL(ioctl)(long fd, long cmd, long arg) {} + +POST_SYSCALL(ioctl)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(flock)(long fd, long cmd) {} + +POST_SYSCALL(flock)(long res, long fd, long cmd) {} + +PRE_SYSCALL(io_setup)(long nr_reqs, void *ctx) {} + +POST_SYSCALL(io_setup)(long res, long nr_reqs, void *ctx) { + if (res >= 0) { + if (ctx) POST_WRITE(ctx, sizeof(long)); + } +} + +PRE_SYSCALL(io_destroy)(long ctx) {} + +POST_SYSCALL(io_destroy)(long res, long ctx) {} + +PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr, void *events, + void *timeout) { + if (timeout) PRE_READ(timeout, struct_timespec_sz); +} + +POST_SYSCALL(io_getevents)(long res, long ctx_id, long min_nr, long nr, + void *events, void *timeout) { + if (res >= 0) { + if (events) POST_WRITE(events, res * struct_io_event_sz); + if (timeout) POST_WRITE(timeout, struct_timespec_sz); + } +} + +PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) { + for (long i = 0; i < nr; ++i) { + if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pwrite && iocbpp[i]->aio_buf && + iocbpp[i]->aio_nbytes) + PRE_READ((void *)iocbpp[i]->aio_buf, iocbpp[i]->aio_nbytes); + } +} + +POST_SYSCALL(io_submit)(long res, long ctx_id, long nr, + __sanitizer_iocb **iocbpp) { + if (res > 0 && iocbpp) { + for (long i = 0; i < res; ++i) { + if (iocbpp[i]->aio_lio_opcode == iocb_cmd_pread && iocbpp[i]->aio_buf && + iocbpp[i]->aio_nbytes) + POST_WRITE((void *)iocbpp[i]->aio_buf, iocbpp[i]->aio_nbytes); + } + } +} + +PRE_SYSCALL(io_cancel)(long ctx_id, void *iocb, void *result) {} + +POST_SYSCALL(io_cancel)(long res, long ctx_id, void *iocb, void *result) { + if (res >= 0) { + if (iocb) POST_WRITE(iocb, sizeof(__sanitizer_iocb)); + if (result) POST_WRITE(result, struct_io_event_sz); + } +} + +PRE_SYSCALL(sendfile)(long out_fd, long in_fd, void *offset, long count) {} + +POST_SYSCALL(sendfile)(long res, long out_fd, long in_fd, + __sanitizer___kernel_off_t *offset, long count) { + if (res >= 0) { + if (offset) POST_WRITE(offset, sizeof(*offset)); + } +} + +PRE_SYSCALL(sendfile64)(long out_fd, long in_fd, void *offset, long count) {} + +POST_SYSCALL(sendfile64)(long res, long out_fd, long in_fd, + __sanitizer___kernel_loff_t *offset, long count) { + if (res >= 0) { + if (offset) POST_WRITE(offset, sizeof(*offset)); + } +} + +PRE_SYSCALL(readlink)(const void *path, void *buf, long bufsiz) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(readlink)(long res, const void *path, void *buf, long bufsiz) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(creat)(const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(creat)(long res, const void *pathname, long mode) {} + +PRE_SYSCALL(open)(const void *filename, long flags, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(open)(long res, const void *filename, long flags, long mode) {} + +PRE_SYSCALL(close)(long fd) { + COMMON_SYSCALL_FD_CLOSE((int)fd); +} + +POST_SYSCALL(close)(long res, long fd) {} + +PRE_SYSCALL(access)(const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(access)(long res, const void *filename, long mode) {} + +PRE_SYSCALL(vhangup)() {} + +POST_SYSCALL(vhangup)(long res) {} + +PRE_SYSCALL(chown)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chown)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(lchown)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lchown)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(fchown)(long fd, long user, long group) {} + +POST_SYSCALL(fchown)(long res, long fd, long user, long group) {} + +PRE_SYSCALL(chown16)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chown16)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(lchown16)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lchown16)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(fchown16)(long fd, long user, long group) {} + +POST_SYSCALL(fchown16)(long res, long fd, long user, long group) {} + +PRE_SYSCALL(setregid16)(long rgid, long egid) {} + +POST_SYSCALL(setregid16)(long res, long rgid, long egid) {} + +PRE_SYSCALL(setgid16)(long gid) {} + +POST_SYSCALL(setgid16)(long res, long gid) {} + +PRE_SYSCALL(setreuid16)(long ruid, long euid) {} + +POST_SYSCALL(setreuid16)(long res, long ruid, long euid) {} + +PRE_SYSCALL(setuid16)(long uid) {} + +POST_SYSCALL(setuid16)(long res, long uid) {} + +PRE_SYSCALL(setresuid16)(long ruid, long euid, long suid) {} + +POST_SYSCALL(setresuid16)(long res, long ruid, long euid, long suid) {} + +PRE_SYSCALL(getresuid16)(void *ruid, void *euid, void *suid) {} + +POST_SYSCALL(getresuid16)(long res, __sanitizer___kernel_old_uid_t *ruid, + __sanitizer___kernel_old_uid_t *euid, + __sanitizer___kernel_old_uid_t *suid) { + if (res >= 0) { + if (ruid) POST_WRITE(ruid, sizeof(*ruid)); + if (euid) POST_WRITE(euid, sizeof(*euid)); + if (suid) POST_WRITE(suid, sizeof(*suid)); + } +} + +PRE_SYSCALL(setresgid16)(long rgid, long egid, long sgid) {} + +POST_SYSCALL(setresgid16)(long res, long rgid, long egid, long sgid) {} + +PRE_SYSCALL(getresgid16)(void *rgid, void *egid, void *sgid) {} + +POST_SYSCALL(getresgid16)(long res, __sanitizer___kernel_old_gid_t *rgid, + __sanitizer___kernel_old_gid_t *egid, + __sanitizer___kernel_old_gid_t *sgid) { + if (res >= 0) { + if (rgid) POST_WRITE(rgid, sizeof(*rgid)); + if (egid) POST_WRITE(egid, sizeof(*egid)); + if (sgid) POST_WRITE(sgid, sizeof(*sgid)); + } +} + +PRE_SYSCALL(setfsuid16)(long uid) {} + +POST_SYSCALL(setfsuid16)(long res, long uid) {} + +PRE_SYSCALL(setfsgid16)(long gid) {} + +POST_SYSCALL(setfsgid16)(long res, long gid) {} + +PRE_SYSCALL(getgroups16)(long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) {} + +POST_SYSCALL(getgroups16)(long res, long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) { + if (res >= 0) { + if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist)); + } +} + +PRE_SYSCALL(setgroups16)(long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) { + if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist)); +} + +POST_SYSCALL(setgroups16)(long res, long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) {} + +PRE_SYSCALL(getuid16)() {} + +POST_SYSCALL(getuid16)(long res) {} + +PRE_SYSCALL(geteuid16)() {} + +POST_SYSCALL(geteuid16)(long res) {} + +PRE_SYSCALL(getgid16)() {} + +POST_SYSCALL(getgid16)(long res) {} + +PRE_SYSCALL(getegid16)() {} + +POST_SYSCALL(getegid16)(long res) {} + +PRE_SYSCALL(utime)(void *filename, void *times) {} + +POST_SYSCALL(utime)(long res, void *filename, void *times) { + if (res >= 0) { + if (filename) + POST_WRITE(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); + if (times) POST_WRITE(times, struct_utimbuf_sz); + } +} + +PRE_SYSCALL(utimes)(void *filename, void *utimes) {} + +POST_SYSCALL(utimes)(long res, void *filename, void *utimes) { + if (res >= 0) { + if (filename) + POST_WRITE(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); + if (utimes) POST_WRITE(utimes, timeval_sz); + } +} + +PRE_SYSCALL(lseek)(long fd, long offset, long origin) {} + +POST_SYSCALL(lseek)(long res, long fd, long offset, long origin) {} + +PRE_SYSCALL(llseek)(long fd, long offset_high, long offset_low, void *result, + long origin) {} + +POST_SYSCALL(llseek)(long res, long fd, long offset_high, long offset_low, + void *result, long origin) { + if (res >= 0) { + if (result) POST_WRITE(result, sizeof(long long)); + } +} + +PRE_SYSCALL(readv)(long fd, const __sanitizer_iovec *vec, long vlen) {} + +POST_SYSCALL(readv)(long res, long fd, const __sanitizer_iovec *vec, + long vlen) { + if (res >= 0) { + if (vec) kernel_write_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(write)(long fd, const void *buf, long count) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(write)(long res, long fd, const void *buf, long count) {} + +PRE_SYSCALL(writev)(long fd, const __sanitizer_iovec *vec, long vlen) {} + +POST_SYSCALL(writev)(long res, long fd, const __sanitizer_iovec *vec, + long vlen) { + if (res >= 0) { + if (vec) kernel_read_iovec(vec, vlen, res); + } +} + +#ifdef _LP64 +PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos) {} + +POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count, + long pos) {} +#else +PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos0, long pos1) {} + +POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos0, + long pos1) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos0, + long pos1) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count, + long pos0, long pos1) {} +#endif + +PRE_SYSCALL(preadv)(long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) {} + +POST_SYSCALL(preadv)(long res, long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) { + if (res >= 0) { + if (vec) kernel_write_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(pwritev)(long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) {} + +POST_SYSCALL(pwritev)(long res, long fd, const __sanitizer_iovec *vec, + long vlen, long pos_l, long pos_h) { + if (res >= 0) { + if (vec) kernel_read_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(getcwd)(void *buf, long size) {} + +POST_SYSCALL(getcwd)(long res, void *buf, long size) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(mkdir)(const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(mkdir)(long res, const void *pathname, long mode) {} + +PRE_SYSCALL(chdir)(const void *filename) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chdir)(long res, const void *filename) {} + +PRE_SYSCALL(fchdir)(long fd) {} + +POST_SYSCALL(fchdir)(long res, long fd) {} + +PRE_SYSCALL(rmdir)(const void *pathname) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(rmdir)(long res, const void *pathname) {} + +PRE_SYSCALL(lookup_dcookie)(u64 cookie64, void *buf, long len) {} + +POST_SYSCALL(lookup_dcookie)(long res, u64 cookie64, void *buf, long len) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(quotactl)(long cmd, const void *special, long id, void *addr) { + if (special) + PRE_READ(special, __sanitizer::internal_strlen((const char *)special) + 1); +} + +POST_SYSCALL(quotactl)(long res, long cmd, const void *special, long id, + void *addr) {} + +PRE_SYSCALL(getdents)(long fd, void *dirent, long count) {} + +POST_SYSCALL(getdents)(long res, long fd, void *dirent, long count) { + if (res >= 0) { + if (dirent) POST_WRITE(dirent, res); + } +} + +PRE_SYSCALL(getdents64)(long fd, void *dirent, long count) {} + +POST_SYSCALL(getdents64)(long res, long fd, void *dirent, long count) { + if (res >= 0) { + if (dirent) POST_WRITE(dirent, res); + } +} + +PRE_SYSCALL(setsockopt)(long fd, long level, long optname, void *optval, + long optlen) {} + +POST_SYSCALL(setsockopt)(long res, long fd, long level, long optname, + void *optval, long optlen) { + if (res >= 0) { + if (optval) + POST_WRITE(optval, + __sanitizer::internal_strlen((const char *)optval) + 1); + } +} + +PRE_SYSCALL(getsockopt)(long fd, long level, long optname, void *optval, + void *optlen) {} + +POST_SYSCALL(getsockopt)(long res, long fd, long level, long optname, + void *optval, void *optlen) { + if (res >= 0) { + if (optval) + POST_WRITE(optval, + __sanitizer::internal_strlen((const char *)optval) + 1); + if (optlen) POST_WRITE(optlen, sizeof(int)); + } +} + +PRE_SYSCALL(bind)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {} + +POST_SYSCALL(bind)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + long arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + } +} + +PRE_SYSCALL(connect)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {} + +POST_SYSCALL(connect)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + long arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + } +} + +PRE_SYSCALL(accept)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2) {} + +POST_SYSCALL(accept)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(accept4)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2, + long arg3) {} + +POST_SYSCALL(accept4)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2, long arg3) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getsockname)(long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) {} + +POST_SYSCALL(getsockname)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getpeername)(long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) {} + +POST_SYSCALL(getpeername)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(send)(long arg0, void *arg1, long arg2, long arg3) {} + +POST_SYSCALL(send)(long res, long arg0, void *arg1, long arg2, long arg3) { + if (res) { + if (arg1) POST_READ(arg1, res); + } +} + +PRE_SYSCALL(sendto)(long arg0, void *arg1, long arg2, long arg3, + sanitizer_kernel_sockaddr *arg4, long arg5) {} + +POST_SYSCALL(sendto)(long res, long arg0, void *arg1, long arg2, long arg3, + sanitizer_kernel_sockaddr *arg4, long arg5) { + if (res >= 0) { + if (arg1) POST_READ(arg1, res); + if (arg4) POST_WRITE(arg4, sizeof(*arg4)); + } +} + +PRE_SYSCALL(sendmsg)(long fd, void *msg, long flags) {} + +POST_SYSCALL(sendmsg)(long res, long fd, void *msg, long flags) { + // FIXME: POST_READ +} + +PRE_SYSCALL(sendmmsg)(long fd, void *msg, long vlen, long flags) {} + +POST_SYSCALL(sendmmsg)(long res, long fd, void *msg, long vlen, long flags) { + // FIXME: POST_READ +} + +PRE_SYSCALL(recv)(long arg0, void *buf, long len, long flags) {} + +POST_SYSCALL(recv)(long res, void *buf, long len, long flags) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(recvfrom)(long arg0, void *buf, long len, long flags, + sanitizer_kernel_sockaddr *arg4, void *arg5) {} + +POST_SYSCALL(recvfrom)(long res, long arg0, void *buf, long len, long flags, + sanitizer_kernel_sockaddr *arg4, void *arg5) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + if (arg4) POST_WRITE(arg4, sizeof(*arg4)); + if (arg5) POST_WRITE(arg5, sizeof(int)); + } +} + +PRE_SYSCALL(socket)(long arg0, long arg1, long arg2) {} + +POST_SYSCALL(socket)(long res, long arg0, long arg1, long arg2) {} + +PRE_SYSCALL(socketpair)(long arg0, long arg1, long arg2, void *arg3) {} + +POST_SYSCALL(socketpair)(long res, long arg0, long arg1, long arg2, + void *arg3) { + if (res >= 0) { + if (arg3) POST_WRITE(arg3, sizeof(int)); + } +} + +PRE_SYSCALL(socketcall)(long call, void *args) {} + +POST_SYSCALL(socketcall)(long res, long call, void *args) { + if (res >= 0) { + if (args) POST_WRITE(args, sizeof(long)); + } +} + +PRE_SYSCALL(listen)(long arg0, long arg1) {} + +POST_SYSCALL(listen)(long res, long arg0, long arg1) {} + +PRE_SYSCALL(poll)(void *ufds, long nfds, long timeout) {} + +POST_SYSCALL(poll)(long res, __sanitizer_pollfd *ufds, long nfds, + long timeout) { + if (res >= 0) { + if (ufds) POST_WRITE(ufds, nfds * sizeof(*ufds)); + } +} + +PRE_SYSCALL(select)(long n, __sanitizer___kernel_fd_set *inp, + __sanitizer___kernel_fd_set *outp, + __sanitizer___kernel_fd_set *exp, void *tvp) {} + +POST_SYSCALL(select)(long res, long n, __sanitizer___kernel_fd_set *inp, + __sanitizer___kernel_fd_set *outp, + __sanitizer___kernel_fd_set *exp, void *tvp) { + if (res >= 0) { + if (inp) POST_WRITE(inp, sizeof(*inp)); + if (outp) POST_WRITE(outp, sizeof(*outp)); + if (exp) POST_WRITE(exp, sizeof(*exp)); + if (tvp) POST_WRITE(tvp, timeval_sz); + } +} + +PRE_SYSCALL(old_select)(void *arg) {} + +POST_SYSCALL(old_select)(long res, void *arg) {} + +PRE_SYSCALL(epoll_create)(long size) {} + +POST_SYSCALL(epoll_create)(long res, long size) {} + +PRE_SYSCALL(epoll_create1)(long flags) {} + +POST_SYSCALL(epoll_create1)(long res, long flags) {} + +PRE_SYSCALL(epoll_ctl)(long epfd, long op, long fd, void *event) {} + +POST_SYSCALL(epoll_ctl)(long res, long epfd, long op, long fd, void *event) { + if (res >= 0) { + if (event) POST_WRITE(event, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(epoll_wait)(long epfd, void *events, long maxevents, long timeout) { +} + +POST_SYSCALL(epoll_wait)(long res, long epfd, void *events, long maxevents, + long timeout) { + if (res >= 0) { + if (events) POST_WRITE(events, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(epoll_pwait)(long epfd, void *events, long maxevents, long timeout, + const kernel_sigset_t *sigmask, long sigsetsize) { + if (sigmask) PRE_READ(sigmask, sigsetsize); +} + +POST_SYSCALL(epoll_pwait)(long res, long epfd, void *events, long maxevents, + long timeout, const void *sigmask, long sigsetsize) { + if (res >= 0) { + if (events) POST_WRITE(events, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(gethostname)(void *name, long len) {} + +POST_SYSCALL(gethostname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(sethostname)(void *name, long len) {} + +POST_SYSCALL(sethostname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(setdomainname)(void *name, long len) {} + +POST_SYSCALL(setdomainname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(newuname)(void *name) {} + +POST_SYSCALL(newuname)(long res, void *name) { + if (res >= 0) { + if (name) POST_WRITE(name, struct_new_utsname_sz); + } +} + +PRE_SYSCALL(uname)(void *arg0) {} + +POST_SYSCALL(uname)(long res, void *arg0) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, struct_old_utsname_sz); + } +} + +PRE_SYSCALL(olduname)(void *arg0) {} + +POST_SYSCALL(olduname)(long res, void *arg0) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, struct_oldold_utsname_sz); + } +} + +PRE_SYSCALL(getrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(getrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +PRE_SYSCALL(old_getrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(old_getrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +PRE_SYSCALL(setrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(setrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +#if !SANITIZER_ANDROID +PRE_SYSCALL(prlimit64)(long pid, long resource, const void *new_rlim, + void *old_rlim) { + if (new_rlim) PRE_READ(new_rlim, struct_rlimit64_sz); +} + +POST_SYSCALL(prlimit64)(long res, long pid, long resource, const void *new_rlim, + void *old_rlim) { + if (res >= 0) { + if (old_rlim) POST_WRITE(old_rlim, struct_rlimit64_sz); + } +} +#endif + +PRE_SYSCALL(getrusage)(long who, void *ru) {} + +POST_SYSCALL(getrusage)(long res, long who, void *ru) { + if (res >= 0) { + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(umask)(long mask) {} + +POST_SYSCALL(umask)(long res, long mask) {} + +PRE_SYSCALL(msgget)(long key, long msgflg) {} + +POST_SYSCALL(msgget)(long res, long key, long msgflg) {} + +PRE_SYSCALL(msgsnd)(long msqid, void *msgp, long msgsz, long msgflg) { + if (msgp) PRE_READ(msgp, msgsz); +} + +POST_SYSCALL(msgsnd)(long res, long msqid, void *msgp, long msgsz, + long msgflg) {} + +PRE_SYSCALL(msgrcv)(long msqid, void *msgp, long msgsz, long msgtyp, + long msgflg) {} + +POST_SYSCALL(msgrcv)(long res, long msqid, void *msgp, long msgsz, long msgtyp, + long msgflg) { + if (res >= 0) { + if (msgp) POST_WRITE(msgp, res); + } +} + +PRE_SYSCALL(msgctl)(long msqid, long cmd, void *buf) {} + +POST_SYSCALL(msgctl)(long res, long msqid, long cmd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_msqid_ds_sz); + } +} + +PRE_SYSCALL(semget)(long key, long nsems, long semflg) {} + +POST_SYSCALL(semget)(long res, long key, long nsems, long semflg) {} + +PRE_SYSCALL(semop)(long semid, void *sops, long nsops) {} + +POST_SYSCALL(semop)(long res, long semid, void *sops, long nsops) {} + +PRE_SYSCALL(semctl)(long semid, long semnum, long cmd, void *arg) {} + +POST_SYSCALL(semctl)(long res, long semid, long semnum, long cmd, void *arg) {} + +PRE_SYSCALL(semtimedop)(long semid, void *sops, long nsops, + const void *timeout) { + if (timeout) PRE_READ(timeout, struct_timespec_sz); +} + +POST_SYSCALL(semtimedop)(long res, long semid, void *sops, long nsops, + const void *timeout) {} + +PRE_SYSCALL(shmat)(long shmid, void *shmaddr, long shmflg) {} + +POST_SYSCALL(shmat)(long res, long shmid, void *shmaddr, long shmflg) { + if (res >= 0) { + if (shmaddr) + POST_WRITE(shmaddr, + __sanitizer::internal_strlen((const char *)shmaddr) + 1); + } +} + +PRE_SYSCALL(shmget)(long key, long size, long flag) {} + +POST_SYSCALL(shmget)(long res, long key, long size, long flag) {} + +PRE_SYSCALL(shmdt)(void *shmaddr) {} + +POST_SYSCALL(shmdt)(long res, void *shmaddr) { + if (res >= 0) { + if (shmaddr) + POST_WRITE(shmaddr, + __sanitizer::internal_strlen((const char *)shmaddr) + 1); + } +} + +PRE_SYSCALL(ipc)(long call, long first, long second, long third, void *ptr, + long fifth) {} + +POST_SYSCALL(ipc)(long res, long call, long first, long second, long third, + void *ptr, long fifth) {} + +#if !SANITIZER_ANDROID +PRE_SYSCALL(shmctl)(long shmid, long cmd, void *buf) {} + +POST_SYSCALL(shmctl)(long res, long shmid, long cmd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, sizeof(__sanitizer_shmid_ds)); + } +} + +PRE_SYSCALL(mq_open)(const void *name, long oflag, long mode, void *attr) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(mq_open)(long res, const void *name, long oflag, long mode, + void *attr) { + if (res >= 0) { + if (attr) POST_WRITE(attr, struct_mq_attr_sz); + } +} + +PRE_SYSCALL(mq_unlink)(const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(mq_unlink)(long res, const void *name) {} + +PRE_SYSCALL(mq_timedsend)(long mqdes, const void *msg_ptr, long msg_len, + long msg_prio, const void *abs_timeout) { + if (msg_ptr) PRE_READ(msg_ptr, msg_len); + if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz); +} + +POST_SYSCALL(mq_timedsend)(long res, long mqdes, const void *msg_ptr, + long msg_len, long msg_prio, + const void *abs_timeout) {} + +PRE_SYSCALL(mq_timedreceive)(long mqdes, void *msg_ptr, long msg_len, + void *msg_prio, const void *abs_timeout) { + if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz); +} + +POST_SYSCALL(mq_timedreceive)(long res, long mqdes, void *msg_ptr, long msg_len, + int *msg_prio, const void *abs_timeout) { + if (res >= 0) { + if (msg_ptr) POST_WRITE(msg_ptr, res); + if (msg_prio) POST_WRITE(msg_prio, sizeof(*msg_prio)); + } +} + +PRE_SYSCALL(mq_notify)(long mqdes, const void *notification) { + if (notification) PRE_READ(notification, struct_sigevent_sz); +} + +POST_SYSCALL(mq_notify)(long res, long mqdes, const void *notification) {} + +PRE_SYSCALL(mq_getsetattr)(long mqdes, const void *mqstat, void *omqstat) { + if (mqstat) PRE_READ(mqstat, struct_mq_attr_sz); +} + +POST_SYSCALL(mq_getsetattr)(long res, long mqdes, const void *mqstat, + void *omqstat) { + if (res >= 0) { + if (omqstat) POST_WRITE(omqstat, struct_mq_attr_sz); + } +} +#endif // SANITIZER_ANDROID + +PRE_SYSCALL(pciconfig_iobase)(long which, long bus, long devfn) {} + +POST_SYSCALL(pciconfig_iobase)(long res, long which, long bus, long devfn) {} + +PRE_SYSCALL(pciconfig_read)(long bus, long dfn, long off, long len, void *buf) { +} + +POST_SYSCALL(pciconfig_read)(long res, long bus, long dfn, long off, long len, + void *buf) {} + +PRE_SYSCALL(pciconfig_write)(long bus, long dfn, long off, long len, + void *buf) {} + +POST_SYSCALL(pciconfig_write)(long res, long bus, long dfn, long off, long len, + void *buf) {} + +PRE_SYSCALL(swapon)(const void *specialfile, long swap_flags) { + if (specialfile) + PRE_READ(specialfile, + __sanitizer::internal_strlen((const char *)specialfile) + 1); +} + +POST_SYSCALL(swapon)(long res, const void *specialfile, long swap_flags) {} + +PRE_SYSCALL(swapoff)(const void *specialfile) { + if (specialfile) + PRE_READ(specialfile, + __sanitizer::internal_strlen((const char *)specialfile) + 1); +} + +POST_SYSCALL(swapoff)(long res, const void *specialfile) {} + +PRE_SYSCALL(sysctl)(__sanitizer___sysctl_args *args) { + if (args) { + if (args->name) PRE_READ(args->name, args->nlen * sizeof(*args->name)); + if (args->newval) PRE_READ(args->name, args->newlen); + } +} + +POST_SYSCALL(sysctl)(long res, __sanitizer___sysctl_args *args) { + if (res >= 0) { + if (args && args->oldval && args->oldlenp) { + POST_WRITE(args->oldlenp, sizeof(*args->oldlenp)); + POST_WRITE(args->oldval, *args->oldlenp); + } + } +} + +PRE_SYSCALL(sysinfo)(void *info) {} + +POST_SYSCALL(sysinfo)(long res, void *info) { + if (res >= 0) { + if (info) POST_WRITE(info, struct_sysinfo_sz); + } +} + +PRE_SYSCALL(sysfs)(long option, long arg1, long arg2) {} + +POST_SYSCALL(sysfs)(long res, long option, long arg1, long arg2) {} + +PRE_SYSCALL(syslog)(long type, void *buf, long len) {} + +POST_SYSCALL(syslog)(long res, long type, void *buf, long len) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(uselib)(const void *library) { + if (library) + PRE_READ(library, __sanitizer::internal_strlen((const char *)library) + 1); +} + +POST_SYSCALL(uselib)(long res, const void *library) {} + +PRE_SYSCALL(ni_syscall)() {} + +POST_SYSCALL(ni_syscall)(long res) {} + +PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { +#if defined(__i386) || defined (__x86_64) + if (data) { + if (request == ptrace_setregs) { + PRE_READ((void *)data, struct_user_regs_struct_sz); + } else if (request == ptrace_setfpregs) { + PRE_READ((void *)data, struct_user_fpregs_struct_sz); + } else if (request == ptrace_setfpxregs) { + PRE_READ((void *)data, struct_user_fpxregs_struct_sz); + } else if (request == ptrace_setsiginfo) { + PRE_READ((void *)data, siginfo_t_sz); + } else if (request == ptrace_setregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + PRE_READ(iov->iov_base, iov->iov_len); + } + } +#endif +} + +POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { +#if defined(__i386) || defined (__x86_64) + if (res >= 0 && data) { + // Note that this is different from the interceptor in + // sanitizer_common_interceptors.inc. + // PEEK* requests return resulting values through data pointer. + if (request == ptrace_getregs) { + POST_WRITE((void *)data, struct_user_regs_struct_sz); + } else if (request == ptrace_getfpregs) { + POST_WRITE((void *)data, struct_user_fpregs_struct_sz); + } else if (request == ptrace_getfpxregs) { + POST_WRITE((void *)data, struct_user_fpxregs_struct_sz); + } else if (request == ptrace_getsiginfo) { + POST_WRITE((void *)data, siginfo_t_sz); + } else if (request == ptrace_getregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + POST_WRITE(iov->iov_base, iov->iov_len); + } else if (request == ptrace_peekdata || request == ptrace_peektext || + request == ptrace_peekuser) { + POST_WRITE((void *)data, sizeof(void *)); + } + } +#endif +} + +PRE_SYSCALL(add_key)(const void *_type, const void *_description, + const void *_payload, long plen, long destringid) { + if (_type) + PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1); + if (_description) + PRE_READ(_description, + __sanitizer::internal_strlen((const char *)_description) + 1); +} + +POST_SYSCALL(add_key)(long res, const void *_type, const void *_description, + const void *_payload, long plen, long destringid) {} + +PRE_SYSCALL(request_key)(const void *_type, const void *_description, + const void *_callout_info, long destringid) { + if (_type) + PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1); + if (_description) + PRE_READ(_description, + __sanitizer::internal_strlen((const char *)_description) + 1); + if (_callout_info) + PRE_READ(_callout_info, + __sanitizer::internal_strlen((const char *)_callout_info) + 1); +} + +POST_SYSCALL(request_key)(long res, const void *_type, const void *_description, + const void *_callout_info, long destringid) {} + +PRE_SYSCALL(keyctl)(long cmd, long arg2, long arg3, long arg4, long arg5) {} + +POST_SYSCALL(keyctl)(long res, long cmd, long arg2, long arg3, long arg4, + long arg5) {} + +PRE_SYSCALL(ioprio_set)(long which, long who, long ioprio) {} + +POST_SYSCALL(ioprio_set)(long res, long which, long who, long ioprio) {} + +PRE_SYSCALL(ioprio_get)(long which, long who) {} + +POST_SYSCALL(ioprio_get)(long res, long which, long who) {} + +PRE_SYSCALL(set_mempolicy)(long mode, void *nmask, long maxnode) {} + +POST_SYSCALL(set_mempolicy)(long res, long mode, void *nmask, long maxnode) { + if (res >= 0) { + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(migrate_pages)(long pid, long maxnode, const void *from, + const void *to) { + if (from) PRE_READ(from, sizeof(long)); + if (to) PRE_READ(to, sizeof(long)); +} + +POST_SYSCALL(migrate_pages)(long res, long pid, long maxnode, const void *from, + const void *to) {} + +PRE_SYSCALL(move_pages)(long pid, long nr_pages, const void **pages, + const int *nodes, int *status, long flags) { + if (pages) PRE_READ(pages, nr_pages * sizeof(*pages)); + if (nodes) PRE_READ(nodes, nr_pages * sizeof(*nodes)); +} + +POST_SYSCALL(move_pages)(long res, long pid, long nr_pages, const void **pages, + const int *nodes, int *status, long flags) { + if (res >= 0) { + if (status) POST_WRITE(status, nr_pages * sizeof(*status)); + } +} + +PRE_SYSCALL(mbind)(long start, long len, long mode, void *nmask, long maxnode, + long flags) {} + +POST_SYSCALL(mbind)(long res, long start, long len, long mode, void *nmask, + long maxnode, long flags) { + if (res >= 0) { + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(get_mempolicy)(void *policy, void *nmask, long maxnode, long addr, + long flags) {} + +POST_SYSCALL(get_mempolicy)(long res, void *policy, void *nmask, long maxnode, + long addr, long flags) { + if (res >= 0) { + if (policy) POST_WRITE(policy, sizeof(int)); + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(inotify_init)() {} + +POST_SYSCALL(inotify_init)(long res) {} + +PRE_SYSCALL(inotify_init1)(long flags) {} + +POST_SYSCALL(inotify_init1)(long res, long flags) {} + +PRE_SYSCALL(inotify_add_watch)(long fd, const void *path, long mask) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(inotify_add_watch)(long res, long fd, const void *path, + long mask) {} + +PRE_SYSCALL(inotify_rm_watch)(long fd, long wd) {} + +POST_SYSCALL(inotify_rm_watch)(long res, long fd, long wd) {} + +PRE_SYSCALL(spu_run)(long fd, void *unpc, void *ustatus) {} + +POST_SYSCALL(spu_run)(long res, long fd, unsigned *unpc, unsigned *ustatus) { + if (res >= 0) { + if (unpc) POST_WRITE(unpc, sizeof(*unpc)); + if (ustatus) POST_WRITE(ustatus, sizeof(*ustatus)); + } +} + +PRE_SYSCALL(spu_create)(const void *name, long flags, long mode, long fd) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(spu_create)(long res, const void *name, long flags, long mode, + long fd) {} + +PRE_SYSCALL(mknodat)(long dfd, const void *filename, long mode, long dev) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(mknodat)(long res, long dfd, const void *filename, long mode, + long dev) {} + +PRE_SYSCALL(mkdirat)(long dfd, const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(mkdirat)(long res, long dfd, const void *pathname, long mode) {} + +PRE_SYSCALL(unlinkat)(long dfd, const void *pathname, long flag) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(unlinkat)(long res, long dfd, const void *pathname, long flag) {} + +PRE_SYSCALL(symlinkat)(const void *oldname, long newdfd, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(symlinkat)(long res, const void *oldname, long newdfd, + const void *newname) {} + +PRE_SYSCALL(linkat)(long olddfd, const void *oldname, long newdfd, + const void *newname, long flags) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(linkat)(long res, long olddfd, const void *oldname, long newdfd, + const void *newname, long flags) {} + +PRE_SYSCALL(renameat)(long olddfd, const void *oldname, long newdfd, + const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(renameat)(long res, long olddfd, const void *oldname, long newdfd, + const void *newname) {} + +PRE_SYSCALL(futimesat)(long dfd, const void *filename, void *utimes) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(futimesat)(long res, long dfd, const void *filename, + void *utimes) { + if (res >= 0) { + if (utimes) POST_WRITE(utimes, timeval_sz); + } +} + +PRE_SYSCALL(faccessat)(long dfd, const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(faccessat)(long res, long dfd, const void *filename, long mode) {} + +PRE_SYSCALL(fchmodat)(long dfd, const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fchmodat)(long res, long dfd, const void *filename, long mode) {} + +PRE_SYSCALL(fchownat)(long dfd, const void *filename, long user, long group, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fchownat)(long res, long dfd, const void *filename, long user, + long group, long flag) {} + +PRE_SYSCALL(openat)(long dfd, const void *filename, long flags, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(openat)(long res, long dfd, const void *filename, long flags, + long mode) {} + +PRE_SYSCALL(newfstatat)(long dfd, const void *filename, void *statbuf, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newfstatat)(long res, long dfd, const void *filename, + void *statbuf, long flag) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(fstatat64)(long dfd, const void *filename, void *statbuf, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fstatat64)(long res, long dfd, const void *filename, void *statbuf, + long flag) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(readlinkat)(long dfd, const void *path, void *buf, long bufsiz) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(readlinkat)(long res, long dfd, const void *path, void *buf, + long bufsiz) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(utimensat)(long dfd, const void *filename, void *utimes, + long flags) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(utimensat)(long res, long dfd, const void *filename, void *utimes, + long flags) { + if (res >= 0) { + if (utimes) POST_WRITE(utimes, struct_timespec_sz); + } +} + +PRE_SYSCALL(unshare)(long unshare_flags) {} + +POST_SYSCALL(unshare)(long res, long unshare_flags) {} + +PRE_SYSCALL(splice)(long fd_in, void *off_in, long fd_out, void *off_out, + long len, long flags) {} + +POST_SYSCALL(splice)(long res, long fd_in, void *off_in, long fd_out, + void *off_out, long len, long flags) { + if (res >= 0) { + if (off_in) POST_WRITE(off_in, sizeof(long long)); + if (off_out) POST_WRITE(off_out, sizeof(long long)); + } +} + +PRE_SYSCALL(vmsplice)(long fd, const __sanitizer_iovec *iov, long nr_segs, + long flags) {} + +POST_SYSCALL(vmsplice)(long res, long fd, const __sanitizer_iovec *iov, + long nr_segs, long flags) { + if (res >= 0) { + if (iov) kernel_read_iovec(iov, nr_segs, res); + } +} + +PRE_SYSCALL(tee)(long fdin, long fdout, long len, long flags) {} + +POST_SYSCALL(tee)(long res, long fdin, long fdout, long len, long flags) {} + +PRE_SYSCALL(get_robust_list)(long pid, void *head_ptr, void *len_ptr) {} + +POST_SYSCALL(get_robust_list)(long res, long pid, void *head_ptr, + void *len_ptr) {} + +PRE_SYSCALL(set_robust_list)(void *head, long len) {} + +POST_SYSCALL(set_robust_list)(long res, void *head, long len) {} + +PRE_SYSCALL(getcpu)(void *cpu, void *node, void *cache) {} + +POST_SYSCALL(getcpu)(long res, void *cpu, void *node, void *cache) { + if (res >= 0) { + if (cpu) POST_WRITE(cpu, sizeof(unsigned)); + if (node) POST_WRITE(node, sizeof(unsigned)); + // The third argument to this system call is nowadays unused. + } +} + +PRE_SYSCALL(signalfd)(long ufd, void *user_mask, long sizemask) {} + +POST_SYSCALL(signalfd)(long res, long ufd, kernel_sigset_t *user_mask, + long sizemask) { + if (res >= 0) { + if (user_mask) POST_WRITE(user_mask, sizemask); + } +} + +PRE_SYSCALL(signalfd4)(long ufd, void *user_mask, long sizemask, long flags) {} + +POST_SYSCALL(signalfd4)(long res, long ufd, kernel_sigset_t *user_mask, + long sizemask, long flags) { + if (res >= 0) { + if (user_mask) POST_WRITE(user_mask, sizemask); + } +} + +PRE_SYSCALL(timerfd_create)(long clockid, long flags) {} + +POST_SYSCALL(timerfd_create)(long res, long clockid, long flags) {} + +PRE_SYSCALL(timerfd_settime)(long ufd, long flags, const void *utmr, + void *otmr) { + if (utmr) PRE_READ(utmr, struct_itimerspec_sz); +} + +POST_SYSCALL(timerfd_settime)(long res, long ufd, long flags, const void *utmr, + void *otmr) { + if (res >= 0) { + if (otmr) POST_WRITE(otmr, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(timerfd_gettime)(long ufd, void *otmr) {} + +POST_SYSCALL(timerfd_gettime)(long res, long ufd, void *otmr) { + if (res >= 0) { + if (otmr) POST_WRITE(otmr, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(eventfd)(long count) {} + +POST_SYSCALL(eventfd)(long res, long count) {} + +PRE_SYSCALL(eventfd2)(long count, long flags) {} + +POST_SYSCALL(eventfd2)(long res, long count, long flags) {} + +PRE_SYSCALL(old_readdir)(long arg0, void *arg1, long arg2) {} + +POST_SYSCALL(old_readdir)(long res, long arg0, void *arg1, long arg2) { + // Missing definition of 'struct old_linux_dirent'. +} + +PRE_SYSCALL(pselect6)(long arg0, __sanitizer___kernel_fd_set *arg1, + __sanitizer___kernel_fd_set *arg2, + __sanitizer___kernel_fd_set *arg3, void *arg4, + void *arg5) {} + +POST_SYSCALL(pselect6)(long res, long arg0, __sanitizer___kernel_fd_set *arg1, + __sanitizer___kernel_fd_set *arg2, + __sanitizer___kernel_fd_set *arg3, void *arg4, + void *arg5) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(*arg2)); + if (arg3) POST_WRITE(arg3, sizeof(*arg3)); + if (arg4) POST_WRITE(arg4, struct_timespec_sz); + } +} + +PRE_SYSCALL(ppoll)(__sanitizer_pollfd *arg0, long arg1, void *arg2, + const kernel_sigset_t *arg3, long arg4) { + if (arg3) PRE_READ(arg3, arg4); +} + +POST_SYSCALL(ppoll)(long res, __sanitizer_pollfd *arg0, long arg1, void *arg2, + const void *arg3, long arg4) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, sizeof(*arg0)); + if (arg2) POST_WRITE(arg2, struct_timespec_sz); + } +} + +PRE_SYSCALL(syncfs)(long fd) {} + +POST_SYSCALL(syncfs)(long res, long fd) {} + +PRE_SYSCALL(perf_event_open)(__sanitizer_perf_event_attr *attr_uptr, long pid, + long cpu, long group_fd, long flags) { + if (attr_uptr) PRE_READ(attr_uptr, attr_uptr->size); +} + +POST_SYSCALL(perf_event_open)(long res, __sanitizer_perf_event_attr *attr_uptr, + long pid, long cpu, long group_fd, long flags) {} + +PRE_SYSCALL(mmap_pgoff)(long addr, long len, long prot, long flags, long fd, + long pgoff) {} + +POST_SYSCALL(mmap_pgoff)(long res, long addr, long len, long prot, long flags, + long fd, long pgoff) {} + +PRE_SYSCALL(old_mmap)(void *arg) {} + +POST_SYSCALL(old_mmap)(long res, void *arg) {} + +PRE_SYSCALL(name_to_handle_at)(long dfd, const void *name, void *handle, + void *mnt_id, long flag) {} + +POST_SYSCALL(name_to_handle_at)(long res, long dfd, const void *name, + void *handle, void *mnt_id, long flag) {} + +PRE_SYSCALL(open_by_handle_at)(long mountdirfd, void *handle, long flags) {} + +POST_SYSCALL(open_by_handle_at)(long res, long mountdirfd, void *handle, + long flags) {} + +PRE_SYSCALL(setns)(long fd, long nstype) {} + +POST_SYSCALL(setns)(long res, long fd, long nstype) {} + +PRE_SYSCALL(process_vm_readv)(long pid, const __sanitizer_iovec *lvec, + long liovcnt, const void *rvec, long riovcnt, + long flags) {} + +POST_SYSCALL(process_vm_readv)(long res, long pid, + const __sanitizer_iovec *lvec, long liovcnt, + const void *rvec, long riovcnt, long flags) { + if (res >= 0) { + if (lvec) kernel_write_iovec(lvec, liovcnt, res); + } +} + +PRE_SYSCALL(process_vm_writev)(long pid, const __sanitizer_iovec *lvec, + long liovcnt, const void *rvec, long riovcnt, + long flags) {} + +POST_SYSCALL(process_vm_writev)(long res, long pid, + const __sanitizer_iovec *lvec, long liovcnt, + const void *rvec, long riovcnt, long flags) { + if (res >= 0) { + if (lvec) kernel_read_iovec(lvec, liovcnt, res); + } +} + +PRE_SYSCALL(fork)() { + COMMON_SYSCALL_PRE_FORK(); +} + +POST_SYSCALL(fork)(long res) { + COMMON_SYSCALL_POST_FORK(res); +} + +PRE_SYSCALL(vfork)() { + COMMON_SYSCALL_PRE_FORK(); +} + +POST_SYSCALL(vfork)(long res) { + COMMON_SYSCALL_POST_FORK(res); +} +} // extern "C" + +#undef PRE_SYSCALL +#undef PRE_READ +#undef PRE_WRITE +#undef POST_SYSCALL +#undef POST_READ +#undef POST_WRITE + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_coverage.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_coverage.cc new file mode 100644 index 00000000..9e7a0f8b --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_coverage.cc @@ -0,0 +1,113 @@ +//===-- sanitizer_coverage.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Sanitizer Coverage. +// This file implements run-time support for a poor man's coverage tool. +// +// Compiler instrumentation: +// For every function F the compiler injects the following code: +// if (*Guard) { +// __sanitizer_cov(&F); +// *Guard = 1; +// } +// It's fine to call __sanitizer_cov more than once for a given function. +// +// Run-time: +// - __sanitizer_cov(pc): record that we've executed a given PC. +// - __sanitizer_cov_dump: dump the coverage data to disk. +// For every module of the current process that has coverage data +// this will create a file module_name.PID.sancov. The file format is simple: +// it's just a sorted sequence of 4-byte offsets in the module. +// +// Eventually, this coverage implementation should be obsoleted by a more +// powerful general purpose Clang/LLVM coverage instrumentation. +// Consider this implementation as prototype. +// +// FIXME: support (or at least test with) dlclose. +//===----------------------------------------------------------------------===// + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_mutex.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_flags.h" + +struct CovData { + BlockingMutex mu; + InternalMmapVector v; +}; + +static uptr cov_data_placeholder[sizeof(CovData) / sizeof(uptr)]; +COMPILER_CHECK(sizeof(cov_data_placeholder) >= sizeof(CovData)); +static CovData *cov_data = reinterpret_cast(cov_data_placeholder); + +namespace __sanitizer { + +// Simply add the pc into the vector under lock. If the function is called more +// than once for a given PC it will be inserted multiple times, which is fine. +static void CovAdd(uptr pc) { + BlockingMutexLock lock(&cov_data->mu); + cov_data->v.push_back(pc); +} + +static inline bool CompareLess(const uptr &a, const uptr &b) { + return a < b; +} + +// Dump the coverage on disk. +void CovDump() { +#if !SANITIZER_WINDOWS + BlockingMutexLock lock(&cov_data->mu); + InternalMmapVector &v = cov_data->v; + InternalSort(&v, v.size(), CompareLess); + InternalMmapVector offsets(v.size()); + const uptr *vb = v.data(); + const uptr *ve = vb + v.size(); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + uptr mb, me, off, prot; + InternalScopedBuffer module(4096); + InternalScopedBuffer path(4096 * 2); + for (int i = 0; + proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); + i++) { + if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) + continue; + if (vb >= ve) break; + if (mb <= *vb && *vb < me) { + offsets.clear(); + const uptr *old_vb = vb; + CHECK_LE(off, *vb); + for (; vb < ve && *vb < me; vb++) { + uptr diff = *vb - (i ? mb : 0) + off; + CHECK_LE(diff, 0xffffffffU); + offsets.push_back(static_cast(diff)); + } + char *module_name = StripModuleName(module.data()); + internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov", + module_name, internal_getpid()); + InternalFree(module_name); + uptr fd = OpenFile(path.data(), true); + internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); + internal_close(fd); + if (common_flags()->verbosity) + Report(" CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb); + } + } +#endif // !SANITIZER_WINDOWS +} + +} // namespace __sanitizer + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc) { + CovAdd(reinterpret_cast(pc)); +} +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } +} // extern "C" diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc new file mode 100644 index 00000000..924ed4bc --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc @@ -0,0 +1,145 @@ +//===-- sanitizer_flags.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_flags.h" + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +void SetCommonFlagDefaults() { + CommonFlags *f = common_flags(); + f->symbolize = true; + f->external_symbolizer_path = ""; + f->strip_path_prefix = ""; + f->fast_unwind_on_fatal = false; + f->fast_unwind_on_malloc = true; + f->handle_ioctl = false; + f->malloc_context_size = 1; + f->log_path = "stderr"; + f->verbosity = 0; + f->detect_leaks = false; + f->leak_check_at_exit = true; + f->allocator_may_return_null = false; + f->print_summary = true; +} + +void ParseCommonFlagsFromString(const char *str) { + CommonFlags *f = common_flags(); + ParseFlag(str, &f->symbolize, "symbolize"); + ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path"); + ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); + ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); + ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); + ParseFlag(str, &f->handle_ioctl, "handle_ioctl"); + ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); + ParseFlag(str, &f->log_path, "log_path"); + ParseFlag(str, &f->verbosity, "verbosity"); + ParseFlag(str, &f->detect_leaks, "detect_leaks"); + ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit"); + ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null"); + ParseFlag(str, &f->print_summary, "print_summary"); + + // Do a sanity check for certain flags. + if (f->malloc_context_size < 1) + f->malloc_context_size = 1; +} + +static bool GetFlagValue(const char *env, const char *name, + const char **value, int *value_length) { + if (env == 0) + return false; + const char *pos = 0; + for (;;) { + pos = internal_strstr(env, name); + if (pos == 0) + return false; + if (pos != env && ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) { + // Seems to be middle of another flag name or value. + env = pos + 1; + continue; + } + break; + } + pos += internal_strlen(name); + const char *end; + if (pos[0] != '=') { + end = pos; + } else { + pos += 1; + if (pos[0] == '"') { + pos += 1; + end = internal_strchr(pos, '"'); + } else if (pos[0] == '\'') { + pos += 1; + end = internal_strchr(pos, '\''); + } else { + // Read until the next space or colon. + end = pos + internal_strcspn(pos, " :"); + } + if (end == 0) + end = pos + internal_strlen(pos); + } + *value = pos; + *value_length = end - pos; + return true; +} + +static bool StartsWith(const char *flag, int flag_length, const char *value) { + if (!flag || !value) + return false; + int value_length = internal_strlen(value); + return (flag_length >= value_length) && + (0 == internal_strncmp(flag, value, value_length)); +} + +void ParseFlag(const char *env, bool *flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + if (StartsWith(value, value_length, "0") || + StartsWith(value, value_length, "no") || + StartsWith(value, value_length, "false")) + *flag = false; + if (StartsWith(value, value_length, "1") || + StartsWith(value, value_length, "yes") || + StartsWith(value, value_length, "true")) + *flag = true; +} + +void ParseFlag(const char *env, int *flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + *flag = static_cast(internal_atoll(value)); +} + +static LowLevelAllocator allocator_for_flags; + +void ParseFlag(const char *env, const char **flag, const char *name) { + const char *value; + int value_length; + if (!GetFlagValue(env, name, &value, &value_length)) + return; + // Copy the flag value. Don't use locks here, as flags are parsed at + // tool startup. + char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1)); + internal_memcpy(value_copy, value, value_length); + value_copy[value_length] = '\0'; + *flag = value_copy; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_flags.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_flags.h new file mode 100644 index 00000000..9461dff8 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_flags.h @@ -0,0 +1,68 @@ +//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_FLAGS_H +#define SANITIZER_FLAGS_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +void ParseFlag(const char *env, bool *flag, const char *name); +void ParseFlag(const char *env, int *flag, const char *name); +void ParseFlag(const char *env, const char **flag, const char *name); + +struct CommonFlags { + // If set, use the online symbolizer from common sanitizer runtime. + bool symbolize; + // Path to external symbolizer. + const char *external_symbolizer_path; + // Strips this prefix from file paths in error reports. + const char *strip_path_prefix; + // Use fast (frame-pointer-based) unwinder on fatal errors (if available). + bool fast_unwind_on_fatal; + // Use fast (frame-pointer-based) unwinder on malloc/free (if available). + bool fast_unwind_on_malloc; + // Intercept and handle ioctl requests. + bool handle_ioctl; + // Max number of stack frames kept for each allocation/deallocation. + int malloc_context_size; + // Write logs to "log_path.pid". + // The special values are "stdout" and "stderr". + // The default is "stderr". + const char *log_path; + // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). + int verbosity; + // Enable memory leak detection. + bool detect_leaks; + // Invoke leak checking in an atexit handler. Has no effect if + // detect_leaks=false, or if __lsan_do_leak_check() is called before the + // handler has a chance to run. + bool leak_check_at_exit; + // If false, the allocator will crash instead of returning 0 on out-of-memory. + bool allocator_may_return_null; + // If false, disable printing error summaries in addition to error reports. + bool print_summary; +}; + +inline CommonFlags *common_flags() { + static CommonFlags f; + return &f; +} + +void SetCommonFlagDefaults(); +void ParseCommonFlagsFromString(const char *str); + +} // namespace __sanitizer + +#endif // SANITIZER_FLAGS_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h new file mode 100644 index 00000000..daa724be --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h @@ -0,0 +1,306 @@ +//===-- sanitizer_internal_defs.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer. +// It contains macro used in run-time libraries code. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_DEFS_H +#define SANITIZER_DEFS_H + +#include "sanitizer_platform.h" + +// Only use SANITIZER_*ATTRIBUTE* before the function return type! +#if SANITIZER_WINDOWS +# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport) +// FIXME find out what we need on Windows, if anything. +# define SANITIZER_WEAK_ATTRIBUTE +#elif defined(SANITIZER_GO) +# define SANITIZER_INTERFACE_ATTRIBUTE +# define SANITIZER_WEAK_ATTRIBUTE +#else +# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) +# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) +#endif + +#if SANITIZER_LINUX && !defined(SANITIZER_GO) +# define SANITIZER_SUPPORTS_WEAK_HOOKS 1 +#else +# define SANITIZER_SUPPORTS_WEAK_HOOKS 0 +#endif + +#if __LP64__ || defined(_WIN64) +# define SANITIZER_WORDSIZE 64 +#else +# define SANITIZER_WORDSIZE 32 +#endif + +// GCC does not understand __has_feature +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif + +// For portability reasons we do not include stddef.h, stdint.h or any other +// system header, but we do need some basic types that are not defined +// in a portable way by the language itself. +namespace __sanitizer { + +#if defined(_WIN64) +// 64-bit Windows uses LLP64 data model. +typedef unsigned long long uptr; // NOLINT +typedef signed long long sptr; // NOLINT +#else +typedef unsigned long uptr; // NOLINT +typedef signed long sptr; // NOLINT +#endif // defined(_WIN64) +#if defined(__x86_64__) +// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use +// 64-bit pointer to unwind stack frame. +typedef unsigned long long uhwptr; // NOLINT +#else +typedef uptr uhwptr; // NOLINT +#endif +typedef unsigned char u8; +typedef unsigned short u16; // NOLINT +typedef unsigned int u32; +typedef unsigned long long u64; // NOLINT +typedef signed char s8; +typedef signed short s16; // NOLINT +typedef signed int s32; +typedef signed long long s64; // NOLINT +typedef int fd_t; + +// WARNING: OFF_T may be different from OS type off_t, depending on the value of +// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls +// like pread and mmap, as opposed to pread64 and mmap64. +// Mac and Linux/x86-64 are special. +#if SANITIZER_MAC || (SANITIZER_LINUX && defined(__x86_64__)) +typedef u64 OFF_T; +#else +typedef uptr OFF_T; +#endif +typedef u64 OFF64_T; + +#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC +typedef uptr operator_new_size_type; +#else +typedef u32 operator_new_size_type; +#endif +} // namespace __sanitizer + +extern "C" { + // Tell the tools to write their reports to "path." instead of stderr. + // The special values are "stdout" and "stderr". + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_set_report_path(const char *path); + + // Notify the tools that the sandbox is going to be turned on. The reserved + // parameter will be used in the future to hold a structure with functions + // that the tools may call to bypass the sandbox. + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + void __sanitizer_sandbox_on_notify(void *reserved); + + // This function is called by the tool when it has just finished reporting + // an error. 'error_summary' is a one-line string that summarizes + // the error message. This function can be overridden by the client. + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + void __sanitizer_report_error_summary(const char *error_summary); + + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc); + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_annotate_contiguous_container(void *beg, void *end, + void *old_mid, void *new_mid); +} // extern "C" + + +using namespace __sanitizer; // NOLINT +// ----------- ATTENTION ------------- +// This header should NOT include any other headers to avoid portability issues. + +// Common defs. +#define INLINE inline +#define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +#define WEAK SANITIZER_WEAK_ATTRIBUTE + +// Platform-specific defs. +#if defined(_MSC_VER) +# define ALWAYS_INLINE __forceinline +// FIXME(timurrrr): do we need this on Windows? +# define ALIAS(x) +# define ALIGNED(x) __declspec(align(x)) +# define FORMAT(f, a) +# define NOINLINE __declspec(noinline) +# define NORETURN __declspec(noreturn) +# define THREADLOCAL __declspec(thread) +# define NOTHROW +# define LIKELY(x) (x) +# define UNLIKELY(x) (x) +# define UNUSED +# define USED +# define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */ +#else // _MSC_VER +# define ALWAYS_INLINE inline __attribute__((always_inline)) +# define ALIAS(x) __attribute__((alias(x))) +// Please only use the ALIGNED macro before the type. +// Using ALIGNED after the variable declaration is not portable! +# define ALIGNED(x) __attribute__((aligned(x))) +# define FORMAT(f, a) __attribute__((format(printf, f, a))) +# define NOINLINE __attribute__((noinline)) +# define NORETURN __attribute__((noreturn)) +# define THREADLOCAL __thread +# define NOTHROW throw() +# define LIKELY(x) __builtin_expect(!!(x), 1) +# define UNLIKELY(x) __builtin_expect(!!(x), 0) +# define UNUSED __attribute__((unused)) +# define USED __attribute__((used)) +# if defined(__i386__) || defined(__x86_64__) +// __builtin_prefetch(x) generates prefetchnt0 on x86 +# define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x)) +# else +# define PREFETCH(x) __builtin_prefetch(x) +# endif +#endif // _MSC_VER + +// Unaligned versions of basic types. +typedef ALIGNED(1) u16 uu16; +typedef ALIGNED(1) u32 uu32; +typedef ALIGNED(1) u64 uu64; +typedef ALIGNED(1) s16 us16; +typedef ALIGNED(1) s32 us32; +typedef ALIGNED(1) s64 us64; + +#if SANITIZER_WINDOWS +typedef unsigned long DWORD; // NOLINT +typedef DWORD thread_return_t; +# define THREAD_CALLING_CONV __stdcall +#else // _WIN32 +typedef void* thread_return_t; +# define THREAD_CALLING_CONV +#endif // _WIN32 +typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg); + +// NOTE: Functions below must be defined in each run-time. +namespace __sanitizer { +void NORETURN Die(); + +// FIXME: No, this shouldn't be in the sanitizer interface. +SANITIZER_INTERFACE_ATTRIBUTE +void NORETURN CheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2); +} // namespace __sanitizer + +// Check macro +#define RAW_CHECK_MSG(expr, msg) do { \ + if (!(expr)) { \ + RawWrite(msg); \ + Die(); \ + } \ +} while (0) + +#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr) + +#define CHECK_IMPL(c1, op, c2) \ + do { \ + __sanitizer::u64 v1 = (u64)(c1); \ + __sanitizer::u64 v2 = (u64)(c2); \ + if (!(v1 op v2)) \ + __sanitizer::CheckFailed(__FILE__, __LINE__, \ + "(" #c1 ") " #op " (" #c2 ")", v1, v2); \ + } while (false) \ +/**/ + +#define CHECK(a) CHECK_IMPL((a), !=, 0) +#define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b)) +#define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b)) +#define CHECK_LT(a, b) CHECK_IMPL((a), <, (b)) +#define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b)) +#define CHECK_GT(a, b) CHECK_IMPL((a), >, (b)) +#define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b)) + +#if TSAN_DEBUG +#define DCHECK(a) CHECK(a) +#define DCHECK_EQ(a, b) CHECK_EQ(a, b) +#define DCHECK_NE(a, b) CHECK_NE(a, b) +#define DCHECK_LT(a, b) CHECK_LT(a, b) +#define DCHECK_LE(a, b) CHECK_LE(a, b) +#define DCHECK_GT(a, b) CHECK_GT(a, b) +#define DCHECK_GE(a, b) CHECK_GE(a, b) +#else +#define DCHECK(a) +#define DCHECK_EQ(a, b) +#define DCHECK_NE(a, b) +#define DCHECK_LT(a, b) +#define DCHECK_LE(a, b) +#define DCHECK_GT(a, b) +#define DCHECK_GE(a, b) +#endif + +#define UNREACHABLE(msg) do { \ + CHECK(0 && msg); \ + Die(); \ +} while (0) + +#define UNIMPLEMENTED() UNREACHABLE("unimplemented") + +#define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__) + +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) + +#define IMPL_PASTE(a, b) a##b +#define IMPL_COMPILER_ASSERT(pred, line) \ + typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1] + +// Limits for integral types. We have to redefine it in case we don't +// have stdint.h (like in Visual Studio 9). +#undef __INT64_C +#undef __UINT64_C +#if SANITIZER_WORDSIZE == 64 +# define __INT64_C(c) c ## L +# define __UINT64_C(c) c ## UL +#else +# define __INT64_C(c) c ## LL +# define __UINT64_C(c) c ## ULL +#endif // SANITIZER_WORDSIZE == 64 +#undef INT32_MIN +#define INT32_MIN (-2147483647-1) +#undef INT32_MAX +#define INT32_MAX (2147483647) +#undef UINT32_MAX +#define UINT32_MAX (4294967295U) +#undef INT64_MIN +#define INT64_MIN (-__INT64_C(9223372036854775807)-1) +#undef INT64_MAX +#define INT64_MAX (__INT64_C(9223372036854775807)) +#undef UINT64_MAX +#define UINT64_MAX (__UINT64_C(18446744073709551615)) + +enum LinkerInitialized { LINKER_INITIALIZED = 0 }; + +#if !defined(_MSC_VER) || defined(__clang__) +# define GET_CALLER_PC() (uptr)__builtin_return_address(0) +# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0) +#else +extern "C" void* _ReturnAddress(void); +# pragma intrinsic(_ReturnAddress) +# define GET_CALLER_PC() (uptr)_ReturnAddress() +// CaptureStackBackTrace doesn't need to know BP on Windows. +// FIXME: This macro is still used when printing error reports though it's not +// clear if the BP value is needed in the ASan reports on Windows. +# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF +#endif + +#define HANDLE_EINTR(res, f) \ + { \ + int rverrno; \ + do { \ + res = (f); \ + } while (internal_iserror(res, &rverrno) && rverrno == EINTR); \ + } + +#endif // SANITIZER_DEFS_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_lfstack.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_lfstack.h new file mode 100644 index 00000000..08841390 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_lfstack.h @@ -0,0 +1,73 @@ +//===-- sanitizer_lfstack.h -=-----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Lock-free stack. +// Uses 32/17 bits as ABA-counter on 32/64-bit platforms. +// The memory passed to Push() must not be ever munmap'ed. +// The type T must contain T *next field. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_LFSTACK_H +#define SANITIZER_LFSTACK_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_common.h" +#include "sanitizer_atomic.h" + +namespace __sanitizer { + +template +struct LFStack { + void Clear() { + atomic_store(&head_, 0, memory_order_relaxed); + } + + bool Empty() const { + return (atomic_load(&head_, memory_order_relaxed) & kPtrMask) == 0; + } + + void Push(T *p) { + u64 cmp = atomic_load(&head_, memory_order_relaxed); + for (;;) { + u64 cnt = (cmp & kCounterMask) + kCounterInc; + u64 xch = (u64)(uptr)p | cnt; + p->next = (T*)(uptr)(cmp & kPtrMask); + if (atomic_compare_exchange_weak(&head_, &cmp, xch, + memory_order_release)) + break; + } + } + + T *Pop() { + u64 cmp = atomic_load(&head_, memory_order_acquire); + for (;;) { + T *cur = (T*)(uptr)(cmp & kPtrMask); + if (cur == 0) + return 0; + T *nxt = cur->next; + u64 cnt = (cmp & kCounterMask); + u64 xch = (u64)(uptr)nxt | cnt; + if (atomic_compare_exchange_weak(&head_, &cmp, xch, + memory_order_acquire)) + return cur; + } + } + + // private: + static const int kCounterBits = FIRST_32_SECOND_64(32, 17); + static const u64 kPtrMask = ((u64)-1) >> kCounterBits; + static const u64 kCounterMask = ~kPtrMask; + static const u64 kCounterInc = kPtrMask + 1; + + atomic_uint64_t head_; +}; +} // namespace __sanitizer + +#endif // #ifndef SANITIZER_LFSTACK_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc new file mode 100644 index 00000000..72ddf0fd --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc @@ -0,0 +1,234 @@ +//===-- sanitizer_libc.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. See sanitizer_libc.h for details. +//===----------------------------------------------------------------------===// +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +s64 internal_atoll(const char *nptr) { + return internal_simple_strtoll(nptr, (char**)0, 10); +} + +void *internal_memchr(const void *s, int c, uptr n) { + const char* t = (char*)s; + for (uptr i = 0; i < n; ++i, ++t) + if (*t == c) + return (void*)t; + return 0; +} + +int internal_memcmp(const void* s1, const void* s2, uptr n) { + const char* t1 = (char*)s1; + const char* t2 = (char*)s2; + for (uptr i = 0; i < n; ++i, ++t1, ++t2) + if (*t1 != *t2) + return *t1 < *t2 ? -1 : 1; + return 0; +} + +void *internal_memcpy(void *dest, const void *src, uptr n) { + char *d = (char*)dest; + char *s = (char*)src; + for (uptr i = 0; i < n; ++i) + d[i] = s[i]; + return dest; +} + +void *internal_memmove(void *dest, const void *src, uptr n) { + char *d = (char*)dest; + char *s = (char*)src; + sptr i, signed_n = (sptr)n; + CHECK_GE(signed_n, 0); + if (d < s) { + for (i = 0; i < signed_n; ++i) + d[i] = s[i]; + } else { + if (d > s && signed_n > 0) + for (i = signed_n - 1; i >= 0 ; --i) { + d[i] = s[i]; + } + } + return dest; +} + +void *internal_memset(void* s, int c, uptr n) { + // The next line prevents Clang from making a call to memset() instead of the + // loop below. + // FIXME: building the runtime with -ffreestanding is a better idea. However + // there currently are linktime problems due to PR12396. + char volatile *t = (char*)s; + for (uptr i = 0; i < n; ++i, ++t) { + *t = c; + } + return s; +} + +uptr internal_strcspn(const char *s, const char *reject) { + uptr i; + for (i = 0; s[i]; i++) { + if (internal_strchr(reject, s[i]) != 0) + return i; + } + return i; +} + +char* internal_strdup(const char *s) { + uptr len = internal_strlen(s); + char *s2 = (char*)InternalAlloc(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + +int internal_strcmp(const char *s1, const char *s2) { + while (true) { + unsigned c1 = *s1; + unsigned c2 = *s2; + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (c1 == 0) break; + s1++; + s2++; + } + return 0; +} + +int internal_strncmp(const char *s1, const char *s2, uptr n) { + for (uptr i = 0; i < n; i++) { + unsigned c1 = *s1; + unsigned c2 = *s2; + if (c1 != c2) return (c1 < c2) ? -1 : 1; + if (c1 == 0) break; + s1++; + s2++; + } + return 0; +} + +char* internal_strchr(const char *s, int c) { + while (true) { + if (*s == (char)c) + return (char*)s; + if (*s == 0) + return 0; + s++; + } +} + +char *internal_strchrnul(const char *s, int c) { + char *res = internal_strchr(s, c); + if (!res) + res = (char*)s + internal_strlen(s); + return res; +} + +char *internal_strrchr(const char *s, int c) { + const char *res = 0; + for (uptr i = 0; s[i]; i++) { + if (s[i] == c) res = s + i; + } + return (char*)res; +} + +uptr internal_strlen(const char *s) { + uptr i = 0; + while (s[i]) i++; + return i; +} + +char *internal_strncat(char *dst, const char *src, uptr n) { + uptr len = internal_strlen(dst); + uptr i; + for (i = 0; i < n && src[i]; i++) + dst[len + i] = src[i]; + dst[len + i] = 0; + return dst; +} + +char *internal_strncpy(char *dst, const char *src, uptr n) { + uptr i; + for (i = 0; i < n && src[i]; i++) + dst[i] = src[i]; + internal_memset(dst + i, '\0', n - i); + return dst; +} + +uptr internal_strnlen(const char *s, uptr maxlen) { + uptr i = 0; + while (i < maxlen && s[i]) i++; + return i; +} + +char *internal_strstr(const char *haystack, const char *needle) { + // This is O(N^2), but we are not using it in hot places. + uptr len1 = internal_strlen(haystack); + uptr len2 = internal_strlen(needle); + if (len1 < len2) return 0; + for (uptr pos = 0; pos <= len1 - len2; pos++) { + if (internal_memcmp(haystack + pos, needle, len2) == 0) + return (char*)haystack + pos; + } + return 0; +} + +s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { + CHECK_EQ(base, 10); + while (IsSpace(*nptr)) nptr++; + int sgn = 1; + u64 res = 0; + bool have_digits = false; + char *old_nptr = (char*)nptr; + if (*nptr == '+') { + sgn = 1; + nptr++; + } else if (*nptr == '-') { + sgn = -1; + nptr++; + } + while (IsDigit(*nptr)) { + res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX; + int digit = ((*nptr) - '0'); + res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX; + have_digits = true; + nptr++; + } + if (endptr != 0) { + *endptr = (have_digits) ? (char*)nptr : old_nptr; + } + if (sgn > 0) { + return (s64)(Min((u64)INT64_MAX, res)); + } else { + return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1); + } +} + +bool mem_is_zero(const char *beg, uptr size) { + CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check. + const char *end = beg + size; + uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr)); + uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr)); + uptr all = 0; + // Prologue. + for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++) + all |= *mem; + // Aligned loop. + for (; aligned_beg < aligned_end; aligned_beg++) + all |= *aligned_beg; + // Epilogue. + if ((char*)aligned_end >= beg) + for (const char *mem = (char*)aligned_end; mem < end; mem++) + all |= *mem; + return all == 0; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_libc.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libc.h new file mode 100644 index 00000000..187a714a --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libc.h @@ -0,0 +1,100 @@ +//===-- sanitizer_libc.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// These tools can not use some of the libc functions directly because those +// functions are intercepted. Instead, we implement a tiny subset of libc here. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LIBC_H +#define SANITIZER_LIBC_H + +// ----------- ATTENTION ------------- +// This header should NOT include any other headers from sanitizer runtime. +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// internal_X() is a custom implementation of X() for use in RTL. + +// String functions +s64 internal_atoll(const char *nptr); +void *internal_memchr(const void *s, int c, uptr n); +int internal_memcmp(const void* s1, const void* s2, uptr n); +void *internal_memcpy(void *dest, const void *src, uptr n); +void *internal_memmove(void *dest, const void *src, uptr n); +// Should not be used in performance-critical places. +void *internal_memset(void *s, int c, uptr n); +char* internal_strchr(const char *s, int c); +char *internal_strchrnul(const char *s, int c); +int internal_strcmp(const char *s1, const char *s2); +uptr internal_strcspn(const char *s, const char *reject); +char *internal_strdup(const char *s); +uptr internal_strlen(const char *s); +char *internal_strncat(char *dst, const char *src, uptr n); +int internal_strncmp(const char *s1, const char *s2, uptr n); +char *internal_strncpy(char *dst, const char *src, uptr n); +uptr internal_strnlen(const char *s, uptr maxlen); +char *internal_strrchr(const char *s, int c); +// This is O(N^2), but we are not using it in hot places. +char *internal_strstr(const char *haystack, const char *needle); +// Works only for base=10 and doesn't set errno. +s64 internal_simple_strtoll(const char *nptr, char **endptr, int base); +int internal_snprintf(char *buffer, uptr length, const char *format, ...); + +// Return true if all bytes in [mem, mem+size) are zero. +// Optimized for the case when the result is true. +bool mem_is_zero(const char *mem, uptr size); + + +// Memory +uptr internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset); +uptr internal_munmap(void *addr, uptr length); + +// I/O +const fd_t kInvalidFd = -1; +const fd_t kStdinFd = 0; +const fd_t kStdoutFd = 1; +const fd_t kStderrFd = 2; +uptr internal_close(fd_t fd); +int internal_isatty(fd_t fd); + +// Use __sanitizer::OpenFile() instead. +uptr internal_open(const char *filename, int flags); +uptr internal_open(const char *filename, int flags, u32 mode); + +uptr internal_read(fd_t fd, void *buf, uptr count); +uptr internal_write(fd_t fd, const void *buf, uptr count); + +// OS +uptr internal_filesize(fd_t fd); // -1 on error. +uptr internal_stat(const char *path, void *buf); +uptr internal_lstat(const char *path, void *buf); +uptr internal_fstat(fd_t fd, void *buf); +uptr internal_dup2(int oldfd, int newfd); +uptr internal_readlink(const char *path, char *buf, uptr bufsize); +uptr internal_unlink(const char *path); +void NORETURN internal__exit(int exitcode); +uptr internal_lseek(fd_t fd, OFF_T offset, int whence); + +uptr internal_ptrace(int request, int pid, void *addr, void *data); +uptr internal_waitpid(int pid, int *status, int options); +uptr internal_getpid(); +uptr internal_getppid(); + +// Threading +uptr internal_sched_yield(); + +// Error handling +bool internal_iserror(uptr retval, int *rverrno = 0); + +} // namespace __sanitizer + +#endif // SANITIZER_LIBC_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc new file mode 100644 index 00000000..0f193a13 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libignore.cc @@ -0,0 +1,105 @@ +//===-- 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_flags.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(const char *name) { + BlockingMutexLock lock(&mutex_); + // Try to match suppressions with symlink target. + InternalScopedBuffer buf(4096); + if (name != 0 && internal_readlink(name, buf.data(), buf.size() - 1) > 0 && + buf.data()[0]) { + for (uptr i = 0; i < count_; i++) { + Lib *lib = &libs_[i]; + if (!lib->loaded && lib->real_name == 0 && + TemplateMatch(lib->templ, name)) + lib->real_name = internal_strdup(buf.data()); + } + } + + // Scan suppressions list and find newly loaded and unloaded libraries. + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + InternalScopedBuffer module(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, module.data(), module.size(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) + continue; + if (TemplateMatch(lib->templ, module.data()) || + (lib->real_name != 0 && + internal_strcmp(lib->real_name, module.data()) == 0)) { + if (loaded) { + Report("%s: called_from_lib suppression '%s' is matched against" + " 2 libraries: '%s' and '%s'\n", + SanitizerToolName, lib->templ, lib->name, module.data()); + Die(); + } + loaded = true; + if (lib->loaded) + continue; + if (common_flags()->verbosity) + Report("Matched called_from_lib suppression '%s' against library" + " '%s'\n", lib->templ, module.data()); + lib->loaded = true; + lib->name = internal_strdup(module.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(0); +} + +} // namespace __sanitizer + +#endif // #if SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h new file mode 100644 index 00000000..8e1d584d --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_libignore.h @@ -0,0 +1,84 @@ +//===-- 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(const char *name); + + // 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; + char *real_name; // target of symlink + 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/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc new file mode 100644 index 00000000..b98ad0ab --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -0,0 +1,858 @@ +//===-- sanitizer_linux.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements linux-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_linux.h" +#include "sanitizer_mutex.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" + +#include +#include +#include +#include +#if !SANITIZER_ANDROID +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !SANITIZER_ANDROID +#include +#endif + +// +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; + +// is broken on some linux distributions. +const int FUTEX_WAIT = 0; +const int FUTEX_WAKE = 1; + +// Are we using 32-bit or 64-bit syscalls? +// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 +// but it still needs to use 64-bit syscalls. +#if defined(__x86_64__) || SANITIZER_WORDSIZE == 64 +# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 +#else +# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 +#endif + +namespace __sanitizer { + +#ifdef __x86_64__ +#include "sanitizer_syscall_linux_x86_64.inc" +#else +#include "sanitizer_syscall_generic.inc" +#endif + +// --------------- sanitizer_libc.h +uptr internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_mmap, (uptr)addr, length, prot, flags, fd, + offset); +#else + return internal_syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); +#endif +} + +uptr internal_munmap(void *addr, uptr length) { + return internal_syscall(__NR_munmap, (uptr)addr, length); +} + +uptr internal_close(fd_t fd) { + return internal_syscall(__NR_close, fd); +} + +uptr internal_open(const char *filename, int flags) { + return internal_syscall(__NR_open, (uptr)filename, flags); +} + +uptr internal_open(const char *filename, int flags, u32 mode) { + return internal_syscall(__NR_open, (uptr)filename, flags, mode); +} + +uptr OpenFile(const char *filename, bool write) { + return internal_open(filename, + write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + sptr res; + HANDLE_EINTR(res, (sptr)internal_syscall(__NR_read, fd, (uptr)buf, count)); + return res; +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + sptr res; + HANDLE_EINTR(res, (sptr)internal_syscall(__NR_write, fd, (uptr)buf, count)); + return res; +} + +#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS +static void stat64_to_stat(struct stat64 *in, struct stat *out) { + internal_memset(out, 0, sizeof(*out)); + out->st_dev = in->st_dev; + out->st_ino = in->st_ino; + out->st_mode = in->st_mode; + out->st_nlink = in->st_nlink; + out->st_uid = in->st_uid; + out->st_gid = in->st_gid; + out->st_rdev = in->st_rdev; + out->st_size = in->st_size; + out->st_blksize = in->st_blksize; + out->st_blocks = in->st_blocks; + out->st_atime = in->st_atime; + out->st_mtime = in->st_mtime; + out->st_ctime = in->st_ctime; + out->st_ino = in->st_ino; +} +#endif + +uptr internal_stat(const char *path, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_stat, (uptr)path, (uptr)buf); +#else + struct stat64 buf64; + int res = internal_syscall(__NR_stat64, path, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; +#endif +} + +uptr internal_lstat(const char *path, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_lstat, (uptr)path, (uptr)buf); +#else + struct stat64 buf64; + int res = internal_syscall(__NR_lstat64, path, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; +#endif +} + +uptr internal_fstat(fd_t fd, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return internal_syscall(__NR_fstat, fd, (uptr)buf); +#else + struct stat64 buf64; + int res = internal_syscall(__NR_fstat64, fd, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; +#endif +} + +uptr internal_filesize(fd_t fd) { + struct stat st; + if (internal_fstat(fd, &st)) + return -1; + return (uptr)st.st_size; +} + +uptr internal_dup2(int oldfd, int newfd) { + return internal_syscall(__NR_dup2, oldfd, newfd); +} + +uptr internal_readlink(const char *path, char *buf, uptr bufsize) { + return internal_syscall(__NR_readlink, (uptr)path, (uptr)buf, bufsize); +} + +uptr internal_unlink(const char *path) { + return internal_syscall(__NR_unlink, (uptr)path); +} + +uptr internal_sched_yield() { + return internal_syscall(__NR_sched_yield); +} + +void internal__exit(int exitcode) { + internal_syscall(__NR_exit_group, exitcode); + Die(); // Unreachable. +} + +uptr internal_execve(const char *filename, char *const argv[], + char *const envp[]) { + return internal_syscall(__NR_execve, (uptr)filename, (uptr)argv, (uptr)envp); +} + +// ----------------- sanitizer_common.h +bool FileExists(const char *filename) { + struct stat st; + if (internal_stat(filename, &st)) + return false; + // Sanity check: filename is a regular file. + return S_ISREG(st.st_mode); +} + +uptr GetTid() { + return internal_syscall(__NR_gettid); +} + +u64 NanoTime() { + kernel_timeval tv; + internal_memset(&tv, 0, sizeof(tv)); + internal_syscall(__NR_gettimeofday, (uptr)&tv, 0); + return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; +} + +// Like getenv, but reads env directly from /proc and does not use libc. +// This function should be called first inside __asan_init. +const char *GetEnv(const char *name) { + static char *environ; + static uptr len; + static bool inited; + if (!inited) { + inited = true; + uptr environ_size; + len = ReadFileToBuffer("/proc/self/environ", + &environ, &environ_size, 1 << 26); + } + if (!environ || len == 0) return 0; + uptr namelen = internal_strlen(name); + const char *p = environ; + while (*p != '\0') { // will happen at the \0\0 that terminates the buffer + // proc file has the format NAME=value\0NAME=value\0NAME=value\0... + const char* endp = + (char*)internal_memchr(p, '\0', len - (p - environ)); + if (endp == 0) // this entry isn't NUL terminated + return 0; + else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. + return p + namelen + 1; // point after = + p = endp + 1; + } + return 0; // Not found. +} + +extern "C" { + SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end; +} + +#if !SANITIZER_GO +static void ReadNullSepFileToArray(const char *path, char ***arr, + int arr_size) { + char *buff; + uptr buff_size = 0; + *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray"); + ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024); + (*arr)[0] = buff; + int count, i; + for (count = 1, i = 1; ; i++) { + if (buff[i] == 0) { + if (buff[i+1] == 0) break; + (*arr)[count] = &buff[i+1]; + CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible. + count++; + } + } + (*arr)[count] = 0; +} +#endif + +static void GetArgsAndEnv(char*** argv, char*** envp) { +#if !SANITIZER_GO + if (&__libc_stack_end) { +#endif + uptr* stack_end = (uptr*)__libc_stack_end; + int argc = *stack_end; + *argv = (char**)(stack_end + 1); + *envp = (char**)(stack_end + argc + 2); +#if !SANITIZER_GO + } else { + static const int kMaxArgv = 2000, kMaxEnvp = 2000; + ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv); + ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp); + } +#endif +} + +void ReExec() { + char **argv, **envp; + GetArgsAndEnv(&argv, &envp); + uptr rv = internal_execve("/proc/self/exe", argv, envp); + int rverrno; + CHECK_EQ(internal_iserror(rv, &rverrno), true); + Printf("execve failed, errno %d\n", rverrno); + Die(); +} + +void PrepareForSandboxing() { + // Some kinds of sandboxes may forbid filesystem access, so we won't be able + // to read the file mappings from /proc/self/maps. Luckily, neither the + // process will be able to load additional libraries, so it's fine to use the + // cached mappings. + MemoryMappingLayout::CacheMemoryMappings(); + // Same for /proc/self/exe in the symbolizer. +#if !SANITIZER_GO + if (Symbolizer *sym = Symbolizer::GetOrNull()) + sym->PrepareForSandboxing(); +#endif +} + +// ----------------- sanitizer_procmaps.h +// Linker initialized. +ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_; +StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized. + +MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { + proc_self_maps_.len = + ReadFileToBuffer("/proc/self/maps", &proc_self_maps_.data, + &proc_self_maps_.mmaped_size, 1 << 26); + if (cache_enabled) { + if (proc_self_maps_.mmaped_size == 0) { + LoadFromCache(); + CHECK_GT(proc_self_maps_.len, 0); + } + } else { + CHECK_GT(proc_self_maps_.mmaped_size, 0); + } + Reset(); + // FIXME: in the future we may want to cache the mappings on demand only. + if (cache_enabled) + CacheMemoryMappings(); +} + +MemoryMappingLayout::~MemoryMappingLayout() { + // Only unmap the buffer if it is different from the cached one. Otherwise + // it will be unmapped when the cache is refreshed. + if (proc_self_maps_.data != cached_proc_self_maps_.data) { + UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size); + } +} + +void MemoryMappingLayout::Reset() { + current_ = proc_self_maps_.data; +} + +// static +void MemoryMappingLayout::CacheMemoryMappings() { + SpinMutexLock l(&cache_lock_); + // Don't invalidate the cache if the mappings are unavailable. + ProcSelfMapsBuff old_proc_self_maps; + old_proc_self_maps = cached_proc_self_maps_; + cached_proc_self_maps_.len = + ReadFileToBuffer("/proc/self/maps", &cached_proc_self_maps_.data, + &cached_proc_self_maps_.mmaped_size, 1 << 26); + if (cached_proc_self_maps_.mmaped_size == 0) { + cached_proc_self_maps_ = old_proc_self_maps; + } else { + if (old_proc_self_maps.mmaped_size) { + UnmapOrDie(old_proc_self_maps.data, + old_proc_self_maps.mmaped_size); + } + } +} + +void MemoryMappingLayout::LoadFromCache() { + SpinMutexLock l(&cache_lock_); + if (cached_proc_self_maps_.data) { + proc_self_maps_ = cached_proc_self_maps_; + } +} + +// Parse a hex value in str and update str. +static uptr ParseHex(char **str) { + uptr x = 0; + char *s; + for (s = *str; ; s++) { + char c = *s; + uptr v = 0; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + v = c - 'A' + 10; + else + break; + x = x * 16 + v; + } + *str = s; + return x; +} + +static bool IsOneOf(char c, char c1, char c2) { + return c == c1 || c == c2; +} + +static bool IsDecimal(char c) { + return c >= '0' && c <= '9'; +} + +static bool IsHex(char c) { + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f'); +} + +static uptr ReadHex(const char *p) { + uptr v = 0; + for (; IsHex(p[0]); p++) { + if (p[0] >= '0' && p[0] <= '9') + v = v * 16 + p[0] - '0'; + else + v = v * 16 + p[0] - 'a' + 10; + } + return v; +} + +static uptr ReadDecimal(const char *p) { + uptr v = 0; + for (; IsDecimal(p[0]); p++) + v = v * 10 + p[0] - '0'; + return v; +} + + +bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size, + uptr *protection) { + char *last = proc_self_maps_.data + proc_self_maps_.len; + if (current_ >= last) return false; + uptr dummy; + if (!start) start = &dummy; + if (!end) end = &dummy; + if (!offset) offset = &dummy; + char *next_line = (char*)internal_memchr(current_, '\n', last - current_); + if (next_line == 0) + next_line = last; + // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar + *start = ParseHex(¤t_); + CHECK_EQ(*current_++, '-'); + *end = ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + uptr local_protection = 0; + CHECK(IsOneOf(*current_, '-', 'r')); + if (*current_++ == 'r') + local_protection |= kProtectionRead; + CHECK(IsOneOf(*current_, '-', 'w')); + if (*current_++ == 'w') + local_protection |= kProtectionWrite; + CHECK(IsOneOf(*current_, '-', 'x')); + if (*current_++ == 'x') + local_protection |= kProtectionExecute; + CHECK(IsOneOf(*current_, 's', 'p')); + if (*current_++ == 's') + local_protection |= kProtectionShared; + if (protection) { + *protection = local_protection; + } + CHECK_EQ(*current_++, ' '); + *offset = ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + ParseHex(¤t_); + CHECK_EQ(*current_++, ':'); + ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + while (IsDecimal(*current_)) + current_++; + // Qemu may lack the trailing space. + // http://code.google.com/p/address-sanitizer/issues/detail?id=160 + // CHECK_EQ(*current_++, ' '); + // Skip spaces. + while (current_ < next_line && *current_ == ' ') + current_++; + // Fill in the filename. + uptr i = 0; + while (current_ < next_line) { + if (filename && i < filename_size - 1) + filename[i++] = *current_; + current_++; + } + if (filename && i < filename_size) + filename[i] = 0; + current_ = next_line + 1; + return true; +} + +// Gets the object name and the offset by walking MemoryMappingLayout. +bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], + uptr filename_size, + uptr *protection) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size, + protection); +} + +void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { + char *smaps = 0; + uptr smaps_cap = 0; + uptr smaps_len = ReadFileToBuffer("/proc/self/smaps", + &smaps, &smaps_cap, 64<<20); + uptr start = 0; + bool file = false; + const char *pos = smaps; + while (pos < smaps + smaps_len) { + if (IsHex(pos[0])) { + start = ReadHex(pos); + for (; *pos != '/' && *pos > '\n'; pos++) {} + file = *pos == '/'; + } else if (internal_strncmp(pos, "Rss:", 4) == 0) { + for (; *pos < '0' || *pos > '9'; pos++) {} + uptr rss = ReadDecimal(pos) * 1024; + cb(start, rss, file, stats, stats_size); + } + while (*pos++ != '\n') {} + } + UnmapOrDie(smaps, smaps_cap); +} + +enum MutexState { + MtxUnlocked = 0, + MtxLocked = 1, + MtxSleeping = 2 +}; + +BlockingMutex::BlockingMutex(LinkerInitialized) { + CHECK_EQ(owner_, 0); +} + +BlockingMutex::BlockingMutex() { + internal_memset(this, 0, sizeof(*this)); +} + +void BlockingMutex::Lock() { + atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); + if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) + return; + while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) + internal_syscall(__NR_futex, (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); +} + +void BlockingMutex::Unlock() { + atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); + u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed); + CHECK_NE(v, MtxUnlocked); + if (v == MtxSleeping) + internal_syscall(__NR_futex, (uptr)m, FUTEX_WAKE, 1, 0, 0, 0); +} + +void BlockingMutex::CheckLocked() { + atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); + CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed)); +} + +// ----------------- sanitizer_linux.h +// The actual size of this structure is specified by d_reclen. +// Note that getdents64 uses a different structure format. We only provide the +// 32-bit syscall here. +struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[256]; +}; + +// Syscall wrappers. +uptr internal_ptrace(int request, int pid, void *addr, void *data) { + return internal_syscall(__NR_ptrace, request, pid, (uptr)addr, (uptr)data); +} + +uptr internal_waitpid(int pid, int *status, int options) { + return internal_syscall(__NR_wait4, pid, (uptr)status, options, + 0 /* rusage */); +} + +uptr internal_getpid() { + return internal_syscall(__NR_getpid); +} + +uptr internal_getppid() { + return internal_syscall(__NR_getppid); +} + +uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { + return internal_syscall(__NR_getdents, fd, (uptr)dirp, count); +} + +uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { + return internal_syscall(__NR_lseek, fd, offset, whence); +} + +uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { + return internal_syscall(__NR_prctl, option, arg2, arg3, arg4, arg5); +} + +uptr internal_sigaltstack(const struct sigaltstack *ss, + struct sigaltstack *oss) { + return internal_syscall(__NR_sigaltstack, (uptr)ss, (uptr)oss); +} + +uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act, + __sanitizer_kernel_sigaction_t *oldact) { + return internal_syscall(__NR_rt_sigaction, signum, act, oldact, + sizeof(__sanitizer_kernel_sigset_t)); +} + +uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set, + __sanitizer_kernel_sigset_t *oldset) { + return internal_syscall(__NR_rt_sigprocmask, (uptr)how, &set->sig[0], + &oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t)); +} + +void internal_sigfillset(__sanitizer_kernel_sigset_t *set) { + internal_memset(set, 0xff, sizeof(*set)); +} + +void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum) { + signum -= 1; + CHECK_GE(signum, 0); + CHECK_LT(signum, sizeof(*set) * 8); + const uptr idx = signum / (sizeof(set->sig[0]) * 8); + const uptr bit = signum % (sizeof(set->sig[0]) * 8); + set->sig[idx] &= ~(1 << bit); +} + +// ThreadLister implementation. +ThreadLister::ThreadLister(int pid) + : pid_(pid), + descriptor_(-1), + buffer_(4096), + error_(true), + entry_((struct linux_dirent *)buffer_.data()), + bytes_read_(0) { + char task_directory_path[80]; + internal_snprintf(task_directory_path, sizeof(task_directory_path), + "/proc/%d/task/", pid); + uptr openrv = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY); + if (internal_iserror(openrv)) { + error_ = true; + Report("Can't open /proc/%d/task for reading.\n", pid); + } else { + error_ = false; + descriptor_ = openrv; + } +} + +int ThreadLister::GetNextTID() { + int tid = -1; + do { + if (error_) + return -1; + if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries()) + return -1; + if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' && + entry_->d_name[0] <= '9') { + // Found a valid tid. + tid = (int)internal_atoll(entry_->d_name); + } + entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen); + } while (tid < 0); + return tid; +} + +void ThreadLister::Reset() { + if (error_ || descriptor_ < 0) + return; + internal_lseek(descriptor_, 0, SEEK_SET); +} + +ThreadLister::~ThreadLister() { + if (descriptor_ >= 0) + internal_close(descriptor_); +} + +bool ThreadLister::error() { return error_; } + +bool ThreadLister::GetDirectoryEntries() { + CHECK_GE(descriptor_, 0); + CHECK_NE(error_, true); + bytes_read_ = internal_getdents(descriptor_, + (struct linux_dirent *)buffer_.data(), + buffer_.size()); + if (internal_iserror(bytes_read_)) { + Report("Can't read directory entries from /proc/%d/task.\n", pid_); + error_ = true; + return false; + } else if (bytes_read_ == 0) { + return false; + } + entry_ = (struct linux_dirent *)buffer_.data(); + return true; +} + +uptr GetPageSize() { +#if defined(__x86_64__) || defined(__i386__) + return EXEC_PAGESIZE; +#else + return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy. +#endif +} + +static char proc_self_exe_cache_str[kMaxPathLength]; +static uptr proc_self_exe_cache_len = 0; + +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { + uptr module_name_len = internal_readlink( + "/proc/self/exe", buf, buf_len); + int readlink_error; + if (internal_iserror(module_name_len, &readlink_error)) { + if (proc_self_exe_cache_len) { + // If available, use the cached module name. + CHECK_LE(proc_self_exe_cache_len, buf_len); + internal_strncpy(buf, proc_self_exe_cache_str, buf_len); + module_name_len = internal_strlen(proc_self_exe_cache_str); + } else { + // We can't read /proc/self/exe for some reason, assume the name of the + // binary is unknown. + Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, " + "some stack frames may not be symbolized\n", readlink_error); + module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe"); + } + CHECK_LT(module_name_len, buf_len); + buf[module_name_len] = '\0'; + } + return module_name_len; +} + +void CacheBinaryName() { + if (!proc_self_exe_cache_len) { + proc_self_exe_cache_len = + ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength); + } +} + +// Match full names of the form /path/to/base_name{-,.}* +bool LibraryNameIs(const char *full_name, const char *base_name) { + const char *name = full_name; + // Strip path. + while (*name != '\0') name++; + while (name > full_name && *name != '/') name--; + if (*name == '/') name++; + uptr base_name_length = internal_strlen(base_name); + if (internal_strncmp(name, base_name, base_name_length)) return false; + return (name[base_name_length] == '-' || name[base_name_length] == '.'); +} + +#if !SANITIZER_ANDROID +// Call cb for each region mapped by map. +void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { + typedef ElfW(Phdr) Elf_Phdr; + typedef ElfW(Ehdr) Elf_Ehdr; + char *base = (char *)map->l_addr; + Elf_Ehdr *ehdr = (Elf_Ehdr *)base; + char *phdrs = base + ehdr->e_phoff; + char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize; + + // Find the segment with the minimum base so we can "relocate" the p_vaddr + // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC + // objects have a non-zero base. + uptr preferred_base = (uptr)-1; + for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { + Elf_Phdr *phdr = (Elf_Phdr *)iter; + if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr) + preferred_base = (uptr)phdr->p_vaddr; + } + + // Compute the delta from the real base to get a relocation delta. + sptr delta = (uptr)base - preferred_base; + // Now we can figure out what the loader really mapped. + for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { + Elf_Phdr *phdr = (Elf_Phdr *)iter; + if (phdr->p_type == PT_LOAD) { + uptr seg_start = phdr->p_vaddr + delta; + uptr seg_end = seg_start + phdr->p_memsz; + // None of these values are aligned. We consider the ragged edges of the + // load command as defined, since they are mapped from the file. + seg_start = RoundDownTo(seg_start, GetPageSizeCached()); + seg_end = RoundUpTo(seg_end, GetPageSizeCached()); + cb((void *)seg_start, seg_end - seg_start); + } + } +} +#endif + +#if defined(__x86_64__) +// We cannot use glibc's clone wrapper, because it messes with the child +// task's TLS. It writes the PID and TID of the child task to its thread +// descriptor, but in our case the child task shares the thread descriptor with +// the parent (because we don't know how to allocate a new thread +// descriptor to keep glibc happy). So the stock version of clone(), when +// used with CLONE_VM, would end up corrupting the parent's thread descriptor. +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr) { + long long res; + 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 void *r8 __asm__("r8") = newtls; + register int *r10 __asm__("r10") = child_tidptr; + __asm__ __volatile__( + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "syscall\n" + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate unwind chain. */ + // XXX: We should also terminate the CFI unwind chain + // here. Unfortunately clang 3.2 doesn't support the + // necessary CFI directives, so we skip that part. + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%rax). */ + "movq %%rax,%%rdi\n" + "movq %2,%%rax\n" + "syscall\n" + + /* Return to parent. */ + "1:\n" + : "=a" (res) + : "a"(__NR_clone), "i"(__NR_exit), + "S"(child_stack), + "D"(flags), + "d"(parent_tidptr), + "r"(r8), + "r"(r10) + : "rsp", "memory", "r11", "rcx"); + return res; +} +#endif // defined(__x86_64__) +} // namespace __sanitizer + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux.h new file mode 100644 index 00000000..a32e9bf0 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux.h @@ -0,0 +1,90 @@ +//===-- sanitizer_linux.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Linux-specific syscall wrappers and classes. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LINUX_H +#define SANITIZER_LINUX_H + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform_limits_posix.h" + +struct link_map; // Opaque type returned by dlopen(). +struct sigaltstack; + +namespace __sanitizer { +// Dirent structure for getdents(). Note that this structure is different from +// the one in , which is used by readdir(). +struct linux_dirent; + +// Syscall wrappers. +uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); +uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); +uptr internal_sigaltstack(const struct sigaltstack* ss, + struct sigaltstack* oss); +uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act, + __sanitizer_kernel_sigaction_t *oldact); +uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set, + __sanitizer_kernel_sigset_t *oldset); +void internal_sigfillset(__sanitizer_kernel_sigset_t *set); +void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum); + +#ifdef __x86_64__ +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr); +#endif + +// This class reads thread IDs from /proc//task using only syscalls. +class ThreadLister { + public: + explicit ThreadLister(int pid); + ~ThreadLister(); + // GetNextTID returns -1 if the list of threads is exhausted, or if there has + // been an error. + int GetNextTID(); + void Reset(); + bool error(); + + private: + bool GetDirectoryEntries(); + + int pid_; + int descriptor_; + InternalScopedBuffer buffer_; + bool error_; + struct linux_dirent* entry_; + int bytes_read_; +}; + +void AdjustStackSizeLinux(void *attr); + +// Exposed for testing. +uptr ThreadDescriptorSize(); +uptr ThreadSelf(); +uptr ThreadSelfOffset(); + +// Matches a library's file name against a base name (stripping path and version +// information). +bool LibraryNameIs(const char *full_name, const char *base_name); + +// Read the name of the current binary from /proc/self/exe. +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); +// Cache the value of /proc/self/exe. +void CacheBinaryName(); + +// Call cb for each region mapped by map. +void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)); +} // namespace __sanitizer + +#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc new file mode 100644 index 00000000..2940686f --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -0,0 +1,352 @@ +//===-- sanitizer_linux_libcdep.cc ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements linux-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_linux.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" + +#include +#include +#include +#include +#include + +#if !SANITIZER_ANDROID +#include +#include +#endif + +namespace __sanitizer { + +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom) { + static const uptr kMaxThreadStackSize = 1 << 30; // 1Gb + CHECK(stack_top); + CHECK(stack_bottom); + if (at_initialization) { + // This is the main thread. Libpthread may not be initialized yet. + struct rlimit rl; + CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); + + // Find the mapping that contains a stack variable. + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr start, end, offset; + uptr prev_end = 0; + while (proc_maps.Next(&start, &end, &offset, 0, 0, /* protection */0)) { + if ((uptr)&rl < end) + break; + prev_end = end; + } + CHECK((uptr)&rl >= start && (uptr)&rl < end); + + // Get stacksize from rlimit, but clip it so that it does not overlap + // with other mappings. + uptr stacksize = rl.rlim_cur; + if (stacksize > end - prev_end) + stacksize = end - prev_end; + // When running with unlimited stack size, we still want to set some limit. + // The unlimited stack size is caused by 'ulimit -s unlimited'. + // Also, for some reason, GNU make spawns subprocesses with unlimited stack. + if (stacksize > kMaxThreadStackSize) + stacksize = kMaxThreadStackSize; + *stack_top = end; + *stack_bottom = end - stacksize; + return; + } + pthread_attr_t attr; + CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); + uptr stacksize = 0; + void *stackaddr = 0; + pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); + pthread_attr_destroy(&attr); + + CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. + *stack_top = (uptr)stackaddr + stacksize; + *stack_bottom = (uptr)stackaddr; +} + +// Does not compile for Go because dlsym() requires -ldl +#ifndef SANITIZER_GO +bool SetEnv(const char *name, const char *value) { + void *f = dlsym(RTLD_NEXT, "setenv"); + if (f == 0) + return false; + typedef int(*setenv_ft)(const char *name, const char *value, int overwrite); + setenv_ft setenv_f; + CHECK_EQ(sizeof(setenv_f), sizeof(f)); + internal_memcpy(&setenv_f, &f, sizeof(f)); + return setenv_f(name, value, 1) == 0; +} +#endif + +bool SanitizerSetThreadName(const char *name) { +#ifdef PR_SET_NAME + return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT +#else + return false; +#endif +} + +bool SanitizerGetThreadName(char *name, int max_len) { +#ifdef PR_GET_NAME + char buff[17]; + if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT + return false; + internal_strncpy(name, buff, max_len); + name[max_len] = 0; + return true; +#else + return false; +#endif +} + +#ifndef SANITIZER_GO +//------------------------- SlowUnwindStack ----------------------------------- +#ifdef __arm__ +#define UNWIND_STOP _URC_END_OF_STACK +#define UNWIND_CONTINUE _URC_NO_REASON +#else +#define UNWIND_STOP _URC_NORMAL_STOP +#define UNWIND_CONTINUE _URC_NO_REASON +#endif + +uptr Unwind_GetIP(struct _Unwind_Context *ctx) { +#ifdef __arm__ + uptr val; + _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, + 15 /* r15 = PC */, _UVRSD_UINT32, &val); + CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); + // Clear the Thumb bit. + return val & ~(uptr)1; +#else + return _Unwind_GetIP(ctx); +#endif +} + +struct UnwindTraceArg { + StackTrace *stack; + uptr max_depth; +}; + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { + UnwindTraceArg *arg = (UnwindTraceArg*)param; + CHECK_LT(arg->stack->size, arg->max_depth); + uptr pc = Unwind_GetIP(ctx); + arg->stack->trace[arg->stack->size++] = pc; + if (arg->stack->size == arg->max_depth) return UNWIND_STOP; + return UNWIND_CONTINUE; +} + +void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { + size = 0; + if (max_depth == 0) + return; + UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; + _Unwind_Backtrace(Unwind_Trace, &arg); + // We need to pop a few frames so that pc is on top. + uptr to_pop = LocatePcInTrace(pc); + // trace[0] belongs to the current function so we always pop it. + if (to_pop == 0) + to_pop = 1; + PopStackFrames(to_pop); + trace[0] = pc; +} + +#endif // !SANITIZER_GO + +static uptr g_tls_size; + +#ifdef __i386__ +# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) +#else +# define DL_INTERNAL_FUNCTION +#endif + +void InitTlsSize() { +#if !defined(SANITIZER_GO) && !SANITIZER_ANDROID + typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; + get_tls_func get_tls; + void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); + CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr)); + internal_memcpy(&get_tls, &get_tls_static_info_ptr, + sizeof(get_tls_static_info_ptr)); + CHECK_NE(get_tls, 0); + size_t tls_size = 0; + size_t tls_align = 0; + get_tls(&tls_size, &tls_align); + g_tls_size = tls_size; +#endif +} + +uptr GetTlsSize() { + return g_tls_size; +} + +#if defined(__x86_64__) || defined(__i386__) +// sizeof(struct thread) from glibc. +// There has been a report of this being different on glibc 2.11 and 2.13. We +// don't know when this change happened, so 2.14 is a conservative estimate. +#if __GLIBC_PREREQ(2, 14) +const uptr kThreadDescriptorSize = FIRST_32_SECOND_64(1216, 2304); +#else +const uptr kThreadDescriptorSize = FIRST_32_SECOND_64(1168, 2304); +#endif + +uptr ThreadDescriptorSize() { + return kThreadDescriptorSize; +} + +// The offset at which pointer to self is located in the thread descriptor. +const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16); + +uptr ThreadSelfOffset() { + return kThreadSelfOffset; +} + +uptr ThreadSelf() { + uptr descr_addr; +#ifdef __i386__ + asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +#else + asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +#endif + return descr_addr; +} +#endif // defined(__x86_64__) || defined(__i386__) + +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size) { +#ifndef SANITIZER_GO +#if defined(__x86_64__) || defined(__i386__) + *tls_addr = ThreadSelf(); + *tls_size = GetTlsSize(); + *tls_addr -= *tls_size; + *tls_addr += kThreadDescriptorSize; +#else + *tls_addr = 0; + *tls_size = 0; +#endif + + uptr stack_top, stack_bottom; + GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); + *stk_addr = stack_bottom; + *stk_size = stack_top - stack_bottom; + + if (!main) { + // If stack and tls intersect, make them non-intersecting. + if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) { + CHECK_GT(*tls_addr + *tls_size, *stk_addr); + CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size); + *stk_size -= *tls_size; + *tls_addr = *stk_addr + *stk_size; + } + } +#else // SANITIZER_GO + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#endif // SANITIZER_GO +} + +void AdjustStackSizeLinux(void *attr_) { + pthread_attr_t *attr = (pthread_attr_t *)attr_; + uptr stackaddr = 0; + size_t stacksize = 0; + pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize); + // GLibC will return (0 - stacksize) as the stack address in the case when + // stacksize is set, but stackaddr is not. + bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0); + // We place a lot of tool data into TLS, account for that. + const uptr minstacksize = GetTlsSize() + 128*1024; + if (stacksize < minstacksize) { + if (!stack_set) { + if (common_flags()->verbosity && stacksize != 0) + Printf("Sanitizer: increasing stacksize %zu->%zu\n", stacksize, + minstacksize); + pthread_attr_setstacksize(attr, minstacksize); + } else { + Printf("Sanitizer: pre-allocated stack size is insufficient: " + "%zu < %zu\n", stacksize, minstacksize); + Printf("Sanitizer: pthread_create is likely to fail.\n"); + } + } +} + +#if SANITIZER_ANDROID +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + return 0; +} +#else // SANITIZER_ANDROID +typedef ElfW(Phdr) Elf_Phdr; + +struct DlIteratePhdrData { + LoadedModule *modules; + uptr current_n; + bool first; + uptr max_n; + string_predicate_t filter; +}; + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData*)arg; + if (data->current_n == data->max_n) + return 0; + InternalScopedBuffer module_name(kMaxPathLength); + module_name.data()[0] = '\0'; + if (data->first) { + data->first = false; + // First module is the binary itself. + ReadBinaryName(module_name.data(), module_name.size()); + } else if (info->dlpi_name) { + internal_strncpy(module_name.data(), info->dlpi_name, module_name.size()); + } + if (module_name.data()[0] == '\0') + return 0; + if (data->filter && !data->filter(module_name.data())) + return 0; + void *mem = &data->modules[data->current_n]; + LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(), + info->dlpi_addr); + data->current_n++; + for (int i = 0; i < info->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &info->dlpi_phdr[i]; + if (phdr->p_type == PT_LOAD) { + uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; + uptr cur_end = cur_beg + phdr->p_memsz; + cur_module->addAddressRange(cur_beg, cur_end); + } + } + return 0; +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + CHECK(modules); + DlIteratePhdrData data = {modules, 0, true, max_modules, filter}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return data.current_n; +} +#endif // SANITIZER_ANDROID + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_list.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_list.h new file mode 100644 index 00000000..f61d28f3 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_list.h @@ -0,0 +1,124 @@ +//===-- sanitizer_list.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains implementation of a list class to be used by +// ThreadSanitizer, etc run-times. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LIST_H +#define SANITIZER_LIST_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// Intrusive singly-linked list with size(), push_back(), push_front() +// pop_front(), append_front() and append_back(). +// This class should be a POD (so that it can be put into TLS) +// and an object with all zero fields should represent a valid empty list. +// This class does not have a CTOR, so clear() should be called on all +// non-zero-initialized objects before using. +template +struct IntrusiveList { + void clear() { + first_ = last_ = 0; + size_ = 0; + } + + bool empty() const { return size_ == 0; } + uptr size() const { return size_; } + + void push_back(Item *x) { + if (empty()) { + x->next = 0; + first_ = last_ = x; + size_ = 1; + } else { + x->next = 0; + last_->next = x; + last_ = x; + size_++; + } + } + + void push_front(Item *x) { + if (empty()) { + x->next = 0; + first_ = last_ = x; + size_ = 1; + } else { + x->next = first_; + first_ = x; + size_++; + } + } + + void pop_front() { + CHECK(!empty()); + first_ = first_->next; + if (first_ == 0) + last_ = 0; + size_--; + } + + Item *front() { return first_; } + Item *back() { return last_; } + + void append_front(IntrusiveList *l) { + CHECK_NE(this, l); + if (l->empty()) + return; + if (empty()) { + *this = *l; + } else if (!l->empty()) { + l->last_->next = first_; + first_ = l->first_; + size_ += l->size(); + } + l->clear(); + } + + void append_back(IntrusiveList *l) { + CHECK_NE(this, l); + if (l->empty()) + return; + if (empty()) { + *this = *l; + } else { + last_->next = l->first_; + last_ = l->last_; + size_ += l->size(); + } + l->clear(); + } + + void CheckConsistency() { + if (size_ == 0) { + CHECK_EQ(first_, 0); + CHECK_EQ(last_, 0); + } else { + uptr count = 0; + for (Item *i = first_; ; i = i->next) { + count++; + if (i == last_) break; + } + CHECK_EQ(size(), count); + CHECK_EQ(last_->next, 0); + } + } + +// private, don't use directly. + uptr size_; + Item *first_; + Item *last_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_LIST_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc new file mode 100644 index 00000000..87ad8b53 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc @@ -0,0 +1,412 @@ +//===-- sanitizer_mac.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements mac-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_MAC + +// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so +// the clients will most certainly use 64-bit ones as well. +#ifndef _DARWIN_USE_64_BIT_INODE +#define _DARWIN_USE_64_BIT_INODE 1 +#endif +#include + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" + +#include // for _NSGetEnviron +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace __sanitizer { + +#include "sanitizer_syscall_generic.inc" + +// ---------------------- sanitizer_libc.h +uptr internal_mmap(void *addr, size_t length, int prot, int flags, + int fd, u64 offset) { + return (uptr)mmap(addr, length, prot, flags, fd, offset); +} + +uptr internal_munmap(void *addr, uptr length) { + return munmap(addr, length); +} + +uptr internal_close(fd_t fd) { + return close(fd); +} + +uptr internal_open(const char *filename, int flags) { + return open(filename, flags); +} + +uptr internal_open(const char *filename, int flags, u32 mode) { + return open(filename, flags, mode); +} + +uptr OpenFile(const char *filename, bool write) { + return internal_open(filename, + write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + return read(fd, buf, count); +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + return write(fd, buf, count); +} + +uptr internal_stat(const char *path, void *buf) { + return stat(path, (struct stat *)buf); +} + +uptr internal_lstat(const char *path, void *buf) { + return lstat(path, (struct stat *)buf); +} + +uptr internal_fstat(fd_t fd, void *buf) { + return fstat(fd, (struct stat *)buf); +} + +uptr internal_filesize(fd_t fd) { + struct stat st; + if (internal_fstat(fd, &st)) + return -1; + return (uptr)st.st_size; +} + +uptr internal_dup2(int oldfd, int newfd) { + return dup2(oldfd, newfd); +} + +uptr internal_readlink(const char *path, char *buf, uptr bufsize) { + return readlink(path, buf, bufsize); +} + +uptr internal_sched_yield() { + return sched_yield(); +} + +void internal__exit(int exitcode) { + _exit(exitcode); +} + +uptr internal_getpid() { + return getpid(); +} + +// ----------------- sanitizer_common.h +bool FileExists(const char *filename) { + struct stat st; + if (stat(filename, &st)) + return false; + // Sanity check: filename is a regular file. + return S_ISREG(st.st_mode); +} + +uptr GetTid() { + return reinterpret_cast(pthread_self()); +} + +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom) { + CHECK(stack_top); + CHECK(stack_bottom); + uptr stacksize = pthread_get_stacksize_np(pthread_self()); + void *stackaddr = pthread_get_stackaddr_np(pthread_self()); + *stack_top = (uptr)stackaddr; + *stack_bottom = *stack_top - stacksize; +} + +const char *GetEnv(const char *name) { + char ***env_ptr = _NSGetEnviron(); + if (!env_ptr) { + Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " + "called after libSystem_initializer().\n"); + CHECK(env_ptr); + } + char **environ = *env_ptr; + CHECK(environ); + uptr name_len = internal_strlen(name); + while (*environ != 0) { + uptr len = internal_strlen(*environ); + if (len > name_len) { + const char *p = *environ; + if (!internal_memcmp(p, name, name_len) && + p[name_len] == '=') { // Match. + return *environ + name_len + 1; // String starting after =. + } + } + environ++; + } + return 0; +} + +void ReExec() { + UNIMPLEMENTED(); +} + +void PrepareForSandboxing() { + // Nothing here for now. +} + +uptr GetPageSize() { + return sysconf(_SC_PAGESIZE); +} + +// ----------------- sanitizer_procmaps.h + +MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { + Reset(); +} + +MemoryMappingLayout::~MemoryMappingLayout() { +} + +// More information about Mach-O headers can be found in mach-o/loader.h +// Each Mach-O image has a header (mach_header or mach_header_64) starting with +// a magic number, and a list of linker load commands directly following the +// header. +// A load command is at least two 32-bit words: the command type and the +// command size in bytes. We're interested only in segment load commands +// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped +// into the task's address space. +// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or +// segment_command_64 correspond to the memory address, memory size and the +// file offset of the current memory segment. +// Because these fields are taken from the images as is, one needs to add +// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime. + +void MemoryMappingLayout::Reset() { + // Count down from the top. + // TODO(glider): as per man 3 dyld, iterating over the headers with + // _dyld_image_count is thread-unsafe. We need to register callbacks for + // adding and removing images which will invalidate the MemoryMappingLayout + // state. + current_image_ = _dyld_image_count(); + current_load_cmd_count_ = -1; + current_load_cmd_addr_ = 0; + current_magic_ = 0; + current_filetype_ = 0; +} + +// static +void MemoryMappingLayout::CacheMemoryMappings() { + // No-op on Mac for now. +} + +void MemoryMappingLayout::LoadFromCache() { + // No-op on Mac for now. +} + +// Next and NextSegmentLoad were inspired by base/sysinfo.cc in +// Google Perftools, http://code.google.com/p/google-perftools. + +// NextSegmentLoad scans the current image for the next segment load command +// and returns the start and end addresses and file offset of the corresponding +// segment. +// Note that the segment addresses are not necessarily sorted. +template +bool MemoryMappingLayout::NextSegmentLoad( + uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size, uptr *protection) { + if (protection) + UNIMPLEMENTED(); + const char* lc = current_load_cmd_addr_; + current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize; + if (((const load_command *)lc)->cmd == kLCSegment) { + const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_); + const SegmentCommand* sc = (const SegmentCommand *)lc; + if (start) *start = sc->vmaddr + dlloff; + if (end) *end = sc->vmaddr + sc->vmsize + dlloff; + if (offset) { + if (current_filetype_ == /*MH_EXECUTE*/ 0x2) { + *offset = sc->vmaddr; + } else { + *offset = sc->fileoff; + } + } + if (filename) { + internal_strncpy(filename, _dyld_get_image_name(current_image_), + filename_size); + } + return true; + } + return false; +} + +bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size, + uptr *protection) { + for (; current_image_ >= 0; current_image_--) { + const mach_header* hdr = _dyld_get_image_header(current_image_); + if (!hdr) continue; + if (current_load_cmd_count_ < 0) { + // Set up for this image; + current_load_cmd_count_ = hdr->ncmds; + current_magic_ = hdr->magic; + current_filetype_ = hdr->filetype; + switch (current_magic_) { +#ifdef MH_MAGIC_64 + case MH_MAGIC_64: { + current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64); + break; + } +#endif + case MH_MAGIC: { + current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header); + break; + } + default: { + continue; + } + } + } + + for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) { + switch (current_magic_) { + // current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64. +#ifdef MH_MAGIC_64 + case MH_MAGIC_64: { + if (NextSegmentLoad( + start, end, offset, filename, filename_size, protection)) + return true; + break; + } +#endif + case MH_MAGIC: { + if (NextSegmentLoad( + start, end, offset, filename, filename_size, protection)) + return true; + break; + } + } + } + // If we get here, no more load_cmd's in this image talk about + // segments. Go on to the next image. + } + return false; +} + +bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], + uptr filename_size, + uptr *protection) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size, + protection); +} + +BlockingMutex::BlockingMutex(LinkerInitialized) { + // We assume that OS_SPINLOCK_INIT is zero +} + +BlockingMutex::BlockingMutex() { + internal_memset(this, 0, sizeof(*this)); +} + +void BlockingMutex::Lock() { + CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); + CHECK_EQ(OS_SPINLOCK_INIT, 0); + CHECK_NE(owner_, (uptr)pthread_self()); + OSSpinLockLock((OSSpinLock*)&opaque_storage_); + CHECK(!owner_); + owner_ = (uptr)pthread_self(); +} + +void BlockingMutex::Unlock() { + CHECK(owner_ == (uptr)pthread_self()); + owner_ = 0; + OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); +} + +void BlockingMutex::CheckLocked() { + CHECK_EQ((uptr)pthread_self(), owner_); +} + +u64 NanoTime() { + return 0; +} + +uptr GetTlsSize() { + return 0; +} + +void InitTlsSize() { +} + +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size) { +#ifndef SANITIZER_GO + uptr stack_top, stack_bottom; + GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); + *stk_addr = stack_bottom; + *stk_size = stack_top - stack_bottom; + *tls_addr = 0; + *tls_size = 0; +#else + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#endif +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + MemoryMappingLayout memory_mapping(false); + memory_mapping.Reset(); + uptr cur_beg, cur_end, cur_offset; + InternalScopedBuffer module_name(kMaxPathLength); + uptr n_modules = 0; + for (uptr i = 0; + n_modules < max_modules && + memory_mapping.Next(&cur_beg, &cur_end, &cur_offset, + module_name.data(), module_name.size(), 0); + i++) { + const char *cur_name = module_name.data(); + if (cur_name[0] == '\0') + continue; + if (filter && !filter(cur_name)) + continue; + LoadedModule *cur_module = 0; + if (n_modules > 0 && + 0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) { + cur_module = &modules[n_modules - 1]; + } else { + void *mem = &modules[n_modules]; + cur_module = new(mem) LoadedModule(cur_name, cur_beg); + n_modules++; + } + cur_module->addAddressRange(cur_beg, cur_end); + } + return n_modules; +} + +} // namespace __sanitizer + +#endif // SANITIZER_MAC diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h new file mode 100644 index 00000000..e812fce9 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h @@ -0,0 +1,129 @@ +//===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_MUTEX_H +#define SANITIZER_MUTEX_H + +#include "sanitizer_atomic.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +class StaticSpinMutex { + public: + void Init() { + atomic_store(&state_, 0, memory_order_relaxed); + } + + void Lock() { + if (TryLock()) + return; + LockSlow(); + } + + bool TryLock() { + return atomic_exchange(&state_, 1, memory_order_acquire) == 0; + } + + void Unlock() { + atomic_store(&state_, 0, memory_order_release); + } + + void CheckLocked() { + CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1); + } + + private: + atomic_uint8_t state_; + + void NOINLINE LockSlow() { + for (int i = 0;; i++) { + if (i < 10) + proc_yield(10); + else + internal_sched_yield(); + if (atomic_load(&state_, memory_order_relaxed) == 0 + && atomic_exchange(&state_, 1, memory_order_acquire) == 0) + return; + } + } +}; + +class SpinMutex : public StaticSpinMutex { + public: + SpinMutex() { + Init(); + } + + private: + SpinMutex(const SpinMutex&); + void operator=(const SpinMutex&); +}; + +class BlockingMutex { + public: + explicit BlockingMutex(LinkerInitialized); + BlockingMutex(); + void Lock(); + void Unlock(); + void CheckLocked(); + private: + uptr opaque_storage_[10]; + uptr owner_; // for debugging +}; + +template +class GenericScopedLock { + public: + explicit GenericScopedLock(MutexType *mu) + : mu_(mu) { + mu_->Lock(); + } + + ~GenericScopedLock() { + mu_->Unlock(); + } + + private: + MutexType *mu_; + + GenericScopedLock(const GenericScopedLock&); + void operator=(const GenericScopedLock&); +}; + +template +class GenericScopedReadLock { + public: + explicit GenericScopedReadLock(MutexType *mu) + : mu_(mu) { + mu_->ReadLock(); + } + + ~GenericScopedReadLock() { + mu_->ReadUnlock(); + } + + private: + MutexType *mu_; + + GenericScopedReadLock(const GenericScopedReadLock&); + void operator=(const GenericScopedReadLock&); +}; + +typedef GenericScopedLock SpinMutexLock; +typedef GenericScopedLock BlockingMutexLock; + +} // namespace __sanitizer + +#endif // SANITIZER_MUTEX_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_placement_new.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_placement_new.h new file mode 100644 index 00000000..8904d104 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_placement_new.h @@ -0,0 +1,25 @@ +//===-- sanitizer_placement_new.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// +// The file provides 'placement new'. +// Do not include it into header files, only into source files. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_PLACEMENT_NEW_H +#define SANITIZER_PLACEMENT_NEW_H + +#include "sanitizer_internal_defs.h" + +inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) { + return p; +} + +#endif // SANITIZER_PLACEMENT_NEW_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform.h new file mode 100644 index 00000000..fce721e3 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform.h @@ -0,0 +1,53 @@ +//===-- sanitizer_platform.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Common platform macros. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_PLATFORM_H +#define SANITIZER_PLATFORM_H + +#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) +# error "This operating system is not supported" +#endif + +#if defined(__linux__) +# define SANITIZER_LINUX 1 +#else +# define SANITIZER_LINUX 0 +#endif + +#if defined(__APPLE__) +# define SANITIZER_MAC 1 +# include +# if TARGET_OS_IPHONE +# define SANITIZER_IOS 1 +# else +# define SANITIZER_IOS 0 +# endif +#else +# define SANITIZER_MAC 0 +# define SANITIZER_IOS 0 +#endif + +#if defined(_WIN32) +# define SANITIZER_WINDOWS 1 +#else +# define SANITIZER_WINDOWS 0 +#endif + +#if defined(__ANDROID__) || defined(ANDROID) +# define SANITIZER_ANDROID 1 +#else +# define SANITIZER_ANDROID 0 +#endif + +#define SANITIZER_POSIX (SANITIZER_LINUX || SANITIZER_MAC) + +#endif // SANITIZER_PLATFORM_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h new file mode 100644 index 00000000..78d1f5ad --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -0,0 +1,170 @@ +//===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines macro telling whether sanitizer tools can/should intercept +// given library functions on a given platform. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_PLATFORM_INTERCEPTORS_H +#define SANITIZER_PLATFORM_INTERCEPTORS_H + +#include "sanitizer_internal_defs.h" + +#if !SANITIZER_WINDOWS +# define SI_NOT_WINDOWS 1 +# include "sanitizer_platform_limits_posix.h" +#else +# define SI_NOT_WINDOWS 0 +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +# define SI_LINUX_NOT_ANDROID 1 +#else +# define SI_LINUX_NOT_ANDROID 0 +#endif + +#if SANITIZER_LINUX +# define SI_LINUX 1 +#else +# define SI_LINUX 0 +#endif + +#if SANITIZER_MAC +# define SI_MAC 1 +#else +# define SI_MAC 0 +#endif + +#if SANITIZER_IOS +# define SI_IOS 1 +#else +# define SI_IOS 0 +#endif + +# define SANITIZER_INTERCEPT_STRCMP 1 +# define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS + +# define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS + +#define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID + +#define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS + +#define SANITIZER_INTERCEPT_PREADV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID + +# define SANITIZER_INTERCEPT_PRCTL SI_LINUX + +# define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS + +# define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX + +# define SANITIZER_INTERCEPT_FREXP 1 +# define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS + +# define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \ + SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX +# define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_WAIT SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_INET SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETADDRINFO SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX +# define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX +# define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_IOCTL SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_INET_ATON SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SYSINFO SI_LINUX +# define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT +# define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX +# define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX +# define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_WORDEXP SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SIGSETOPS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX +# define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATFS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STATFS64 \ + (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ETHER SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SHMCTL SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ + SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SINCOS SI_LINUX +# define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX +# define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID + +// FIXME: getline seems to be available on OSX 10.7 +# define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID + +# define SANITIZER_INTERCEPT__EXIT SI_LINUX + +# define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_COND SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID + +#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_linux.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_linux.cc new file mode 100644 index 00000000..4c9f12ac --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_linux.cc @@ -0,0 +1,95 @@ +//===-- sanitizer_platform_limits_linux.cc --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Sanitizer common code. +// +// Sizes and layouts of linux kernel data structures. +//===----------------------------------------------------------------------===// + +// This is a separate compilation unit for linux headers that conflict with +// userspace headers. +// Most "normal" includes go in sanitizer_platform_limits_posix.cc + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform_limits_posix.h" + +// For offsetof -> __builtin_offsetof definition. +#include + +// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that +// are not defined anywhere in userspace headers. Fake them. This seems to work +// fine with newer headers, too. +#include +#define ino_t __kernel_ino_t +#define mode_t __kernel_mode_t +#define nlink_t __kernel_nlink_t +#define uid_t __kernel_uid_t +#define gid_t __kernel_gid_t +#define off_t __kernel_off_t +// This header seems to contain the definitions of _kernel_ stat* structs. +#include +#undef ino_t +#undef mode_t +#undef nlink_t +#undef uid_t +#undef gid_t +#undef off_t + +#include + +#if SANITIZER_ANDROID +#include +#else +#include +#endif + +#if !SANITIZER_ANDROID +#include +#endif + +namespace __sanitizer { + unsigned struct_statfs64_sz = sizeof(struct statfs64); +} // namespace __sanitizer + +#if !defined(__powerpc64__) +COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat)); +#endif + +COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat)); + +#if defined(__i386__) +COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64)); +#endif + +COMPILER_CHECK(struct_io_event_sz == sizeof(struct io_event)); + +#if !SANITIZER_ANDROID +COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <= + sizeof(struct perf_event_attr)); +CHECK_SIZE_AND_OFFSET(perf_event_attr, type); +CHECK_SIZE_AND_OFFSET(perf_event_attr, size); +#endif + +COMPILER_CHECK(iocb_cmd_pread == IOCB_CMD_PREAD); +COMPILER_CHECK(iocb_cmd_pwrite == IOCB_CMD_PWRITE); + +CHECK_TYPE_SIZE(iocb); +CHECK_SIZE_AND_OFFSET(iocb, aio_data); +// Skip aio_key, it's weird. +CHECK_SIZE_AND_OFFSET(iocb, aio_lio_opcode); +CHECK_SIZE_AND_OFFSET(iocb, aio_reqprio); +CHECK_SIZE_AND_OFFSET(iocb, aio_fildes); +CHECK_SIZE_AND_OFFSET(iocb, aio_buf); +CHECK_SIZE_AND_OFFSET(iocb, aio_nbytes); +CHECK_SIZE_AND_OFFSET(iocb, aio_offset); + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc new file mode 100644 index 00000000..e887751d --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -0,0 +1,947 @@ +//===-- sanitizer_platform_limits_posix.cc --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Sanitizer common code. +// +// Sizes and layouts of platform-specific POSIX data structures. +//===----------------------------------------------------------------------===// + + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC + +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform_limits_posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if SANITIZER_LINUX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if !SANITIZER_ANDROID +#include +#include +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + +#if SANITIZER_ANDROID +#include +#include +#include +#include +#endif + +#if SANITIZER_LINUX +#include +#include +#include +#include +#endif // SANITIZER_LINUX + +#if SANITIZER_MAC +#include +#include +#include +#include +#endif + +namespace __sanitizer { + unsigned struct_utsname_sz = sizeof(struct utsname); + unsigned struct_stat_sz = sizeof(struct stat); +#if !SANITIZER_IOS + unsigned struct_stat64_sz = sizeof(struct stat64); +#endif // !SANITIZER_IOS + unsigned struct_rusage_sz = sizeof(struct rusage); + unsigned struct_tm_sz = sizeof(struct tm); + unsigned struct_passwd_sz = sizeof(struct passwd); + unsigned struct_group_sz = sizeof(struct group); + unsigned siginfo_t_sz = sizeof(siginfo_t); + unsigned struct_sigaction_sz = sizeof(struct sigaction); + unsigned struct_itimerval_sz = sizeof(struct itimerval); + unsigned pthread_t_sz = sizeof(pthread_t); + unsigned pthread_cond_t_sz = sizeof(pthread_cond_t); + unsigned pid_t_sz = sizeof(pid_t); + unsigned timeval_sz = sizeof(timeval); + unsigned uid_t_sz = sizeof(uid_t); + unsigned mbstate_t_sz = sizeof(mbstate_t); + unsigned sigset_t_sz = sizeof(sigset_t); + unsigned struct_timezone_sz = sizeof(struct timezone); + unsigned struct_tms_sz = sizeof(struct tms); + unsigned struct_sigevent_sz = sizeof(struct sigevent); + unsigned struct_sched_param_sz = sizeof(struct sched_param); + unsigned struct_statfs_sz = sizeof(struct statfs); + +#if SANITIZER_MAC && !SANITIZER_IOS + unsigned struct_statfs64_sz = sizeof(struct statfs64); +#endif // SANITIZER_MAC && !SANITIZER_IOS + +#if !SANITIZER_ANDROID + unsigned ucontext_t_sz = sizeof(ucontext_t); +#endif // !SANITIZER_ANDROID + +#if SANITIZER_LINUX + unsigned struct_rlimit_sz = sizeof(struct rlimit); + unsigned struct_epoll_event_sz = sizeof(struct epoll_event); + unsigned struct_sysinfo_sz = sizeof(struct sysinfo); + unsigned struct_timespec_sz = sizeof(struct timespec); + unsigned __user_cap_header_struct_sz = + sizeof(struct __user_cap_header_struct); + unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct); + unsigned struct_utimbuf_sz = sizeof(struct utimbuf); + unsigned struct_new_utsname_sz = sizeof(struct new_utsname); + unsigned struct_old_utsname_sz = sizeof(struct old_utsname); + unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname); + unsigned struct_itimerspec_sz = sizeof(struct itimerspec); + unsigned struct_ustat_sz = sizeof(struct ustat); +#endif // SANITIZER_LINUX + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned struct_rlimit64_sz = sizeof(struct rlimit64); + unsigned struct_timex_sz = sizeof(struct timex); + unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds); + unsigned struct_mq_attr_sz = sizeof(struct mq_attr); + unsigned struct_statvfs_sz = sizeof(struct statvfs); + unsigned struct_statvfs64_sz = sizeof(struct statvfs64); +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + + uptr sig_ign = (uptr)SIG_IGN; + uptr sig_dfl = (uptr)SIG_DFL; + uptr sa_siginfo = (uptr)SA_SIGINFO; + +#if SANITIZER_LINUX + int e_tabsz = (int)E_TABSZ; +#endif + + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned struct_shminfo_sz = sizeof(struct shminfo); + unsigned struct_shm_info_sz = sizeof(struct shm_info); + int shmctl_ipc_stat = (int)IPC_STAT; + int shmctl_ipc_info = (int)IPC_INFO; + int shmctl_shm_info = (int)SHM_INFO; + int shmctl_shm_stat = (int)SHM_INFO; +#endif + + int af_inet = (int)AF_INET; + int af_inet6 = (int)AF_INET6; + + uptr __sanitizer_in_addr_sz(int af) { + if (af == AF_INET) + return sizeof(struct in_addr); + else if (af == AF_INET6) + return sizeof(struct in6_addr); + else + return 0; + } + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + int glob_nomatch = GLOB_NOMATCH; + int glob_altdirfunc = GLOB_ALTDIRFUNC; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT + unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); + unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); +#ifdef __x86_64 + unsigned struct_user_fpxregs_struct_sz = 0; +#else + unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); +#endif + + int ptrace_peektext = PTRACE_PEEKTEXT; + int ptrace_peekdata = PTRACE_PEEKDATA; + int ptrace_peekuser = PTRACE_PEEKUSER; + int ptrace_getregs = PTRACE_GETREGS; + int ptrace_setregs = PTRACE_SETREGS; + int ptrace_getfpregs = PTRACE_GETFPREGS; + int ptrace_setfpregs = PTRACE_SETFPREGS; + int ptrace_getfpxregs = PTRACE_GETFPXREGS; + int ptrace_setfpxregs = PTRACE_SETFPXREGS; + int ptrace_getsiginfo = PTRACE_GETSIGINFO; + int ptrace_setsiginfo = PTRACE_SETSIGINFO; +#if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET) + int ptrace_getregset = PTRACE_GETREGSET; + int ptrace_setregset = PTRACE_SETREGSET; +#else + int ptrace_getregset = -1; + int ptrace_setregset = -1; +#endif +#endif + + unsigned path_max = PATH_MAX; + + // ioctl arguments + unsigned struct_arpreq_sz = sizeof(struct arpreq); + unsigned struct_ifreq_sz = sizeof(struct ifreq); + unsigned struct_termios_sz = sizeof(struct termios); + unsigned struct_winsize_sz = sizeof(struct winsize); + +#if SANITIZER_LINUX + unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf); + unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession); + unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio); + unsigned struct_cdrom_subchnl_sz = sizeof(struct cdrom_subchnl); + unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti); + unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry); + unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr); + unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl); +#if SOUND_VERSION >= 0x040000 + unsigned struct_copr_buffer_sz = 0; + unsigned struct_copr_debug_buf_sz = 0; + unsigned struct_copr_msg_sz = 0; +#else + unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer); + unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf); + unsigned struct_copr_msg_sz = sizeof(struct copr_msg); +#endif + unsigned struct_ff_effect_sz = sizeof(struct ff_effect); + unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params); + unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct); + unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state); + unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors); + unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd); + unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct); + unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors); + unsigned struct_format_descr_sz = sizeof(struct format_descr); + unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid); + unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry); + unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo); + unsigned struct_input_id_sz = sizeof(struct input_id); + unsigned struct_midi_info_sz = sizeof(struct midi_info); + unsigned struct_mtget_sz = sizeof(struct mtget); + unsigned struct_mtop_sz = sizeof(struct mtop); + unsigned struct_mtpos_sz = sizeof(struct mtpos); + unsigned struct_rtentry_sz = sizeof(struct rtentry); + unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument); + unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec); + unsigned struct_synth_info_sz = sizeof(struct synth_info); + unsigned struct_termio_sz = sizeof(struct termio); + unsigned struct_vt_consize_sz = sizeof(struct vt_consize); + unsigned struct_vt_mode_sz = sizeof(struct vt_mode); + unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes); + unsigned struct_vt_stat_sz = sizeof(struct vt_stat); +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info); + unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct); + unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor); +#if EV_VERSION > (0x010000) + unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry); +#else + unsigned struct_input_keymap_entry_sz = 0; +#endif + unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data); + unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs); + unsigned struct_kbentry_sz = sizeof(struct kbentry); + unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode); + unsigned struct_kbsentry_sz = sizeof(struct kbsentry); + unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo); + unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct); + unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats); + unsigned struct_scc_modem_sz = sizeof(struct scc_modem); + unsigned struct_scc_stat_sz = sizeof(struct scc_stat); + unsigned struct_serial_multiport_struct_sz + = sizeof(struct serial_multiport_struct); + unsigned struct_serial_struct_sz = sizeof(struct serial_struct); + unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25); + unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc); + unsigned struct_unimapinit_sz = sizeof(struct unimapinit); +#endif + +#if !SANITIZER_ANDROID && !SANITIZER_MAC + unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); + unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req); +#endif + + unsigned IOCTL_NOT_PRESENT = 0; + + unsigned IOCTL_FIOASYNC = FIOASYNC; + unsigned IOCTL_FIOCLEX = FIOCLEX; + unsigned IOCTL_FIOGETOWN = FIOGETOWN; + unsigned IOCTL_FIONBIO = FIONBIO; + unsigned IOCTL_FIONCLEX = FIONCLEX; + unsigned IOCTL_FIOSETOWN = FIOSETOWN; + unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI; + unsigned IOCTL_SIOCATMARK = SIOCATMARK; + unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI; + unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR; + unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR; + unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF; + unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR; + unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS; + unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC; + unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU; + unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK; + unsigned IOCTL_SIOCGPGRP = SIOCGPGRP; + unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR; + unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR; + unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR; + unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS; + unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC; + unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU; + unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK; + unsigned IOCTL_SIOCSPGRP = SIOCSPGRP; + unsigned IOCTL_TIOCCONS = TIOCCONS; + unsigned IOCTL_TIOCEXCL = TIOCEXCL; + unsigned IOCTL_TIOCGETD = TIOCGETD; + unsigned IOCTL_TIOCGPGRP = TIOCGPGRP; + unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ; + unsigned IOCTL_TIOCMBIC = TIOCMBIC; + unsigned IOCTL_TIOCMBIS = TIOCMBIS; + unsigned IOCTL_TIOCMGET = TIOCMGET; + unsigned IOCTL_TIOCMSET = TIOCMSET; + unsigned IOCTL_TIOCNOTTY = TIOCNOTTY; + unsigned IOCTL_TIOCNXCL = TIOCNXCL; + unsigned IOCTL_TIOCOUTQ = TIOCOUTQ; + unsigned IOCTL_TIOCPKT = TIOCPKT; + unsigned IOCTL_TIOCSCTTY = TIOCSCTTY; + unsigned IOCTL_TIOCSETD = TIOCSETD; + unsigned IOCTL_TIOCSPGRP = TIOCSPGRP; + unsigned IOCTL_TIOCSTI = TIOCSTI; + unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ; +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) + unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT; + unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT; +#endif +#if SANITIZER_LINUX + unsigned IOCTL_EVIOCGABS = EVIOCGABS(0); + unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0); + unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS; + unsigned IOCTL_EVIOCGID = EVIOCGID; + unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0); + unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE; + unsigned IOCTL_EVIOCGLED = EVIOCGLED(0); + unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0); + unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0); + unsigned IOCTL_EVIOCGRAB = EVIOCGRAB; + unsigned IOCTL_EVIOCGREP = EVIOCGREP; + unsigned IOCTL_EVIOCGSND = EVIOCGSND(0); + unsigned IOCTL_EVIOCGSW = EVIOCGSW(0); + unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0); + unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION; + unsigned IOCTL_EVIOCRMFF = EVIOCRMFF; + unsigned IOCTL_EVIOCSABS = EVIOCSABS(0); + unsigned IOCTL_EVIOCSFF = EVIOCSFF; + unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE; + unsigned IOCTL_EVIOCSREP = EVIOCSREP; + unsigned IOCTL_BLKFLSBUF = BLKFLSBUF; + unsigned IOCTL_BLKGETSIZE = BLKGETSIZE; + unsigned IOCTL_BLKRAGET = BLKRAGET; + unsigned IOCTL_BLKRASET = BLKRASET; + unsigned IOCTL_BLKROGET = BLKROGET; + unsigned IOCTL_BLKROSET = BLKROSET; + unsigned IOCTL_BLKRRPART = BLKRRPART; + unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ; + unsigned IOCTL_CDROMEJECT = CDROMEJECT; + unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW; + unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION; + unsigned IOCTL_CDROMPAUSE = CDROMPAUSE; + unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF; + unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND; + unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO; + unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED; + unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1; + unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2; + unsigned IOCTL_CDROMREADRAW = CDROMREADRAW; + unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY; + unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR; + unsigned IOCTL_CDROMRESET = CDROMRESET; + unsigned IOCTL_CDROMRESUME = CDROMRESUME; + unsigned IOCTL_CDROMSEEK = CDROMSEEK; + unsigned IOCTL_CDROMSTART = CDROMSTART; + unsigned IOCTL_CDROMSTOP = CDROMSTOP; + unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL; + unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL; + unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD; + unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC; + unsigned IOCTL_FDCLRPRM = FDCLRPRM; + unsigned IOCTL_FDDEFPRM = FDDEFPRM; + unsigned IOCTL_FDFLUSH = FDFLUSH; + unsigned IOCTL_FDFMTBEG = FDFMTBEG; + unsigned IOCTL_FDFMTEND = FDFMTEND; + unsigned IOCTL_FDFMTTRK = FDFMTTRK; + unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM; + unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT; + unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP; + unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT; + unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS; + unsigned IOCTL_FDGETPRM = FDGETPRM; + unsigned IOCTL_FDMSGOFF = FDMSGOFF; + unsigned IOCTL_FDMSGON = FDMSGON; + unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT; + unsigned IOCTL_FDRAWCMD = FDRAWCMD; + unsigned IOCTL_FDRESET = FDRESET; + unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM; + unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH; + unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS; + unsigned IOCTL_FDSETPRM = FDSETPRM; + unsigned IOCTL_FDTWADDLE = FDTWADDLE; + unsigned IOCTL_FDWERRORCLR = FDWERRORCLR; + unsigned IOCTL_FDWERRORGET = FDWERRORGET; + unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD; + unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO; + unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT; + unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA; + unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY; + unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS; + unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT; + unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR; + unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR; + unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT; + unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA; + unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS; + unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT; + unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR; + unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR; + unsigned IOCTL_MTIOCGET = MTIOCGET; + unsigned IOCTL_MTIOCPOS = MTIOCPOS; + unsigned IOCTL_MTIOCTOP = MTIOCTOP; + unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP; + unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG; + unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS; + unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT; + unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP; + unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP; + unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG; + unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS; + unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID; + unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU; + unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP; + unsigned IOCTL_SIOCADDRT = SIOCADDRT; + unsigned IOCTL_SIOCDARP = SIOCDARP; + unsigned IOCTL_SIOCDELRT = SIOCDELRT; + unsigned IOCTL_SIOCDRARP = SIOCDRARP; + unsigned IOCTL_SIOCGARP = SIOCGARP; + unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP; + unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR; + unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP; + unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM; + unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME; + unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE; + unsigned IOCTL_SIOCGRARP = SIOCGRARP; + unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP; + unsigned IOCTL_SIOCSARP = SIOCSARP; + unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP; + unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR; + unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK; + unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP; + unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM; + unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE; + unsigned IOCTL_SIOCSRARP = SIOCSRARP; +#if SOUND_VERSION >= 0x040000 + unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_FILTER = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT; +#else + unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT; + unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD; + unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE; + unsigned IOCTL_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG; + unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA; + unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET; + unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN; + unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG; + unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE; + unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA; + unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS; + unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS; + unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER; + unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE; + unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS; + unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER; +#endif + unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE; + unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS; + unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK; + unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST; + unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET; + unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT; + unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT; + unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED; + unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO; + unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE; + unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC; + unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE; + unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR; + unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO; + unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME; + unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE; + unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT; + unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT; + unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS; + unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS; + unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND; + unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC; + unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE; + unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET; + unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES; + unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC; + unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI; + unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD; + unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO; + unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL; + unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE; + unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME; + unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT; + unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE; + unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START; + unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP; + unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO; + unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE; + unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM; + unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS; + unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS; + unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD; + unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK; + unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE; + unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN; + unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX; + unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE; + unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1; + unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2; + unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3; + unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD; + unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC; + unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE; + unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN; + unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM; + unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV; + unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK; + unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC; + unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER; + unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS; + unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH; + unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE; + unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME; + unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM; + unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS; + unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD; + unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE; + unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN; + unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3; + unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD; + unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC; + unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE; + unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN; + unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM; + unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV; + unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC; + unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER; + unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH; + unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE; + unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME; + unsigned IOCTL_TCFLSH = TCFLSH; + unsigned IOCTL_TCGETA = TCGETA; + unsigned IOCTL_TCGETS = TCGETS; + unsigned IOCTL_TCSBRK = TCSBRK; + unsigned IOCTL_TCSBRKP = TCSBRKP; + unsigned IOCTL_TCSETA = TCSETA; + unsigned IOCTL_TCSETAF = TCSETAF; + unsigned IOCTL_TCSETAW = TCSETAW; + unsigned IOCTL_TCSETS = TCSETS; + unsigned IOCTL_TCSETSF = TCSETSF; + unsigned IOCTL_TCSETSW = TCSETSW; + unsigned IOCTL_TCXONC = TCXONC; + unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS; + unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR; + unsigned IOCTL_TIOCINQ = TIOCINQ; + unsigned IOCTL_TIOCLINUX = TIOCLINUX; + unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG; + unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR; + unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD; + unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD; + unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS; + unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR; + unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE; + unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE; + unsigned IOCTL_VT_GETMODE = VT_GETMODE; + unsigned IOCTL_VT_GETSTATE = VT_GETSTATE; + unsigned IOCTL_VT_OPENQRY = VT_OPENQRY; + unsigned IOCTL_VT_RELDISP = VT_RELDISP; + unsigned IOCTL_VT_RESIZE = VT_RESIZE; + unsigned IOCTL_VT_RESIZEX = VT_RESIZEX; + unsigned IOCTL_VT_SENDSIG = VT_SENDSIG; + unsigned IOCTL_VT_SETMODE = VT_SETMODE; + unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE; +#endif +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH; + unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT; + unsigned IOCTL_CYGETMON = CYGETMON; + unsigned IOCTL_CYGETTHRESH = CYGETTHRESH; + unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT; + unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH; + unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT; + unsigned IOCTL_CYSETTHRESH = CYSETTHRESH; + unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT; + unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE; + unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE; + unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG; + unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG; + unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG; + unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG; +#if EV_VERSION > (0x010000) + unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2; + unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0); + unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2; +#else + unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT; + unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT; + unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT; +#endif + unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS; + unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION; + unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS; + unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION; + unsigned IOCTL_GIO_CMAP = GIO_CMAP; + unsigned IOCTL_GIO_FONT = GIO_FONT; + unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP; + unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP; + unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP; + unsigned IOCTL_KDADDIO = KDADDIO; + unsigned IOCTL_KDDELIO = KDDELIO; + unsigned IOCTL_KDDISABIO = KDDISABIO; + unsigned IOCTL_KDENABIO = KDENABIO; + unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE; + unsigned IOCTL_KDGETLED = KDGETLED; + unsigned IOCTL_KDGETMODE = KDGETMODE; + unsigned IOCTL_KDGKBDIACR = KDGKBDIACR; + unsigned IOCTL_KDGKBENT = KDGKBENT; + unsigned IOCTL_KDGKBLED = KDGKBLED; + unsigned IOCTL_KDGKBMETA = KDGKBMETA; + unsigned IOCTL_KDGKBMODE = KDGKBMODE; + unsigned IOCTL_KDGKBSENT = KDGKBSENT; + unsigned IOCTL_KDGKBTYPE = KDGKBTYPE; + unsigned IOCTL_KDMAPDISP = KDMAPDISP; + unsigned IOCTL_KDMKTONE = KDMKTONE; + unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE; + unsigned IOCTL_KDSETLED = KDSETLED; + unsigned IOCTL_KDSETMODE = KDSETMODE; + unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT; + unsigned IOCTL_KDSKBDIACR = KDSKBDIACR; + unsigned IOCTL_KDSKBENT = KDSKBENT; + unsigned IOCTL_KDSKBLED = KDSKBLED; + unsigned IOCTL_KDSKBMETA = KDSKBMETA; + unsigned IOCTL_KDSKBMODE = KDSKBMODE; + unsigned IOCTL_KDSKBSENT = KDSKBSENT; + unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP; + unsigned IOCTL_KIOCSOUND = KIOCSOUND; + unsigned IOCTL_LPABORT = LPABORT; + unsigned IOCTL_LPABORTOPEN = LPABORTOPEN; + unsigned IOCTL_LPCAREFUL = LPCAREFUL; + unsigned IOCTL_LPCHAR = LPCHAR; + unsigned IOCTL_LPGETIRQ = LPGETIRQ; + unsigned IOCTL_LPGETSTATUS = LPGETSTATUS; + unsigned IOCTL_LPRESET = LPRESET; + unsigned IOCTL_LPSETIRQ = LPSETIRQ; + unsigned IOCTL_LPTIME = LPTIME; + unsigned IOCTL_LPWAIT = LPWAIT; + unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG; + unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG; + unsigned IOCTL_PIO_CMAP = PIO_CMAP; + unsigned IOCTL_PIO_FONT = PIO_FONT; + unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP; + unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP; + unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR; + unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP; + unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN; + unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST; + unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE; + unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE; + unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT; + unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT; + unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID; + unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID; + unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS; + unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID; + unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID; + unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS; + unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP; + unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA; + unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS; + unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS; + unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL; + unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS; + unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE; + unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE; + unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL; + unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI; + unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; + unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL; +#endif + +// EOWNERDEAD is not present in some older platforms. +#if defined(EOWNERDEAD) + extern const int errno_EOWNERDEAD = EOWNERDEAD; +#else + extern const int errno_EOWNERDEAD = -1; +#endif +} // namespace __sanitizer + +COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t)); + +COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned)); +CHECK_TYPE_SIZE(pthread_key_t); + +#if SANITIZER_LINUX +// There are more undocumented fields in dl_phdr_info that we are not interested +// in. +COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info)); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr); +CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum); + +COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678)); +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +CHECK_TYPE_SIZE(glob_t); +CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc); +CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv); +CHECK_SIZE_AND_OFFSET(glob_t, gl_offs); +CHECK_SIZE_AND_OFFSET(glob_t, gl_flags); +CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat); +CHECK_SIZE_AND_OFFSET(glob_t, gl_stat); +#endif + +CHECK_TYPE_SIZE(addrinfo); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_family); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr); + +CHECK_TYPE_SIZE(hostent); +CHECK_SIZE_AND_OFFSET(hostent, h_name); +CHECK_SIZE_AND_OFFSET(hostent, h_aliases); +CHECK_SIZE_AND_OFFSET(hostent, h_addrtype); +CHECK_SIZE_AND_OFFSET(hostent, h_length); +CHECK_SIZE_AND_OFFSET(hostent, h_addr_list); + +CHECK_TYPE_SIZE(iovec); +CHECK_SIZE_AND_OFFSET(iovec, iov_base); +CHECK_SIZE_AND_OFFSET(iovec, iov_len); + +CHECK_TYPE_SIZE(msghdr); +CHECK_SIZE_AND_OFFSET(msghdr, msg_name); +CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_iov); +CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_control); +CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_flags); + +CHECK_TYPE_SIZE(cmsghdr); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type); + +COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent)); +CHECK_SIZE_AND_OFFSET(dirent, d_ino); +#if SANITIZER_MAC +CHECK_SIZE_AND_OFFSET(dirent, d_seekoff); +#else +CHECK_SIZE_AND_OFFSET(dirent, d_off); +#endif +CHECK_SIZE_AND_OFFSET(dirent, d_reclen); + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64)); +CHECK_SIZE_AND_OFFSET(dirent64, d_ino); +CHECK_SIZE_AND_OFFSET(dirent64, d_off); +CHECK_SIZE_AND_OFFSET(dirent64, d_reclen); +#endif + +CHECK_TYPE_SIZE(ifconf); +CHECK_SIZE_AND_OFFSET(ifconf, ifc_len); +CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu); + +CHECK_TYPE_SIZE(pollfd); +CHECK_SIZE_AND_OFFSET(pollfd, fd); +CHECK_SIZE_AND_OFFSET(pollfd, events); +CHECK_SIZE_AND_OFFSET(pollfd, revents); + +CHECK_TYPE_SIZE(nfds_t); + +CHECK_TYPE_SIZE(sigset_t); + +COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction)); +// Can't write checks for sa_handler and sa_sigaction due to them being +// preprocessor macros. +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); +#if SANITIZER_LINUX +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); +#endif + +#if SANITIZER_LINUX +CHECK_TYPE_SIZE(__sysctl_args); +CHECK_SIZE_AND_OFFSET(__sysctl_args, name); +CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen); +CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval); +CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp); +CHECK_SIZE_AND_OFFSET(__sysctl_args, newval); +CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen); + +CHECK_TYPE_SIZE(__kernel_uid_t); +CHECK_TYPE_SIZE(__kernel_gid_t); +CHECK_TYPE_SIZE(__kernel_old_uid_t); +CHECK_TYPE_SIZE(__kernel_old_gid_t); +CHECK_TYPE_SIZE(__kernel_off_t); +CHECK_TYPE_SIZE(__kernel_loff_t); +CHECK_TYPE_SIZE(__kernel_fd_set); +#endif + +#if !SANITIZER_ANDROID +CHECK_TYPE_SIZE(wordexp_t); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs); +#endif + +CHECK_TYPE_SIZE(tm); +CHECK_SIZE_AND_OFFSET(tm, tm_sec); +CHECK_SIZE_AND_OFFSET(tm, tm_min); +CHECK_SIZE_AND_OFFSET(tm, tm_hour); +CHECK_SIZE_AND_OFFSET(tm, tm_mday); +CHECK_SIZE_AND_OFFSET(tm, tm_mon); +CHECK_SIZE_AND_OFFSET(tm, tm_year); +CHECK_SIZE_AND_OFFSET(tm, tm_wday); +CHECK_SIZE_AND_OFFSET(tm, tm_yday); +CHECK_SIZE_AND_OFFSET(tm, tm_isdst); +CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff); +CHECK_SIZE_AND_OFFSET(tm, tm_zone); + +#if SANITIZER_LINUX +CHECK_TYPE_SIZE(mntent); +CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname); +CHECK_SIZE_AND_OFFSET(mntent, mnt_dir); +CHECK_SIZE_AND_OFFSET(mntent, mnt_type); +CHECK_SIZE_AND_OFFSET(mntent, mnt_opts); +CHECK_SIZE_AND_OFFSET(mntent, mnt_freq); +CHECK_SIZE_AND_OFFSET(mntent, mnt_passno); +#endif + +CHECK_TYPE_SIZE(ether_addr); + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +CHECK_TYPE_SIZE(ipc_perm); +CHECK_SIZE_AND_OFFSET(ipc_perm, __key); +CHECK_SIZE_AND_OFFSET(ipc_perm, uid); +CHECK_SIZE_AND_OFFSET(ipc_perm, gid); +CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); +CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); +CHECK_SIZE_AND_OFFSET(ipc_perm, mode); +CHECK_SIZE_AND_OFFSET(ipc_perm, __seq); + +CHECK_TYPE_SIZE(shmid_ds); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch); +#endif + +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h new file mode 100644 index 00000000..b9a0fc98 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -0,0 +1,997 @@ +//===-- sanitizer_platform_limits_posix.h ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Sanitizer common code. +// +// Sizes and layouts of platform-specific POSIX data structures. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H +#define SANITIZER_PLATFORM_LIMITS_POSIX_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform.h" + +namespace __sanitizer { + extern unsigned struct_utsname_sz; + extern unsigned struct_stat_sz; +#if !SANITIZER_IOS + extern unsigned struct_stat64_sz; +#endif + extern unsigned struct_rusage_sz; + extern unsigned struct_passwd_sz; + extern unsigned struct_group_sz; + extern unsigned siginfo_t_sz; + extern unsigned struct_itimerval_sz; + extern unsigned pthread_t_sz; + extern unsigned pthread_cond_t_sz; + extern unsigned pid_t_sz; + extern unsigned timeval_sz; + extern unsigned uid_t_sz; + extern unsigned mbstate_t_sz; + extern unsigned struct_timezone_sz; + extern unsigned struct_tms_sz; + extern unsigned struct_itimerspec_sz; + extern unsigned struct_sigevent_sz; + extern unsigned struct_sched_param_sz; + extern unsigned struct_statfs_sz; + extern unsigned struct_statfs64_sz; + +#if !SANITIZER_ANDROID + extern unsigned ucontext_t_sz; +#endif // !SANITIZER_ANDROID + +#if SANITIZER_LINUX + +#if defined(__x86_64__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 144; + const unsigned struct_kernel_stat64_sz = 0; +#elif defined(__i386__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 64; + const unsigned struct_kernel_stat64_sz = 96; +#elif defined(__arm__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 64; + const unsigned struct_kernel_stat64_sz = 104; +#elif defined(__powerpc__) && !defined(__powerpc64__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 72; + const unsigned struct_kernel_stat64_sz = 104; +#elif defined(__powerpc64__) + const unsigned struct___old_kernel_stat_sz = 0; + const unsigned struct_kernel_stat_sz = 144; + const unsigned struct_kernel_stat64_sz = 104; +#endif + const unsigned struct_io_event_sz = 32; + struct __sanitizer_perf_event_attr { + unsigned type; + unsigned size; + // More fields that vary with the kernel version. + }; + + extern unsigned struct_utimbuf_sz; + extern unsigned struct_new_utsname_sz; + extern unsigned struct_old_utsname_sz; + extern unsigned struct_oldold_utsname_sz; + extern unsigned struct_msqid_ds_sz; + extern unsigned struct_mq_attr_sz; + extern unsigned struct_timex_sz; + extern unsigned struct_ustat_sz; + + extern unsigned struct_rlimit_sz; + extern unsigned struct_epoll_event_sz; + extern unsigned struct_sysinfo_sz; + extern unsigned struct_timespec_sz; + extern unsigned __user_cap_header_struct_sz; + extern unsigned __user_cap_data_struct_sz; + const unsigned old_sigset_t_sz = sizeof(unsigned long); + const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long); + + struct __sanitizer_iocb { + u64 aio_data; + u32 aio_key_or_aio_reserved1; // Simply crazy. + u32 aio_reserved1_or_aio_key; // Luckily, we don't need these. + u16 aio_lio_opcode; + s16 aio_reqprio; + u32 aio_fildes; + u64 aio_buf; + u64 aio_nbytes; + s64 aio_offset; + u64 aio_reserved2; + u64 aio_reserved3; + }; + + const unsigned iocb_cmd_pread = 0; + const unsigned iocb_cmd_pwrite = 1; + + struct __sanitizer___sysctl_args { + int *name; + int nlen; + void *oldval; + uptr *oldlenp; + void *newval; + uptr newlen; + unsigned long ___unused[4]; + }; +#endif // SANITIZER_LINUX + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_rlimit64_sz; + extern unsigned struct_statvfs_sz; + extern unsigned struct_statvfs64_sz; + + struct __sanitizer_ipc_perm { + int __key; + int uid; + int gid; + int cuid; + int cgid; +#ifdef __powerpc__ + unsigned mode; + unsigned __seq; + u64 __unused1; + u64 __unused2; +#else + unsigned short mode; + unsigned short __pad1; + unsigned short __seq; + unsigned short __pad2; +#if defined(__x86_64__) && !defined(_LP64) + u64 __unused1; + u64 __unused2; +#else + unsigned long __unused1; + unsigned long __unused2; +#endif +#endif + }; + + struct __sanitizer_shmid_ds { + __sanitizer_ipc_perm shm_perm; + #ifndef __powerpc__ + uptr shm_segsz; + #elif !defined(__powerpc64__) + uptr __unused0; + #endif + uptr shm_atime; + #ifndef _LP64 + uptr __unused1; + #endif + uptr shm_dtime; + #ifndef _LP64 + uptr __unused2; + #endif + uptr shm_ctime; + #ifndef _LP64 + uptr __unused3; + #endif + #ifdef __powerpc__ + uptr shm_segsz; + #endif + int shm_cpid; + int shm_lpid; + uptr shm_nattch; + uptr __unused4; + uptr __unused5; + }; + #endif // SANITIZER_LINUX && !SANITIZER_ANDROID + + struct __sanitizer_iovec { + void *iov_base; + uptr iov_len; + }; + +#if SANITIZER_MAC + typedef unsigned long __sanitizer_pthread_key_t; +#else + typedef unsigned __sanitizer_pthread_key_t; +#endif + + struct __sanitizer_ether_addr { + u8 octet[6]; + }; + + struct __sanitizer_tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + long int tm_gmtoff; + const char *tm_zone; + }; + +#if SANITIZER_LINUX + struct __sanitizer_mntent { + char *mnt_fsname; + char *mnt_dir; + char *mnt_type; + char *mnt_opts; + int mnt_freq; + int mnt_passno; + }; +#endif + +#if SANITIZER_ANDROID || SANITIZER_MAC + struct __sanitizer_msghdr { + void *msg_name; + unsigned msg_namelen; + struct __sanitizer_iovec *msg_iov; + unsigned msg_iovlen; + void *msg_control; + unsigned msg_controllen; + int msg_flags; + }; + struct __sanitizer_cmsghdr { + unsigned cmsg_len; + int cmsg_level; + int cmsg_type; + }; +#else + struct __sanitizer_msghdr { + void *msg_name; + unsigned msg_namelen; + struct __sanitizer_iovec *msg_iov; + uptr msg_iovlen; + void *msg_control; + uptr msg_controllen; + int msg_flags; + }; + struct __sanitizer_cmsghdr { + uptr cmsg_len; + int cmsg_level; + int cmsg_type; + }; +#endif + +#if SANITIZER_MAC + struct __sanitizer_dirent { + unsigned long long d_ino; + unsigned long long d_seekoff; + unsigned short d_reclen; + // more fields that we don't care about + }; +#elif SANITIZER_ANDROID || defined(__x86_64__) + struct __sanitizer_dirent { + unsigned long long d_ino; + unsigned long long d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#else + struct __sanitizer_dirent { + uptr d_ino; + uptr d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + struct __sanitizer_dirent64 { + unsigned long long d_ino; + unsigned long long d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#endif + +#if SANITIZER_LINUX +#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__) + typedef unsigned __sanitizer___kernel_uid_t; + typedef unsigned __sanitizer___kernel_gid_t; +#else + typedef unsigned short __sanitizer___kernel_uid_t; + typedef unsigned short __sanitizer___kernel_gid_t; +#endif +#if defined(__x86_64__) && !defined(_LP64) + typedef long long __sanitizer___kernel_off_t; +#else + typedef long __sanitizer___kernel_off_t; +#endif + +#if defined(__powerpc__) + typedef unsigned int __sanitizer___kernel_old_uid_t; + typedef unsigned int __sanitizer___kernel_old_gid_t; +#else + typedef unsigned short __sanitizer___kernel_old_uid_t; + typedef unsigned short __sanitizer___kernel_old_gid_t; +#endif + + typedef long long __sanitizer___kernel_loff_t; + typedef struct { + unsigned long fds_bits[1024 / (8 * sizeof(long))]; + } __sanitizer___kernel_fd_set; +#endif + + // This thing depends on the platform. We are only interested in the upper + // limit. Verified with a compiler assert in .cc. + const int pthread_attr_t_max_sz = 128; + union __sanitizer_pthread_attr_t { + char size[pthread_attr_t_max_sz]; // NOLINT + void *align; + }; + +#if SANITIZER_ANDROID + typedef unsigned long __sanitizer_sigset_t; +#elif SANITIZER_MAC + typedef unsigned __sanitizer_sigset_t; +#elif SANITIZER_LINUX + struct __sanitizer_sigset_t { + // The size is determined by looking at sizeof of real sigset_t on linux. + uptr val[128 / sizeof(uptr)]; + }; +#endif + + struct __sanitizer_sigaction { + union { + void (*sa_handler)(int sig); + void (*sa_sigaction)(int sig, void *siginfo, void *uctx); + }; + __sanitizer_sigset_t sa_mask; + int sa_flags; +#if SANITIZER_LINUX + void (*sa_restorer)(); +#endif + }; + + struct __sanitizer_kernel_sigset_t { + u8 sig[8]; + }; + + struct __sanitizer_kernel_sigaction_t { + union { + void (*sigaction)(int signo, void *info, void *ctx); + void (*handler)(int signo); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + __sanitizer_kernel_sigset_t sa_mask; + }; + + extern uptr sig_ign; + extern uptr sig_dfl; + extern uptr sa_siginfo; + +#if SANITIZER_LINUX + extern int e_tabsz; +#endif + + extern int af_inet; + extern int af_inet6; + uptr __sanitizer_in_addr_sz(int af); + +#if SANITIZER_LINUX + struct __sanitizer_dl_phdr_info { + uptr dlpi_addr; + const char *dlpi_name; + const void *dlpi_phdr; + short dlpi_phnum; + }; +#endif + + struct __sanitizer_addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; +#if SANITIZER_ANDROID || SANITIZER_MAC + unsigned ai_addrlen; + char *ai_canonname; + void *ai_addr; +#else // LINUX + unsigned ai_addrlen; + void *ai_addr; + char *ai_canonname; +#endif + struct __sanitizer_addrinfo *ai_next; + }; + + struct __sanitizer_hostent { + char *h_name; + char **h_aliases; + int h_addrtype; + int h_length; + char **h_addr_list; + }; + + struct __sanitizer_pollfd { + int fd; + short events; + short revents; + }; + +#if SANITIZER_ANDROID || SANITIZER_MAC + typedef unsigned __sanitizer_nfds_t; +#else + typedef unsigned long __sanitizer_nfds_t; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + struct __sanitizer_glob_t { + uptr gl_pathc; + char **gl_pathv; + uptr gl_offs; + int gl_flags; + + void (*gl_closedir)(void *dirp); + void *(*gl_readdir)(void *dirp); + void *(*gl_opendir)(const char *); + int (*gl_lstat)(const char *, void *); + int (*gl_stat)(const char *, void *); + }; + + extern int glob_nomatch; + extern int glob_altdirfunc; +#endif + + extern unsigned path_max; + + struct __sanitizer_wordexp_t { + uptr we_wordc; + char **we_wordv; + uptr we_offs; + }; + +#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT + extern unsigned struct_user_regs_struct_sz; + extern unsigned struct_user_fpregs_struct_sz; + extern unsigned struct_user_fpxregs_struct_sz; + + extern int ptrace_peektext; + extern int ptrace_peekdata; + extern int ptrace_peekuser; + extern int ptrace_getregs; + extern int ptrace_setregs; + extern int ptrace_getfpregs; + extern int ptrace_setfpregs; + extern int ptrace_getfpxregs; + extern int ptrace_setfpxregs; + extern int ptrace_getsiginfo; + extern int ptrace_setsiginfo; + extern int ptrace_getregset; + extern int ptrace_setregset; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_shminfo_sz; + extern unsigned struct_shm_info_sz; + extern int shmctl_ipc_stat; + extern int shmctl_ipc_info; + extern int shmctl_shm_info; + extern int shmctl_shm_stat; +#endif + + // ioctl arguments + struct __sanitizer_ifconf { + int ifc_len; + union { + void *ifcu_req; + } ifc_ifcu; +#if SANITIZER_MAC + } __attribute__((packed)); +#else + }; +#endif + +#define IOC_SIZE(nr) (((nr) >> 16) & 0x3fff) + + extern unsigned struct_arpreq_sz; + extern unsigned struct_ifreq_sz; + extern unsigned struct_termios_sz; + extern unsigned struct_winsize_sz; + +#if SANITIZER_LINUX + extern unsigned struct_cdrom_msf_sz; + extern unsigned struct_cdrom_multisession_sz; + extern unsigned struct_cdrom_read_audio_sz; + extern unsigned struct_cdrom_subchnl_sz; + extern unsigned struct_cdrom_ti_sz; + extern unsigned struct_cdrom_tocentry_sz; + extern unsigned struct_cdrom_tochdr_sz; + extern unsigned struct_cdrom_volctrl_sz; + extern unsigned struct_copr_buffer_sz; + extern unsigned struct_copr_debug_buf_sz; + extern unsigned struct_copr_msg_sz; + extern unsigned struct_ff_effect_sz; + extern unsigned struct_floppy_drive_params_sz; + extern unsigned struct_floppy_drive_struct_sz; + extern unsigned struct_floppy_fdc_state_sz; + extern unsigned struct_floppy_max_errors_sz; + extern unsigned struct_floppy_raw_cmd_sz; + extern unsigned struct_floppy_struct_sz; + extern unsigned struct_floppy_write_errors_sz; + extern unsigned struct_format_descr_sz; + extern unsigned struct_hd_driveid_sz; + extern unsigned struct_hd_geometry_sz; + extern unsigned struct_input_absinfo_sz; + extern unsigned struct_input_id_sz; + extern unsigned struct_midi_info_sz; + extern unsigned struct_mtget_sz; + extern unsigned struct_mtop_sz; + extern unsigned struct_mtpos_sz; + extern unsigned struct_rtentry_sz; + extern unsigned struct_sbi_instrument_sz; + extern unsigned struct_seq_event_rec_sz; + extern unsigned struct_synth_info_sz; + extern unsigned struct_termio_sz; + extern unsigned struct_vt_consize_sz; + extern unsigned struct_vt_mode_sz; + extern unsigned struct_vt_sizes_sz; + extern unsigned struct_vt_stat_sz; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_audio_buf_info_sz; + extern unsigned struct_ax25_parms_struct_sz; + extern unsigned struct_cyclades_monitor_sz; + extern unsigned struct_input_keymap_entry_sz; + extern unsigned struct_ipx_config_data_sz; + extern unsigned struct_kbdiacrs_sz; + extern unsigned struct_kbentry_sz; + extern unsigned struct_kbkeycode_sz; + extern unsigned struct_kbsentry_sz; + extern unsigned struct_mtconfiginfo_sz; + extern unsigned struct_nr_parms_struct_sz; + extern unsigned struct_ppp_stats_sz; + extern unsigned struct_scc_modem_sz; + extern unsigned struct_scc_stat_sz; + extern unsigned struct_serial_multiport_struct_sz; + extern unsigned struct_serial_struct_sz; + extern unsigned struct_sockaddr_ax25_sz; + extern unsigned struct_unimapdesc_sz; + extern unsigned struct_unimapinit_sz; +#endif + +#if !SANITIZER_ANDROID && !SANITIZER_MAC + extern unsigned struct_sioc_sg_req_sz; + extern unsigned struct_sioc_vif_req_sz; +#endif + + // ioctl request identifiers + + // A special value to mark ioctls that are not present on the target platform, + // when it can not be determined without including any system headers. + extern unsigned IOCTL_NOT_PRESENT; + + extern unsigned IOCTL_FIOASYNC; + extern unsigned IOCTL_FIOCLEX; + extern unsigned IOCTL_FIOGETOWN; + extern unsigned IOCTL_FIONBIO; + extern unsigned IOCTL_FIONCLEX; + extern unsigned IOCTL_FIOSETOWN; + extern unsigned IOCTL_SIOCADDMULTI; + extern unsigned IOCTL_SIOCATMARK; + extern unsigned IOCTL_SIOCDELMULTI; + extern unsigned IOCTL_SIOCGIFADDR; + extern unsigned IOCTL_SIOCGIFBRDADDR; + extern unsigned IOCTL_SIOCGIFCONF; + extern unsigned IOCTL_SIOCGIFDSTADDR; + extern unsigned IOCTL_SIOCGIFFLAGS; + extern unsigned IOCTL_SIOCGIFMETRIC; + extern unsigned IOCTL_SIOCGIFMTU; + extern unsigned IOCTL_SIOCGIFNETMASK; + extern unsigned IOCTL_SIOCGPGRP; + extern unsigned IOCTL_SIOCSIFADDR; + extern unsigned IOCTL_SIOCSIFBRDADDR; + extern unsigned IOCTL_SIOCSIFDSTADDR; + extern unsigned IOCTL_SIOCSIFFLAGS; + extern unsigned IOCTL_SIOCSIFMETRIC; + extern unsigned IOCTL_SIOCSIFMTU; + extern unsigned IOCTL_SIOCSIFNETMASK; + extern unsigned IOCTL_SIOCSPGRP; + extern unsigned IOCTL_TIOCCONS; + extern unsigned IOCTL_TIOCEXCL; + extern unsigned IOCTL_TIOCGETD; + extern unsigned IOCTL_TIOCGPGRP; + extern unsigned IOCTL_TIOCGWINSZ; + extern unsigned IOCTL_TIOCMBIC; + extern unsigned IOCTL_TIOCMBIS; + extern unsigned IOCTL_TIOCMGET; + extern unsigned IOCTL_TIOCMSET; + extern unsigned IOCTL_TIOCNOTTY; + extern unsigned IOCTL_TIOCNXCL; + extern unsigned IOCTL_TIOCOUTQ; + extern unsigned IOCTL_TIOCPKT; + extern unsigned IOCTL_TIOCSCTTY; + extern unsigned IOCTL_TIOCSETD; + extern unsigned IOCTL_TIOCSPGRP; + extern unsigned IOCTL_TIOCSTI; + extern unsigned IOCTL_TIOCSWINSZ; +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) + extern unsigned IOCTL_SIOCGETSGCNT; + extern unsigned IOCTL_SIOCGETVIFCNT; +#endif +#if SANITIZER_LINUX + extern unsigned IOCTL_EVIOCGABS; + extern unsigned IOCTL_EVIOCGBIT; + extern unsigned IOCTL_EVIOCGEFFECTS; + extern unsigned IOCTL_EVIOCGID; + extern unsigned IOCTL_EVIOCGKEY; + extern unsigned IOCTL_EVIOCGKEYCODE; + extern unsigned IOCTL_EVIOCGLED; + extern unsigned IOCTL_EVIOCGNAME; + extern unsigned IOCTL_EVIOCGPHYS; + extern unsigned IOCTL_EVIOCGRAB; + extern unsigned IOCTL_EVIOCGREP; + extern unsigned IOCTL_EVIOCGSND; + extern unsigned IOCTL_EVIOCGSW; + extern unsigned IOCTL_EVIOCGUNIQ; + extern unsigned IOCTL_EVIOCGVERSION; + extern unsigned IOCTL_EVIOCRMFF; + extern unsigned IOCTL_EVIOCSABS; + extern unsigned IOCTL_EVIOCSFF; + extern unsigned IOCTL_EVIOCSKEYCODE; + extern unsigned IOCTL_EVIOCSREP; + extern unsigned IOCTL_BLKFLSBUF; + extern unsigned IOCTL_BLKGETSIZE; + extern unsigned IOCTL_BLKRAGET; + extern unsigned IOCTL_BLKRASET; + extern unsigned IOCTL_BLKROGET; + extern unsigned IOCTL_BLKROSET; + extern unsigned IOCTL_BLKRRPART; + extern unsigned IOCTL_CDROMAUDIOBUFSIZ; + extern unsigned IOCTL_CDROMEJECT; + extern unsigned IOCTL_CDROMEJECT_SW; + extern unsigned IOCTL_CDROMMULTISESSION; + extern unsigned IOCTL_CDROMPAUSE; + extern unsigned IOCTL_CDROMPLAYMSF; + extern unsigned IOCTL_CDROMPLAYTRKIND; + extern unsigned IOCTL_CDROMREADAUDIO; + extern unsigned IOCTL_CDROMREADCOOKED; + extern unsigned IOCTL_CDROMREADMODE1; + extern unsigned IOCTL_CDROMREADMODE2; + extern unsigned IOCTL_CDROMREADRAW; + extern unsigned IOCTL_CDROMREADTOCENTRY; + extern unsigned IOCTL_CDROMREADTOCHDR; + extern unsigned IOCTL_CDROMRESET; + extern unsigned IOCTL_CDROMRESUME; + extern unsigned IOCTL_CDROMSEEK; + extern unsigned IOCTL_CDROMSTART; + extern unsigned IOCTL_CDROMSTOP; + extern unsigned IOCTL_CDROMSUBCHNL; + extern unsigned IOCTL_CDROMVOLCTRL; + extern unsigned IOCTL_CDROMVOLREAD; + extern unsigned IOCTL_CDROM_GET_UPC; + extern unsigned IOCTL_FDCLRPRM; + extern unsigned IOCTL_FDDEFPRM; + extern unsigned IOCTL_FDFLUSH; + extern unsigned IOCTL_FDFMTBEG; + extern unsigned IOCTL_FDFMTEND; + extern unsigned IOCTL_FDFMTTRK; + extern unsigned IOCTL_FDGETDRVPRM; + extern unsigned IOCTL_FDGETDRVSTAT; + extern unsigned IOCTL_FDGETDRVTYP; + extern unsigned IOCTL_FDGETFDCSTAT; + extern unsigned IOCTL_FDGETMAXERRS; + extern unsigned IOCTL_FDGETPRM; + extern unsigned IOCTL_FDMSGOFF; + extern unsigned IOCTL_FDMSGON; + extern unsigned IOCTL_FDPOLLDRVSTAT; + extern unsigned IOCTL_FDRAWCMD; + extern unsigned IOCTL_FDRESET; + extern unsigned IOCTL_FDSETDRVPRM; + extern unsigned IOCTL_FDSETEMSGTRESH; + extern unsigned IOCTL_FDSETMAXERRS; + extern unsigned IOCTL_FDSETPRM; + extern unsigned IOCTL_FDTWADDLE; + extern unsigned IOCTL_FDWERRORCLR; + extern unsigned IOCTL_FDWERRORGET; + extern unsigned IOCTL_HDIO_DRIVE_CMD; + extern unsigned IOCTL_HDIO_GETGEO; + extern unsigned IOCTL_HDIO_GET_32BIT; + extern unsigned IOCTL_HDIO_GET_DMA; + extern unsigned IOCTL_HDIO_GET_IDENTITY; + extern unsigned IOCTL_HDIO_GET_KEEPSETTINGS; + extern unsigned IOCTL_HDIO_GET_MULTCOUNT; + extern unsigned IOCTL_HDIO_GET_NOWERR; + extern unsigned IOCTL_HDIO_GET_UNMASKINTR; + extern unsigned IOCTL_HDIO_SET_32BIT; + extern unsigned IOCTL_HDIO_SET_DMA; + extern unsigned IOCTL_HDIO_SET_KEEPSETTINGS; + extern unsigned IOCTL_HDIO_SET_MULTCOUNT; + extern unsigned IOCTL_HDIO_SET_NOWERR; + extern unsigned IOCTL_HDIO_SET_UNMASKINTR; + extern unsigned IOCTL_MTIOCGET; + extern unsigned IOCTL_MTIOCPOS; + extern unsigned IOCTL_MTIOCTOP; + extern unsigned IOCTL_PPPIOCGASYNCMAP; + extern unsigned IOCTL_PPPIOCGDEBUG; + extern unsigned IOCTL_PPPIOCGFLAGS; + extern unsigned IOCTL_PPPIOCGUNIT; + extern unsigned IOCTL_PPPIOCGXASYNCMAP; + extern unsigned IOCTL_PPPIOCSASYNCMAP; + extern unsigned IOCTL_PPPIOCSDEBUG; + extern unsigned IOCTL_PPPIOCSFLAGS; + extern unsigned IOCTL_PPPIOCSMAXCID; + extern unsigned IOCTL_PPPIOCSMRU; + extern unsigned IOCTL_PPPIOCSXASYNCMAP; + extern unsigned IOCTL_SIOCADDRT; + extern unsigned IOCTL_SIOCDARP; + extern unsigned IOCTL_SIOCDELRT; + extern unsigned IOCTL_SIOCDRARP; + extern unsigned IOCTL_SIOCGARP; + extern unsigned IOCTL_SIOCGIFENCAP; + extern unsigned IOCTL_SIOCGIFHWADDR; + extern unsigned IOCTL_SIOCGIFMAP; + extern unsigned IOCTL_SIOCGIFMEM; + extern unsigned IOCTL_SIOCGIFNAME; + extern unsigned IOCTL_SIOCGIFSLAVE; + extern unsigned IOCTL_SIOCGRARP; + extern unsigned IOCTL_SIOCGSTAMP; + extern unsigned IOCTL_SIOCSARP; + extern unsigned IOCTL_SIOCSIFENCAP; + extern unsigned IOCTL_SIOCSIFHWADDR; + extern unsigned IOCTL_SIOCSIFLINK; + extern unsigned IOCTL_SIOCSIFMAP; + extern unsigned IOCTL_SIOCSIFMEM; + extern unsigned IOCTL_SIOCSIFSLAVE; + extern unsigned IOCTL_SIOCSRARP; + extern unsigned IOCTL_SNDCTL_COPR_HALT; + extern unsigned IOCTL_SNDCTL_COPR_LOAD; + extern unsigned IOCTL_SNDCTL_COPR_RCODE; + extern unsigned IOCTL_SNDCTL_COPR_RCVMSG; + extern unsigned IOCTL_SNDCTL_COPR_RDATA; + extern unsigned IOCTL_SNDCTL_COPR_RESET; + extern unsigned IOCTL_SNDCTL_COPR_RUN; + extern unsigned IOCTL_SNDCTL_COPR_SENDMSG; + extern unsigned IOCTL_SNDCTL_COPR_WCODE; + extern unsigned IOCTL_SNDCTL_COPR_WDATA; + extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE; + extern unsigned IOCTL_SNDCTL_DSP_GETFMTS; + extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK; + extern unsigned IOCTL_SNDCTL_DSP_POST; + extern unsigned IOCTL_SNDCTL_DSP_RESET; + extern unsigned IOCTL_SNDCTL_DSP_SETFMT; + extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT; + extern unsigned IOCTL_SNDCTL_DSP_SPEED; + extern unsigned IOCTL_SNDCTL_DSP_STEREO; + extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE; + extern unsigned IOCTL_SNDCTL_DSP_SYNC; + extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE; + extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR; + extern unsigned IOCTL_SNDCTL_MIDI_INFO; + extern unsigned IOCTL_SNDCTL_MIDI_PRETIME; + extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE; + extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT; + extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT; + extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS; + extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS; + extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND; + extern unsigned IOCTL_SNDCTL_SEQ_PANIC; + extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE; + extern unsigned IOCTL_SNDCTL_SEQ_RESET; + extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES; + extern unsigned IOCTL_SNDCTL_SEQ_SYNC; + extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI; + extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD; + extern unsigned IOCTL_SNDCTL_SYNTH_INFO; + extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL; + extern unsigned IOCTL_SNDCTL_TMR_CONTINUE; + extern unsigned IOCTL_SNDCTL_TMR_METRONOME; + extern unsigned IOCTL_SNDCTL_TMR_SELECT; + extern unsigned IOCTL_SNDCTL_TMR_SOURCE; + extern unsigned IOCTL_SNDCTL_TMR_START; + extern unsigned IOCTL_SNDCTL_TMR_STOP; + extern unsigned IOCTL_SNDCTL_TMR_TEMPO; + extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE; + extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM; + extern unsigned IOCTL_SOUND_MIXER_READ_BASS; + extern unsigned IOCTL_SOUND_MIXER_READ_CAPS; + extern unsigned IOCTL_SOUND_MIXER_READ_CD; + extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK; + extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE; + extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN; + extern unsigned IOCTL_SOUND_MIXER_READ_IMIX; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE1; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE2; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE3; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE; + extern unsigned IOCTL_SOUND_MIXER_READ_LOUD; + extern unsigned IOCTL_SOUND_MIXER_READ_MIC; + extern unsigned IOCTL_SOUND_MIXER_READ_MUTE; + extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN; + extern unsigned IOCTL_SOUND_MIXER_READ_PCM; + extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV; + extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK; + extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC; + extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER; + extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS; + extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH; + extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE; + extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME; + extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM; + extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS; + extern unsigned IOCTL_SOUND_MIXER_WRITE_CD; + extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN; + extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD; + extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC; + extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN; + extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM; + extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV; + extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC; + extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER; + extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH; + extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME; + extern unsigned IOCTL_SOUND_PCM_READ_BITS; + extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS; + extern unsigned IOCTL_SOUND_PCM_READ_FILTER; + extern unsigned IOCTL_SOUND_PCM_READ_RATE; + extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS; + extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER; + extern unsigned IOCTL_TCFLSH; + extern unsigned IOCTL_TCGETA; + extern unsigned IOCTL_TCGETS; + extern unsigned IOCTL_TCSBRK; + extern unsigned IOCTL_TCSBRKP; + extern unsigned IOCTL_TCSETA; + extern unsigned IOCTL_TCSETAF; + extern unsigned IOCTL_TCSETAW; + extern unsigned IOCTL_TCSETS; + extern unsigned IOCTL_TCSETSF; + extern unsigned IOCTL_TCSETSW; + extern unsigned IOCTL_TCXONC; + extern unsigned IOCTL_TIOCGLCKTRMIOS; + extern unsigned IOCTL_TIOCGSOFTCAR; + extern unsigned IOCTL_TIOCINQ; + extern unsigned IOCTL_TIOCLINUX; + extern unsigned IOCTL_TIOCSERCONFIG; + extern unsigned IOCTL_TIOCSERGETLSR; + extern unsigned IOCTL_TIOCSERGWILD; + extern unsigned IOCTL_TIOCSERSWILD; + extern unsigned IOCTL_TIOCSLCKTRMIOS; + extern unsigned IOCTL_TIOCSSOFTCAR; + extern unsigned IOCTL_VT_ACTIVATE; + extern unsigned IOCTL_VT_DISALLOCATE; + extern unsigned IOCTL_VT_GETMODE; + extern unsigned IOCTL_VT_GETSTATE; + extern unsigned IOCTL_VT_OPENQRY; + extern unsigned IOCTL_VT_RELDISP; + extern unsigned IOCTL_VT_RESIZE; + extern unsigned IOCTL_VT_RESIZEX; + extern unsigned IOCTL_VT_SENDSIG; + extern unsigned IOCTL_VT_SETMODE; + extern unsigned IOCTL_VT_WAITACTIVE; +#endif +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned IOCTL_CYGETDEFTHRESH; + extern unsigned IOCTL_CYGETDEFTIMEOUT; + extern unsigned IOCTL_CYGETMON; + extern unsigned IOCTL_CYGETTHRESH; + extern unsigned IOCTL_CYGETTIMEOUT; + extern unsigned IOCTL_CYSETDEFTHRESH; + extern unsigned IOCTL_CYSETDEFTIMEOUT; + extern unsigned IOCTL_CYSETTHRESH; + extern unsigned IOCTL_CYSETTIMEOUT; + extern unsigned IOCTL_EQL_EMANCIPATE; + extern unsigned IOCTL_EQL_ENSLAVE; + extern unsigned IOCTL_EQL_GETMASTRCFG; + extern unsigned IOCTL_EQL_GETSLAVECFG; + extern unsigned IOCTL_EQL_SETMASTRCFG; + extern unsigned IOCTL_EQL_SETSLAVECFG; + extern unsigned IOCTL_EVIOCGKEYCODE_V2; + extern unsigned IOCTL_EVIOCGPROP; + extern unsigned IOCTL_EVIOCSKEYCODE_V2; + extern unsigned IOCTL_FS_IOC_GETFLAGS; + extern unsigned IOCTL_FS_IOC_GETVERSION; + extern unsigned IOCTL_FS_IOC_SETFLAGS; + extern unsigned IOCTL_FS_IOC_SETVERSION; + extern unsigned IOCTL_GIO_CMAP; + extern unsigned IOCTL_GIO_FONT; + extern unsigned IOCTL_GIO_SCRNMAP; + extern unsigned IOCTL_GIO_UNIMAP; + extern unsigned IOCTL_GIO_UNISCRNMAP; + extern unsigned IOCTL_KDADDIO; + extern unsigned IOCTL_KDDELIO; + extern unsigned IOCTL_KDDISABIO; + extern unsigned IOCTL_KDENABIO; + extern unsigned IOCTL_KDGETKEYCODE; + extern unsigned IOCTL_KDGETLED; + extern unsigned IOCTL_KDGETMODE; + extern unsigned IOCTL_KDGKBDIACR; + extern unsigned IOCTL_KDGKBENT; + extern unsigned IOCTL_KDGKBLED; + extern unsigned IOCTL_KDGKBMETA; + extern unsigned IOCTL_KDGKBMODE; + extern unsigned IOCTL_KDGKBSENT; + extern unsigned IOCTL_KDGKBTYPE; + extern unsigned IOCTL_KDMAPDISP; + extern unsigned IOCTL_KDMKTONE; + extern unsigned IOCTL_KDSETKEYCODE; + extern unsigned IOCTL_KDSETLED; + extern unsigned IOCTL_KDSETMODE; + extern unsigned IOCTL_KDSIGACCEPT; + extern unsigned IOCTL_KDSKBDIACR; + extern unsigned IOCTL_KDSKBENT; + extern unsigned IOCTL_KDSKBLED; + extern unsigned IOCTL_KDSKBMETA; + extern unsigned IOCTL_KDSKBMODE; + extern unsigned IOCTL_KDSKBSENT; + extern unsigned IOCTL_KDUNMAPDISP; + extern unsigned IOCTL_KIOCSOUND; + extern unsigned IOCTL_LPABORT; + extern unsigned IOCTL_LPABORTOPEN; + extern unsigned IOCTL_LPCAREFUL; + extern unsigned IOCTL_LPCHAR; + extern unsigned IOCTL_LPGETIRQ; + extern unsigned IOCTL_LPGETSTATUS; + extern unsigned IOCTL_LPRESET; + extern unsigned IOCTL_LPSETIRQ; + extern unsigned IOCTL_LPTIME; + extern unsigned IOCTL_LPWAIT; + extern unsigned IOCTL_MTIOCGETCONFIG; + extern unsigned IOCTL_MTIOCSETCONFIG; + extern unsigned IOCTL_PIO_CMAP; + extern unsigned IOCTL_PIO_FONT; + extern unsigned IOCTL_PIO_SCRNMAP; + extern unsigned IOCTL_PIO_UNIMAP; + extern unsigned IOCTL_PIO_UNIMAPCLR; + extern unsigned IOCTL_PIO_UNISCRNMAP; + extern unsigned IOCTL_SCSI_IOCTL_GET_IDLUN; + extern unsigned IOCTL_SCSI_IOCTL_PROBE_HOST; + extern unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE; + extern unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE; + extern unsigned IOCTL_SIOCAIPXITFCRT; + extern unsigned IOCTL_SIOCAIPXPRISLT; + extern unsigned IOCTL_SIOCAX25ADDUID; + extern unsigned IOCTL_SIOCAX25DELUID; + extern unsigned IOCTL_SIOCAX25GETPARMS; + extern unsigned IOCTL_SIOCAX25GETUID; + extern unsigned IOCTL_SIOCAX25NOUID; + extern unsigned IOCTL_SIOCAX25SETPARMS; + extern unsigned IOCTL_SIOCDEVPLIP; + extern unsigned IOCTL_SIOCIPXCFGDATA; + extern unsigned IOCTL_SIOCNRDECOBS; + extern unsigned IOCTL_SIOCNRGETPARMS; + extern unsigned IOCTL_SIOCNRRTCTL; + extern unsigned IOCTL_SIOCNRSETPARMS; + extern unsigned IOCTL_SNDCTL_DSP_GETISPACE; + extern unsigned IOCTL_SNDCTL_DSP_GETOSPACE; + extern unsigned IOCTL_TIOCGSERIAL; + extern unsigned IOCTL_TIOCSERGETMULTI; + extern unsigned IOCTL_TIOCSERSETMULTI; + extern unsigned IOCTL_TIOCSSERIAL; +#endif + + extern const int errno_EOWNERDEAD; +} // namespace __sanitizer + +#define CHECK_TYPE_SIZE(TYPE) \ + COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE)) + +#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \ + offsetof(CLASS, MEMBER)) + +// For sigaction, which is a function and struct at the same time, +// and thus requires explicit "struct" in sizeof() expression. +#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((struct CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \ + offsetof(struct CLASS, MEMBER)) + +#endif diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc new file mode 100644 index 00000000..ffe91df1 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc @@ -0,0 +1,249 @@ +//===-- sanitizer_posix.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements POSIX-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" + +#include + +namespace __sanitizer { + +// ------------- sanitizer_common.h +uptr GetMmapGranularity() { + return GetPageSize(); +} + +uptr GetMaxVirtualAddress() { +#if SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) + // On PowerPC64 we have two different address space layouts: 44- and 46-bit. + // We somehow need to figure our which one we are using now and choose + // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. + // Note that with 'ulimit -s unlimited' the stack is moved away from the top + // of the address space, so simply checking the stack address is not enough. + return (1ULL << 44) - 1; // 0x00000fffffffffffUL +# else + return (1ULL << 47) - 1; // 0x00007fffffffffffUL; +# endif +#else // SANITIZER_WORDSIZE == 32 + // FIXME: We can probably lower this on Android? + return (1ULL << 32) - 1; // 0xffffffff; +#endif // SANITIZER_WORDSIZE +} + +void *MmapOrDie(uptr size, const char *mem_type) { + size = RoundUpTo(size, GetPageSizeCached()); + uptr res = internal_mmap(0, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + int reserrno; + if (internal_iserror(res, &reserrno)) { + static int recursion_count; + if (recursion_count) { + // The Report() and CHECK calls below may call mmap recursively and fail. + // If we went into recursion, just die. + RawWrite("ERROR: Failed to mmap\n"); + Die(); + } + recursion_count++; + Report("ERROR: %s failed to allocate 0x%zx (%zd) bytes of %s: %d\n", + SanitizerToolName, size, size, mem_type, reserrno); + DumpProcessMap(); + CHECK("unable to mmap" && 0); + } + return (void *)res; +} + +void UnmapOrDie(void *addr, uptr size) { + if (!addr || !size) return; + uptr res = internal_munmap(addr, size); + if (internal_iserror(res)) { + Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", + SanitizerToolName, size, size, addr); + CHECK("unable to unmap" && 0); + } +} + +void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { + uptr PageSize = GetPageSizeCached(); + uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), + RoundUpTo(size, PageSize), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, + -1, 0); + int reserrno; + if (internal_iserror(p, &reserrno)) + Report("ERROR: " + "%s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", + SanitizerToolName, size, size, fixed_addr, reserrno); + return (void *)p; +} + +void *MmapFixedOrDie(uptr fixed_addr, uptr size) { + uptr PageSize = GetPageSizeCached(); + uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), + RoundUpTo(size, PageSize), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, 0); + int reserrno; + if (internal_iserror(p, &reserrno)) { + Report("ERROR:" + " %s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", + SanitizerToolName, size, size, fixed_addr, reserrno); + CHECK("unable to mmap" && 0); + } + return (void *)p; +} + +void *Mprotect(uptr fixed_addr, uptr size) { + return (void *)internal_mmap((void*)fixed_addr, size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | + MAP_NORESERVE, -1, 0); +} + +void *MapFileToMemory(const char *file_name, uptr *buff_size) { + uptr openrv = OpenFile(file_name, false); + CHECK(!internal_iserror(openrv)); + fd_t fd = openrv; + uptr fsize = internal_filesize(fd); + CHECK_NE(fsize, (uptr)-1); + CHECK_GT(fsize, 0); + *buff_size = RoundUpTo(fsize, GetPageSizeCached()); + uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); + return internal_iserror(map) ? 0 : (void *)map; +} + + +static inline bool IntervalsAreSeparate(uptr start1, uptr end1, + uptr start2, uptr end2) { + CHECK(start1 <= end1); + CHECK(start2 <= end2); + return (end1 < start2) || (end2 < start1); +} + +// FIXME: this is thread-unsafe, but should not cause problems most of the time. +// When the shadow is mapped only a single thread usually exists (plus maybe +// several worker threads on Mac, which aren't expected to map big chunks of +// memory). +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr start, end; + while (proc_maps.Next(&start, &end, + /*offset*/0, /*filename*/0, /*filename_size*/0, + /*protection*/0)) { + if (!IntervalsAreSeparate(start, end, range_start, range_end)) + return false; + } + return true; +} + +void DumpProcessMap() { + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr start, end; + const sptr kBufSize = 4095; + char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__); + Report("Process memory map follows:\n"); + while (proc_maps.Next(&start, &end, /* file_offset */0, + filename, kBufSize, /* protection */0)) { + Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); + } + Report("End of process memory map.\n"); + UnmapOrDie(filename, kBufSize); +} + +const char *GetPwd() { + return GetEnv("PWD"); +} + +char *FindPathToBinary(const char *name) { + const char *path = GetEnv("PATH"); + if (!path) + return 0; + uptr name_len = internal_strlen(name); + InternalScopedBuffer buffer(kMaxPathLength); + const char *beg = path; + while (true) { + const char *end = internal_strchrnul(beg, ':'); + uptr prefix_len = end - beg; + if (prefix_len + name_len + 2 <= kMaxPathLength) { + internal_memcpy(buffer.data(), beg, prefix_len); + buffer[prefix_len] = '/'; + internal_memcpy(&buffer[prefix_len + 1], name, name_len); + buffer[prefix_len + 1 + name_len] = '\0'; + if (FileExists(buffer.data())) + return internal_strdup(buffer.data()); + } + if (*end == '\0') break; + beg = end + 1; + } + return 0; +} + +void MaybeOpenReportFile() { + if (!log_to_file || (report_fd_pid == internal_getpid())) return; + InternalScopedBuffer report_path_full(4096); + internal_snprintf(report_path_full.data(), report_path_full.size(), + "%s.%d", report_path_prefix, internal_getpid()); + uptr openrv = OpenFile(report_path_full.data(), true); + if (internal_iserror(openrv)) { + report_fd = kStderrFd; + log_to_file = false; + Report("ERROR: Can't open file: %s\n", report_path_full.data()); + Die(); + } + if (report_fd != kInvalidFd) { + // We're in the child. Close the parent's log. + internal_close(report_fd); + } + report_fd = openrv; + report_fd_pid = internal_getpid(); +} + +void RawWrite(const char *buffer) { + static const char *kRawWriteError = + "RawWrite can't output requested buffer!\n"; + uptr length = (uptr)internal_strlen(buffer); + MaybeOpenReportFile(); + if (length != internal_write(report_fd, buffer, length)) { + internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError)); + Die(); + } +} + +bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { + uptr s, e, off, prot; + InternalMmapVector fn(4096); + fn.push_back(0); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + while (proc_maps.Next(&s, &e, &off, &fn[0], fn.capacity(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 + && internal_strcmp(module, &fn[0]) == 0) { + *start = s; + *end = e; + return true; + } + } + return false; +} + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc new file mode 100644 index 00000000..6032f520 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -0,0 +1,94 @@ +//===-- sanitizer_posix_libcdep.cc ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements libc-dependent POSIX-specific functions +// from sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#if SANITIZER_LINUX || SANITIZER_MAC +#include "sanitizer_common.h" +#include "sanitizer_stacktrace.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace __sanitizer { + +u32 GetUid() { + return getuid(); +} + +uptr GetThreadSelf() { + return (uptr)pthread_self(); +} + +void FlushUnneededShadowMemory(uptr addr, uptr size) { + madvise((void*)addr, size, MADV_DONTNEED); +} + +void DisableCoreDumper() { + struct rlimit nocore; + nocore.rlim_cur = 0; + nocore.rlim_max = 0; + setrlimit(RLIMIT_CORE, &nocore); +} + +bool StackSizeIsUnlimited() { + struct rlimit rlim; + CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim)); + return (rlim.rlim_cur == (uptr)-1); +} + +void SetStackSizeLimitInBytes(uptr limit) { + struct rlimit rlim; + rlim.rlim_cur = limit; + rlim.rlim_max = limit; + if (setrlimit(RLIMIT_STACK, &rlim)) { + Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); + Die(); + } + CHECK(!StackSizeIsUnlimited()); +} + +void SleepForSeconds(int seconds) { + sleep(seconds); +} + +void SleepForMillis(int millis) { + usleep(millis * 1000); +} + +void Abort() { + abort(); +} + +int Atexit(void (*function)(void)) { +#ifndef SANITIZER_GO + return atexit(function); +#else + return 0; +#endif +} + +int internal_isatty(fd_t fd) { + return isatty(fd); +} + +} // namespace __sanitizer + +#endif diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc new file mode 100644 index 00000000..bbdb24b6 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc @@ -0,0 +1,306 @@ +//===-- sanitizer_printf.cc -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer. +// +// Internal printf function, used inside run-time libraries. +// We can't use libc printf because we intercept some of the functions used +// inside it. +//===----------------------------------------------------------------------===// + + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +#include +#include + +#if SANITIZER_WINDOWS && !defined(va_copy) +# define va_copy(dst, src) ((dst) = (src)) +#endif + +namespace __sanitizer { + +StaticSpinMutex CommonSanitizerReportMutex; + +static int AppendChar(char **buff, const char *buff_end, char c) { + if (*buff < buff_end) { + **buff = c; + (*buff)++; + } + return 1; +} + +// Appends number in a given base to buffer. If its length is less than +// |minimal_num_length|, it is padded with leading zeroes or spaces, depending +// on the value of |pad_with_zero|. +static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value, + u8 base, u8 minimal_num_length, bool pad_with_zero, + bool negative) { + uptr const kMaxLen = 30; + RAW_CHECK(base == 10 || base == 16); + RAW_CHECK(base == 10 || !negative); + RAW_CHECK(absolute_value || !negative); + RAW_CHECK(minimal_num_length < kMaxLen); + int result = 0; + if (negative && minimal_num_length) + --minimal_num_length; + if (negative && pad_with_zero) + result += AppendChar(buff, buff_end, '-'); + uptr num_buffer[kMaxLen]; + int pos = 0; + do { + RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow"); + num_buffer[pos++] = absolute_value % base; + absolute_value /= base; + } while (absolute_value > 0); + if (pos < minimal_num_length) { + // Make sure compiler doesn't insert call to memset here. + internal_memset(&num_buffer[pos], 0, + sizeof(num_buffer[0]) * (minimal_num_length - pos)); + pos = minimal_num_length; + } + RAW_CHECK(pos > 0); + pos--; + for (; pos >= 0 && num_buffer[pos] == 0; pos--) { + char c = (pad_with_zero || pos == 0) ? '0' : ' '; + result += AppendChar(buff, buff_end, c); + } + if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-'); + for (; pos >= 0; pos--) { + char digit = static_cast(num_buffer[pos]); + result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit + : 'a' + digit - 10); + } + return result; +} + +static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base, + u8 minimal_num_length, bool pad_with_zero) { + return AppendNumber(buff, buff_end, num, base, minimal_num_length, + pad_with_zero, false /* negative */); +} + +static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num, + u8 minimal_num_length, bool pad_with_zero) { + bool negative = (num < 0); + return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10, + minimal_num_length, pad_with_zero, negative); +} + +static int AppendString(char **buff, const char *buff_end, const char *s) { + if (s == 0) + s = ""; + int result = 0; + for (; *s; s++) { + result += AppendChar(buff, buff_end, *s); + } + return result; +} + +static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) { + int result = 0; + result += AppendString(buff, buff_end, "0x"); + result += AppendUnsigned(buff, buff_end, ptr_value, 16, + (SANITIZER_WORDSIZE == 64) ? 12 : 8, true); + return result; +} + +int VSNPrintf(char *buff, int buff_length, + const char *format, va_list args) { + static const char *kPrintfFormatsHelp = + "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %s; %c\n"; + RAW_CHECK(format); + RAW_CHECK(buff_length > 0); + const char *buff_end = &buff[buff_length - 1]; + const char *cur = format; + int result = 0; + for (; *cur; cur++) { + if (*cur != '%') { + result += AppendChar(&buff, buff_end, *cur); + continue; + } + cur++; + bool have_width = (*cur >= '0' && *cur <= '9'); + bool pad_with_zero = (*cur == '0'); + int width = 0; + if (have_width) { + while (*cur >= '0' && *cur <= '9') { + width = width * 10 + *cur++ - '0'; + } + } + bool have_z = (*cur == 'z'); + cur += have_z; + bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l'); + cur += have_ll * 2; + s64 dval; + u64 uval; + bool have_flags = have_width | have_z | have_ll; + switch (*cur) { + case 'd': { + dval = have_ll ? va_arg(args, s64) + : have_z ? va_arg(args, sptr) + : va_arg(args, int); + result += AppendSignedDecimal(&buff, buff_end, dval, width, + pad_with_zero); + break; + } + case 'u': + case 'x': { + uval = have_ll ? va_arg(args, u64) + : have_z ? va_arg(args, uptr) + : va_arg(args, unsigned); + result += AppendUnsigned(&buff, buff_end, uval, + (*cur == 'u') ? 10 : 16, width, pad_with_zero); + break; + } + case 'p': { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendPointer(&buff, buff_end, va_arg(args, uptr)); + break; + } + case 's': { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendString(&buff, buff_end, va_arg(args, char*)); + break; + } + case 'c': { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendChar(&buff, buff_end, va_arg(args, int)); + break; + } + case '%' : { + RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp); + result += AppendChar(&buff, buff_end, '%'); + break; + } + default: { + RAW_CHECK_MSG(false, kPrintfFormatsHelp); + } + } + } + RAW_CHECK(buff <= buff_end); + AppendChar(&buff, buff_end + 1, '\0'); + return result; +} + +static void (*PrintfAndReportCallback)(const char *); +void SetPrintfAndReportCallback(void (*callback)(const char *)) { + PrintfAndReportCallback = callback; +} + +// Can be overriden in frontend. +#if SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void OnPrint(const char *str) { + (void)str; +} +#elif defined(SANITIZER_GO) && defined(TSAN_EXTERNAL_HOOKS) +void OnPrint(const char *str); +#else +void OnPrint(const char *str) { + (void)str; +} +#endif + +static void CallPrintfAndReportCallback(const char *str) { + OnPrint(str); + if (PrintfAndReportCallback) + PrintfAndReportCallback(str); +} + +static void SharedPrintfCode(bool append_pid, const char *format, + va_list args) { + va_list args2; + va_copy(args2, args); + const int kLen = 16 * 1024; + // |local_buffer| is small enough not to overflow the stack and/or violate + // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other + // hand, the bigger the buffer is, the more the chance the error report will + // fit into it. + char local_buffer[400]; + int needed_length; + char *buffer = local_buffer; + int buffer_size = ARRAY_SIZE(local_buffer); + // First try to print a message using a local buffer, and then fall back to + // mmaped buffer. + for (int use_mmap = 0; use_mmap < 2; use_mmap++) { + if (use_mmap) { + va_end(args); + va_copy(args, args2); + buffer = (char*)MmapOrDie(kLen, "Report"); + buffer_size = kLen; + } + needed_length = 0; + if (append_pid) { + int pid = internal_getpid(); + needed_length += internal_snprintf(buffer, buffer_size, "==%d==", pid); + if (needed_length >= buffer_size) { + // The pid doesn't fit into the current buffer. + if (!use_mmap) + continue; + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); + } + } + needed_length += VSNPrintf(buffer + needed_length, + buffer_size - needed_length, format, args); + if (needed_length >= buffer_size) { + // The message doesn't fit into the current buffer. + if (!use_mmap) + continue; + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); + } + // If the message fit into the buffer, print it and exit. + break; + } + RawWrite(buffer); + CallPrintfAndReportCallback(buffer); + // If we had mapped any memory, clean up. + if (buffer != local_buffer) + UnmapOrDie((void *)buffer, buffer_size); + va_end(args2); +} + +void Printf(const char *format, ...) { + va_list args; + va_start(args, format); + SharedPrintfCode(false, format, args); + va_end(args); +} + +// Like Printf, but prints the current PID before the output string. +void Report(const char *format, ...) { + va_list args; + va_start(args, format); + SharedPrintfCode(true, format, args); + va_end(args); +} + +// Writes at most "length" symbols to "buffer" (including trailing '\0'). +// Returns the number of symbols that should have been written to buffer +// (not including trailing '\0'). Thus, the string is truncated +// iff return value is not less than "length". +int internal_snprintf(char *buffer, uptr length, const char *format, ...) { + va_list args; + va_start(args, format); + int needed_length = VSNPrintf(buffer, length, format, args); + va_end(args); + return needed_length; +} + +void InternalScopedString::append(const char *format, ...) { + CHECK_LT(length_, size()); + va_list args; + va_start(args, format); + VSNPrintf(data() + length_, size() - length_, format, args); + va_end(args); + length_ += internal_strlen(data() + length_); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_procmaps.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_procmaps.h new file mode 100644 index 00000000..65bcac63 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_procmaps.h @@ -0,0 +1,136 @@ +//===-- sanitizer_procmaps.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer. +// +// Information about the process mappings. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_PROCMAPS_H +#define SANITIZER_PROCMAPS_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +#if SANITIZER_WINDOWS +class MemoryMappingLayout { + public: + explicit MemoryMappingLayout(bool cache_enabled) { + (void)cache_enabled; + } + bool GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], uptr filename_size, + uptr *protection) { + UNIMPLEMENTED(); + } +}; + +#else // SANITIZER_WINDOWS +#if SANITIZER_LINUX +struct ProcSelfMapsBuff { + char *data; + uptr mmaped_size; + uptr len; +}; +#endif // SANITIZER_LINUX + +class MemoryMappingLayout { + public: + explicit MemoryMappingLayout(bool cache_enabled); + bool Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size, uptr *protection); + void Reset(); + // Gets the object file name and the offset in that object for a given + // address 'addr'. Returns true on success. + bool GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], uptr filename_size, + uptr *protection); + // In some cases, e.g. when running under a sandbox on Linux, ASan is unable + // to obtain the memory mappings. It should fall back to pre-cached data + // instead of aborting. + static void CacheMemoryMappings(); + ~MemoryMappingLayout(); + + // Memory protection masks. + static const uptr kProtectionRead = 1; + static const uptr kProtectionWrite = 2; + static const uptr kProtectionExecute = 4; + static const uptr kProtectionShared = 8; + + private: + void LoadFromCache(); + // Default implementation of GetObjectNameAndOffset. + // Quite slow, because it iterates through the whole process map for each + // lookup. + bool IterateForObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], uptr filename_size, + uptr *protection) { + Reset(); + uptr start, end, file_offset; + for (int i = 0; Next(&start, &end, &file_offset, filename, filename_size, + protection); + i++) { + if (addr >= start && addr < end) { + // Don't subtract 'start' for the first entry: + // * If a binary is compiled w/o -pie, then the first entry in + // process maps is likely the binary itself (all dynamic libs + // are mapped higher in address space). For such a binary, + // instruction offset in binary coincides with the actual + // instruction address in virtual memory (as code section + // is mapped to a fixed memory range). + // * If a binary is compiled with -pie, all the modules are + // mapped high at address space (in particular, higher than + // shadow memory of the tool), so the module can't be the + // first entry. + *offset = (addr - (i ? start : 0)) + file_offset; + return true; + } + } + if (filename_size) + filename[0] = '\0'; + return false; + } + +# if SANITIZER_LINUX + ProcSelfMapsBuff proc_self_maps_; + char *current_; + + // Static mappings cache. + static ProcSelfMapsBuff cached_proc_self_maps_; + static StaticSpinMutex cache_lock_; // protects cached_proc_self_maps_. +# elif SANITIZER_MAC + template + bool NextSegmentLoad(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size, + uptr *protection); + int current_image_; + u32 current_magic_; + u32 current_filetype_; + int current_load_cmd_count_; + char *current_load_cmd_addr_; +# endif +}; + +typedef void (*fill_profile_f)(uptr start, uptr rss, bool file, + /*out*/uptr *stats, uptr stats_size); + +// Parse the contents of /proc/self/smaps and generate a memory profile. +// |cb| is a tool-specific callback that fills the |stats| array containing +// |stats_size| elements. +void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size); + +// Returns code range for the specified module. +bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end); + +#endif // SANITIZER_WINDOWS + +} // namespace __sanitizer + +#endif // SANITIZER_PROCMAPS_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h new file mode 100644 index 00000000..db4eb745 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h @@ -0,0 +1,179 @@ +//===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Memory quarantine for AddressSanitizer and potentially other tools. +// Quarantine caches some specified amount of memory in per-thread caches, +// then evicts to global FIFO queue. When the queue reaches specified threshold, +// oldest memory is recycled. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_QUARANTINE_H +#define SANITIZER_QUARANTINE_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_mutex.h" +#include "sanitizer_list.h" + +namespace __sanitizer { + +template class QuarantineCache; + +struct QuarantineBatch { + static const uptr kSize = 1021; + QuarantineBatch *next; + uptr size; + uptr count; + void *batch[kSize]; +}; + +COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13)); // 8Kb. + +// The callback interface is: +// void Callback::Recycle(Node *ptr); +// void *cb.Allocate(uptr size); +// void cb.Deallocate(void *ptr); +template +class Quarantine { + public: + typedef QuarantineCache Cache; + + explicit Quarantine(LinkerInitialized) + : cache_(LINKER_INITIALIZED) { + } + + void Init(uptr size, uptr cache_size) { + max_size_ = size; + min_size_ = size / 10 * 9; // 90% of max size. + max_cache_size_ = cache_size; + } + + void Put(Cache *c, Callback cb, Node *ptr, uptr size) { + c->Enqueue(cb, ptr, size); + if (c->Size() > max_cache_size_) + Drain(c, cb); + } + + void NOINLINE Drain(Cache *c, Callback cb) { + { + SpinMutexLock l(&cache_mutex_); + cache_.Transfer(c); + } + if (cache_.Size() > max_size_ && recycle_mutex_.TryLock()) + Recycle(cb); + } + + private: + // Read-only data. + char pad0_[kCacheLineSize]; + uptr max_size_; + uptr min_size_; + uptr max_cache_size_; + char pad1_[kCacheLineSize]; + SpinMutex cache_mutex_; + SpinMutex recycle_mutex_; + Cache cache_; + char pad2_[kCacheLineSize]; + + void NOINLINE Recycle(Callback cb) { + Cache tmp; + { + SpinMutexLock l(&cache_mutex_); + while (cache_.Size() > min_size_) { + QuarantineBatch *b = cache_.DequeueBatch(); + tmp.EnqueueBatch(b); + } + } + recycle_mutex_.Unlock(); + DoRecycle(&tmp, cb); + } + + void NOINLINE DoRecycle(Cache *c, Callback cb) { + while (QuarantineBatch *b = c->DequeueBatch()) { + const uptr kPrefetch = 16; + for (uptr i = 0; i < kPrefetch; i++) + PREFETCH(b->batch[i]); + for (uptr i = 0; i < b->count; i++) { + PREFETCH(b->batch[i + kPrefetch]); + cb.Recycle((Node*)b->batch[i]); + } + cb.Deallocate(b); + } + } +}; + +// Per-thread cache of memory blocks. +template +class QuarantineCache { + public: + explicit QuarantineCache(LinkerInitialized) { + } + + QuarantineCache() + : size_() { + list_.clear(); + } + + uptr Size() const { + return atomic_load(&size_, memory_order_relaxed); + } + + void Enqueue(Callback cb, void *ptr, uptr size) { + if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) { + AllocBatch(cb); + size += sizeof(QuarantineBatch); // Count the batch in Quarantine size. + } + QuarantineBatch *b = list_.back(); + b->batch[b->count++] = ptr; + b->size += size; + SizeAdd(size); + } + + void Transfer(QuarantineCache *c) { + list_.append_back(&c->list_); + SizeAdd(c->Size()); + atomic_store(&c->size_, 0, memory_order_relaxed); + } + + void EnqueueBatch(QuarantineBatch *b) { + list_.push_back(b); + SizeAdd(b->size); + } + + QuarantineBatch *DequeueBatch() { + if (list_.empty()) + return 0; + QuarantineBatch *b = list_.front(); + list_.pop_front(); + SizeSub(b->size); + return b; + } + + private: + IntrusiveList list_; + atomic_uintptr_t size_; + + void SizeAdd(uptr add) { + atomic_store(&size_, Size() + add, memory_order_relaxed); + } + void SizeSub(uptr sub) { + atomic_store(&size_, Size() - sub, memory_order_relaxed); + } + + NOINLINE QuarantineBatch* AllocBatch(Callback cb) { + QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b)); + b->count = 0; + b->size = 0; + list_.push_back(b); + return b; + } +}; +} // namespace __sanitizer + +#endif // #ifndef SANITIZER_QUARANTINE_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_report_decorator.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_report_decorator.h new file mode 100644 index 00000000..eef2b15c --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_report_decorator.h @@ -0,0 +1,41 @@ +//===-- sanitizer_report_decorator.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tags to decorate the sanitizer reports. +// Currently supported tags: +// * None. +// * ANSI color sequences. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_REPORT_DECORATOR_H +#define SANITIZER_REPORT_DECORATOR_H + +namespace __sanitizer { +class AnsiColorDecorator { + // FIXME: This is not portable. It assumes the special strings are printed to + // stdout, which is not the case on Windows (see SetConsoleTextAttribute()). + public: + explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { } + const char *Bold() const { return ansi_ ? "\033[1m" : ""; } + const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; } + const char *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; } + const char *Green() const { return ansi_ ? "\033[1m\033[32m" : ""; } + const char *Yellow() const { return ansi_ ? "\033[1m\033[33m" : ""; } + const char *Blue() const { return ansi_ ? "\033[1m\033[34m" : ""; } + const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; } + const char *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; } + const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; } + const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; } + private: + bool ansi_; +}; +} // namespace __sanitizer + +#endif // SANITIZER_REPORT_DECORATOR_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc new file mode 100644 index 00000000..2793bd07 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc @@ -0,0 +1,238 @@ +//===-- sanitizer_stackdepot.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_stackdepot.h" +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_mutex.h" +#include "sanitizer_atomic.h" + +namespace __sanitizer { + +const int kTabSize = 1024 * 1024; // Hash table size. +const int kPartBits = 8; +const int kPartShift = sizeof(u32) * 8 - kPartBits - 1; +const int kPartCount = 1 << kPartBits; // Number of subparts in the table. +const int kPartSize = kTabSize / kPartCount; +const int kMaxId = 1 << kPartShift; + +struct StackDesc { + StackDesc *link; + u32 id; + u32 hash; + uptr size; + uptr stack[1]; // [size] +}; + +static struct { + StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator. + atomic_uintptr_t region_pos; // Region allocator for StackDesc's. + atomic_uintptr_t region_end; + atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's. + atomic_uint32_t seq[kPartCount]; // Unique id generators. +} depot; + +static StackDepotStats stats; + +StackDepotStats *StackDepotGetStats() { + return &stats; +} + +static u32 hash(const uptr *stack, uptr size) { + // murmur2 + const u32 m = 0x5bd1e995; + const u32 seed = 0x9747b28c; + const u32 r = 24; + u32 h = seed ^ (size * sizeof(uptr)); + for (uptr i = 0; i < size; i++) { + u32 k = stack[i]; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + } + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; +} + +static StackDesc *tryallocDesc(uptr memsz) { + // Optimisic lock-free allocation, essentially try to bump the region ptr. + for (;;) { + uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire); + uptr end = atomic_load(&depot.region_end, memory_order_acquire); + if (cmp == 0 || cmp + memsz > end) + return 0; + if (atomic_compare_exchange_weak( + &depot.region_pos, &cmp, cmp + memsz, + memory_order_acquire)) + return (StackDesc*)cmp; + } +} + +static StackDesc *allocDesc(uptr size) { + // First, try to allocate optimisitically. + uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr); + StackDesc *s = tryallocDesc(memsz); + if (s) + return s; + // If failed, lock, retry and alloc new superblock. + SpinMutexLock l(&depot.mtx); + for (;;) { + s = tryallocDesc(memsz); + if (s) + return s; + atomic_store(&depot.region_pos, 0, memory_order_relaxed); + uptr allocsz = 64 * 1024; + if (allocsz < memsz) + allocsz = memsz; + uptr mem = (uptr)MmapOrDie(allocsz, "stack depot"); + stats.mapped += allocsz; + atomic_store(&depot.region_end, mem + allocsz, memory_order_release); + atomic_store(&depot.region_pos, mem, memory_order_release); + } +} + +static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) { + // Searches linked list s for the stack, returns its id. + for (; s; s = s->link) { + if (s->hash == hash && s->size == size) { + uptr i = 0; + for (; i < size; i++) { + if (stack[i] != s->stack[i]) + break; + } + if (i == size) + return s->id; + } + } + return 0; +} + +static StackDesc *lock(atomic_uintptr_t *p) { + // Uses the pointer lsb as mutex. + for (int i = 0;; i++) { + uptr cmp = atomic_load(p, memory_order_relaxed); + if ((cmp & 1) == 0 + && atomic_compare_exchange_weak(p, &cmp, cmp | 1, + memory_order_acquire)) + return (StackDesc*)cmp; + if (i < 10) + proc_yield(10); + else + internal_sched_yield(); + } +} + +static void unlock(atomic_uintptr_t *p, StackDesc *s) { + DCHECK_EQ((uptr)s & 1, 0); + atomic_store(p, (uptr)s, memory_order_release); +} + +u32 StackDepotPut(const uptr *stack, uptr size) { + if (stack == 0 || size == 0) + return 0; + uptr h = hash(stack, size); + atomic_uintptr_t *p = &depot.tab[h % kTabSize]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + // First, try to find the existing stack. + u32 id = find(s, stack, size, h); + if (id) + return id; + // If failed, lock, retry and insert new. + StackDesc *s2 = lock(p); + if (s2 != s) { + id = find(s2, stack, size, h); + if (id) { + unlock(p, s2); + return id; + } + } + uptr part = (h % kTabSize) / kPartSize; + id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1; + stats.n_uniq_ids++; + CHECK_LT(id, kMaxId); + id |= part << kPartShift; + CHECK_NE(id, 0); + CHECK_EQ(id & (1u << 31), 0); + s = allocDesc(size); + s->id = id; + s->hash = h; + s->size = size; + internal_memcpy(s->stack, stack, size * sizeof(uptr)); + s->link = s2; + unlock(p, s); + return id; +} + +const uptr *StackDepotGet(u32 id, uptr *size) { + if (id == 0) + return 0; + CHECK_EQ(id & (1u << 31), 0); + // High kPartBits contain part id, so we need to scan at most kPartSize lists. + uptr part = id >> kPartShift; + for (int i = 0; i != kPartSize; i++) { + uptr idx = part * kPartSize + i; + CHECK_LT(idx, kTabSize); + atomic_uintptr_t *p = &depot.tab[idx]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + for (; s; s = s->link) { + if (s->id == id) { + *size = s->size; + return s->stack; + } + } + } + *size = 0; + return 0; +} + +bool StackDepotReverseMap::IdDescPair::IdComparator( + const StackDepotReverseMap::IdDescPair &a, + const StackDepotReverseMap::IdDescPair &b) { + return a.id < b.id; +} + +StackDepotReverseMap::StackDepotReverseMap() + : map_(StackDepotGetStats()->n_uniq_ids + 100) { + for (int idx = 0; idx < kTabSize; idx++) { + atomic_uintptr_t *p = &depot.tab[idx]; + uptr v = atomic_load(p, memory_order_consume); + StackDesc *s = (StackDesc*)(v & ~1); + for (; s; s = s->link) { + IdDescPair pair = {s->id, s}; + map_.push_back(pair); + } + } + InternalSort(&map_, map_.size(), IdDescPair::IdComparator); +} + +const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) { + if (!map_.size()) return 0; + IdDescPair pair = {id, 0}; + uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair, + IdDescPair::IdComparator); + if (idx > map_.size()) { + *size = 0; + return 0; + } + StackDesc *desc = map_[idx].desc; + *size = desc->size; + return desc->stack; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h new file mode 100644 index 00000000..4f77ff28 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h @@ -0,0 +1,62 @@ +//===-- sanitizer_stackdepot.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_STACKDEPOT_H +#define SANITIZER_STACKDEPOT_H + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// StackDepot efficiently stores huge amounts of stack traces. + +// Maps stack trace to an unique id. +u32 StackDepotPut(const uptr *stack, uptr size); +// Retrieves a stored stack trace by the id. +const uptr *StackDepotGet(u32 id, uptr *size); + +struct StackDepotStats { + uptr n_uniq_ids; + uptr mapped; +}; + +StackDepotStats *StackDepotGetStats(); + +struct StackDesc; + +// Instantiating this class creates a snapshot of StackDepot which can be +// efficiently queried with StackDepotGet(). You can use it concurrently with +// StackDepot, but the snapshot is only guaranteed to contain those stack traces +// which were stored before it was instantiated. +class StackDepotReverseMap { + public: + StackDepotReverseMap(); + const uptr *Get(u32 id, uptr *size); + + private: + struct IdDescPair { + u32 id; + StackDesc *desc; + + static bool IdComparator(const IdDescPair &a, const IdDescPair &b); + }; + + InternalMmapVector map_; + + // Disallow evil constructors. + StackDepotReverseMap(const StackDepotReverseMap&); + void operator=(const StackDepotReverseMap&); +}; +} // namespace __sanitizer + +#endif // SANITIZER_STACKDEPOT_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc new file mode 100644 index 00000000..70ce26bd --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc @@ -0,0 +1,172 @@ +//===-- sanitizer_stacktrace.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +uptr StackTrace::GetPreviousInstructionPc(uptr pc) { +#ifdef __arm__ + // Cancel Thumb bit. + pc = pc & (~1); +#endif +#if defined(__powerpc__) || defined(__powerpc64__) + // PCs are always 4 byte aligned. + return pc - 4; +#elif defined(__sparc__) + return pc - 8; +#else + return pc - 1; +#endif +} + +static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num, + uptr pc) { + buffer->append(" #%zu 0x%zx", frame_num, pc); +} + +void StackTrace::PrintStack(const uptr *addr, uptr size, + SymbolizeCallback symbolize_callback) { + if (addr == 0) { + Printf("\n\n"); + return; + } + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + InternalScopedBuffer buff(GetPageSizeCached() * 2); + InternalScopedBuffer addr_frames(64); + InternalScopedString frame_desc(GetPageSizeCached() * 2); + uptr frame_num = 0; + for (uptr i = 0; i < size && addr[i]; i++) { + // PCs in stack traces are actually the return addresses, that is, + // addresses of the next instructions after the call. + uptr pc = GetPreviousInstructionPc(addr[i]); + uptr addr_frames_num = 0; // The number of stack frames for current + // instruction address. + if (symbolize_callback) { + if (symbolize_callback((void*)pc, buff.data(), buff.size())) { + addr_frames_num = 1; + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); + // We can't know anything about the string returned by external + // symbolizer, but if it starts with filename, try to strip path prefix + // from it. + frame_desc.append( + " %s", + StripPathPrefix(buff.data(), common_flags()->strip_path_prefix)); + Printf("%s\n", frame_desc.data()); + frame_num++; + } + } + if (common_flags()->symbolize && addr_frames_num == 0) { + // Use our own (online) symbolizer, if necessary. + if (Symbolizer *sym = Symbolizer::GetOrNull()) + addr_frames_num = + sym->SymbolizeCode(pc, addr_frames.data(), addr_frames.size()); + for (uptr j = 0; j < addr_frames_num; j++) { + AddressInfo &info = addr_frames[j]; + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); + if (info.function) { + frame_desc.append(" in %s", info.function); + } + if (info.file) { + frame_desc.append(" "); + PrintSourceLocation(&frame_desc, info.file, info.line, info.column); + } else if (info.module) { + frame_desc.append(" "); + PrintModuleAndOffset(&frame_desc, info.module, info.module_offset); + } + Printf("%s\n", frame_desc.data()); + frame_num++; + info.Clear(); + } + } + if (addr_frames_num == 0) { + // If online symbolization failed, try to output at least module and + // offset for instruction. + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); + uptr offset; + if (proc_maps.GetObjectNameAndOffset(pc, &offset, + buff.data(), buff.size(), + /* protection */0)) { + frame_desc.append(" "); + PrintModuleAndOffset(&frame_desc, buff.data(), offset); + } + Printf("%s\n", frame_desc.data()); + frame_num++; + } + } + // Always print a trailing empty line after stack trace. + Printf("\n"); +} + +uptr StackTrace::GetCurrentPc() { + return GET_CALLER_PC(); +} + +void StackTrace::FastUnwindStack(uptr pc, uptr bp, + uptr stack_top, uptr stack_bottom, + uptr max_depth) { + if (max_depth == 0) { + size = 0; + return; + } + trace[0] = pc; + size = 1; + uhwptr *frame = (uhwptr *)bp; + uhwptr *prev_frame = frame - 1; + if (stack_top < 4096) return; // Sanity check for stack top. + // Avoid infinite loop when frame == frame[0] by using frame > prev_frame. + while (frame > prev_frame && + frame < (uhwptr *)stack_top - 2 && + frame > (uhwptr *)stack_bottom && + IsAligned((uptr)frame, sizeof(*frame)) && + size < max_depth) { + uhwptr pc1 = frame[1]; + if (pc1 != pc) { + trace[size++] = (uptr) pc1; + } + prev_frame = frame; + frame = (uhwptr *)frame[0]; + } +} + +void StackTrace::PopStackFrames(uptr count) { + CHECK(size >= count); + size -= count; + for (uptr i = 0; i < size; i++) { + trace[i] = trace[i + count]; + } +} + +static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) { + return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold; +} + +uptr StackTrace::LocatePcInTrace(uptr pc) { + // Use threshold to find PC in stack trace, as PC we want to unwind from may + // slightly differ from return address in the actual unwinded stack trace. + const int kPcThreshold = 96; + for (uptr i = 0; i < size; ++i) { + if (MatchPc(pc, trace[i], kPcThreshold)) + return i; + } + return 0; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h new file mode 100644 index 00000000..5042f230 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h @@ -0,0 +1,94 @@ +//===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_STACKTRACE_H +#define SANITIZER_STACKTRACE_H + +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +static const uptr kStackTraceMax = 256; + +#if SANITIZER_LINUX && (defined(__arm__) || \ + defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__sparc__) || \ + defined(__mips__)) +# define SANITIZER_CAN_FAST_UNWIND 0 +#elif SANITIZER_WINDOWS +# define SANITIZER_CAN_FAST_UNWIND 0 +#else +# define SANITIZER_CAN_FAST_UNWIND 1 +#endif + +struct StackTrace { + typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, + int out_size); + uptr top_frame_bp; + uptr size; + uptr trace[kStackTraceMax]; + + // Prints a symbolized stacktrace, followed by an empty line. + static void PrintStack(const uptr *addr, uptr size, + SymbolizeCallback symbolize_callback = 0); + + void CopyFrom(const uptr *src, uptr src_size) { + top_frame_bp = 0; + size = src_size; + if (size > kStackTraceMax) size = kStackTraceMax; + for (uptr i = 0; i < size; i++) + trace[i] = src[i]; + } + + static bool WillUseFastUnwind(bool request_fast_unwind) { + // Check if fast unwind is available. Fast unwind is the only option on Mac. + if (!SANITIZER_CAN_FAST_UNWIND) + return false; + else if (SANITIZER_MAC) + return true; + return request_fast_unwind; + } + + void Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top, + uptr stack_bottom, bool request_fast_unwind); + + static uptr GetCurrentPc(); + static uptr GetPreviousInstructionPc(uptr pc); + + private: + void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom, + uptr max_depth); + void SlowUnwindStack(uptr pc, uptr max_depth); + void PopStackFrames(uptr count); + uptr LocatePcInTrace(uptr pc); +}; + +} // namespace __sanitizer + +// Use this macro if you want to print stack trace with the caller +// of the current function in the top frame. +#define GET_CALLER_PC_BP_SP \ + uptr bp = GET_CURRENT_FRAME(); \ + uptr pc = GET_CALLER_PC(); \ + uptr local_stack; \ + uptr sp = (uptr)&local_stack + +// Use this macro if you want to print stack trace with the current +// function in the top frame. +#define GET_CURRENT_PC_BP_SP \ + uptr bp = GET_CURRENT_FRAME(); \ + uptr pc = StackTrace::GetCurrentPc(); \ + uptr local_stack; \ + uptr sp = (uptr)&local_stack + + +#endif // SANITIZER_STACKTRACE_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc new file mode 100644 index 00000000..58fa1825 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -0,0 +1,28 @@ +//===-- sanitizer_stacktrace_libcdep.cc -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_stacktrace.h" + +namespace __sanitizer { + +void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top, + uptr stack_bottom, bool request_fast_unwind) { + if (!WillUseFastUnwind(request_fast_unwind)) + SlowUnwindStack(pc, max_depth); + else + FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth); + + top_frame_bp = size ? bp : 0; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld.h new file mode 100644 index 00000000..a3264670 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld.h @@ -0,0 +1,68 @@ +//===-- sanitizer_stoptheworld.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the StopTheWorld function which suspends the execution of the current +// process and runs the user-supplied callback in the same address space. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_STOPTHEWORLD_H +#define SANITIZER_STOPTHEWORLD_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_common.h" + +namespace __sanitizer { +typedef int SuspendedThreadID; + +// Holds the list of suspended threads and provides an interface to dump their +// register contexts. +class SuspendedThreadsList { + public: + SuspendedThreadsList() + : thread_ids_(1024) {} + SuspendedThreadID GetThreadID(uptr index) const { + CHECK_LT(index, thread_ids_.size()); + return thread_ids_[index]; + } + int GetRegistersAndSP(uptr index, uptr *buffer, uptr *sp) const; + // The buffer in GetRegistersAndSP should be at least this big. + static uptr RegisterCount(); + uptr thread_count() const { return thread_ids_.size(); } + bool Contains(SuspendedThreadID thread_id) const { + for (uptr i = 0; i < thread_ids_.size(); i++) { + if (thread_ids_[i] == thread_id) + return true; + } + return false; + } + void Append(SuspendedThreadID thread_id) { + thread_ids_.push_back(thread_id); + } + + private: + InternalMmapVector thread_ids_; + + // Prohibit copy and assign. + SuspendedThreadsList(const SuspendedThreadsList&); + void operator=(const SuspendedThreadsList&); +}; + +typedef void (*StopTheWorldCallback)( + const SuspendedThreadsList &suspended_threads_list, + void *argument); + +// Suspend all threads in the current process and run the callback on the list +// of suspended threads. This function will resume the threads before returning. +// The callback should not call any libc functions. +// This function should NOT be called from multiple threads simultaneously. +void StopTheWorld(StopTheWorldCallback callback, void *argument); + +} // namespace __sanitizer + +#endif // SANITIZER_STOPTHEWORLD_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc new file mode 100644 index 00000000..a34ddd87 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -0,0 +1,455 @@ +//===-- sanitizer_stoptheworld_linux_libcdep.cc ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// See sanitizer_stoptheworld.h for details. +// This implementation was inspired by Markus Gutschke's linuxthreads.cc. +// +//===----------------------------------------------------------------------===// + + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX && defined(__x86_64__) + +#include "sanitizer_stoptheworld.h" + +#include "sanitizer_platform_limits_posix.h" + +#include +#include // for CLONE_* definitions +#include +#include // for PR_* definitions +#include // for PTRACE_* definitions +#include // for pid_t +#if SANITIZER_ANDROID && defined(__arm__) +# include // for pt_regs +#else +# include // for user_regs_struct +#endif +#include // for signal-related stuff + +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + +#include "sanitizer_common.h" +#include "sanitizer_flags.h" +#include "sanitizer_libc.h" +#include "sanitizer_linux.h" +#include "sanitizer_mutex.h" +#include "sanitizer_placement_new.h" + +// This module works by spawning a Linux task which then attaches to every +// thread in the caller process with ptrace. This suspends the threads, and +// PTRACE_GETREGS can then be used to obtain their register state. The callback +// supplied to StopTheWorld() is run in the tracer task while the threads are +// suspended. +// The tracer task must be placed in a different thread group for ptrace to +// work, so it cannot be spawned as a pthread. Instead, we use the low-level +// clone() interface (we want to share the address space with the caller +// process, so we prefer clone() over fork()). +// +// We don't use any libc functions, relying instead on direct syscalls. There +// are two reasons for this: +// 1. calling a library function while threads are suspended could cause a +// deadlock, if one of the treads happens to be holding a libc lock; +// 2. it's generally not safe to call libc functions from the tracer task, +// because clone() does not set up a thread-local storage for it. Any +// thread-local variables used by libc will be shared between the tracer task +// and the thread which spawned it. + +COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); + +namespace __sanitizer { +// This class handles thread suspending/unsuspending in the tracer thread. +class ThreadSuspender { + public: + explicit ThreadSuspender(pid_t pid) + : pid_(pid) { + CHECK_GE(pid, 0); + } + bool SuspendAllThreads(); + void ResumeAllThreads(); + void KillAllThreads(); + SuspendedThreadsList &suspended_threads_list() { + return suspended_threads_list_; + } + private: + SuspendedThreadsList suspended_threads_list_; + pid_t pid_; + bool SuspendThread(SuspendedThreadID thread_id); +}; + +bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { + // Are we already attached to this thread? + // Currently this check takes linear time, however the number of threads is + // usually small. + if (suspended_threads_list_.Contains(thread_id)) + return false; + int pterrno; + if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL), + &pterrno)) { + // Either the thread is dead, or something prevented us from attaching. + // Log this event and move on. + if (common_flags()->verbosity) + Report("Could not attach to thread %d (errno %d).\n", thread_id, pterrno); + return false; + } else { + if (common_flags()->verbosity) + Report("Attached to thread %d.\n", thread_id); + // The thread is not guaranteed to stop before ptrace returns, so we must + // wait on it. + uptr waitpid_status; + HANDLE_EINTR(waitpid_status, internal_waitpid(thread_id, NULL, __WALL)); + int wperrno; + if (internal_iserror(waitpid_status, &wperrno)) { + // Got a ECHILD error. I don't think this situation is possible, but it + // doesn't hurt to report it. + if (common_flags()->verbosity) + Report("Waiting on thread %d failed, detaching (errno %d).\n", + thread_id, wperrno); + internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL); + return false; + } + suspended_threads_list_.Append(thread_id); + return true; + } +} + +void ThreadSuspender::ResumeAllThreads() { + for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) { + pid_t tid = suspended_threads_list_.GetThreadID(i); + int pterrno; + if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL), + &pterrno)) { + if (common_flags()->verbosity) + Report("Detached from thread %d.\n", tid); + } else { + // Either the thread is dead, or we are already detached. + // The latter case is possible, for instance, if this function was called + // from a signal handler. + if (common_flags()->verbosity) + Report("Could not detach from thread %d (errno %d).\n", tid, pterrno); + } + } +} + +void ThreadSuspender::KillAllThreads() { + for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) + internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i), + NULL, NULL); +} + +bool ThreadSuspender::SuspendAllThreads() { + ThreadLister thread_lister(pid_); + bool added_threads; + do { + // Run through the directory entries once. + added_threads = false; + pid_t tid = thread_lister.GetNextTID(); + while (tid >= 0) { + if (SuspendThread(tid)) + added_threads = true; + tid = thread_lister.GetNextTID(); + } + if (thread_lister.error()) { + // Detach threads and fail. + ResumeAllThreads(); + return false; + } + thread_lister.Reset(); + } while (added_threads); + return true; +} + +// Pointer to the ThreadSuspender instance for use in signal handler. +static ThreadSuspender *thread_suspender_instance = NULL; + +// Signals that should not be blocked (this is used in the parent thread as well +// as the tracer thread). +static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ }; + +// Structure for passing arguments into the tracer thread. +struct TracerThreadArgument { + StopTheWorldCallback callback; + void *callback_argument; + // The tracer thread waits on this mutex while the parent finishes its + // preparations. + BlockingMutex mutex; + uptr parent_pid; +}; + +static DieCallbackType old_die_callback; + +// Signal handler to wake up suspended threads when the tracer thread dies. +void TracerThreadSignalHandler(int signum, void *siginfo, void *) { + if (thread_suspender_instance != NULL) { + if (signum == SIGABRT) + thread_suspender_instance->KillAllThreads(); + else + thread_suspender_instance->ResumeAllThreads(); + } + internal__exit((signum == SIGABRT) ? 1 : 2); +} + +static void TracerThreadDieCallback() { + // Generally a call to Die() in the tracer thread should be fatal to the + // parent process as well, because they share the address space. + // This really only works correctly if all the threads are suspended at this + // point. So we correctly handle calls to Die() from within the callback, but + // not those that happen before or after the callback. Hopefully there aren't + // a lot of opportunities for that to happen... + if (thread_suspender_instance) + thread_suspender_instance->KillAllThreads(); + if (old_die_callback) + old_die_callback(); +} + +// Size of alternative stack for signal handlers in the tracer thread. +static const int kHandlerStackSize = 4096; + +// This function will be run as a cloned task. +static int TracerThread(void* argument) { + TracerThreadArgument *tracer_thread_argument = + (TracerThreadArgument *)argument; + + internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + // Check if parent is already dead. + if (internal_getppid() != tracer_thread_argument->parent_pid) + internal__exit(4); + + // Wait for the parent thread to finish preparations. + tracer_thread_argument->mutex.Lock(); + tracer_thread_argument->mutex.Unlock(); + + SetDieCallback(TracerThreadDieCallback); + + ThreadSuspender thread_suspender(internal_getppid()); + // Global pointer for the signal handler. + thread_suspender_instance = &thread_suspender; + + // Alternate stack for signal handling. + InternalScopedBuffer handler_stack_memory(kHandlerStackSize); + struct sigaltstack handler_stack; + internal_memset(&handler_stack, 0, sizeof(handler_stack)); + handler_stack.ss_sp = handler_stack_memory.data(); + handler_stack.ss_size = kHandlerStackSize; + internal_sigaltstack(&handler_stack, NULL); + + // Install our handler for fatal signals. Other signals should be blocked by + // the mask we inherited from the caller thread. + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + __sanitizer_kernel_sigaction_t new_sigaction; + internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); + new_sigaction.sigaction = TracerThreadSignalHandler; + new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO; + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); + } + + int exit_code = 0; + if (!thread_suspender.SuspendAllThreads()) { + if (common_flags()->verbosity) + Report("Failed suspending threads.\n"); + exit_code = 3; + } else { + tracer_thread_argument->callback(thread_suspender.suspended_threads_list(), + tracer_thread_argument->callback_argument); + thread_suspender.ResumeAllThreads(); + exit_code = 0; + } + thread_suspender_instance = NULL; + handler_stack.ss_flags = SS_DISABLE; + internal_sigaltstack(&handler_stack, NULL); + return exit_code; +} + +class ScopedStackSpaceWithGuard { + public: + explicit ScopedStackSpaceWithGuard(uptr stack_size) { + stack_size_ = stack_size; + guard_size_ = GetPageSizeCached(); + // FIXME: Omitting MAP_STACK here works in current kernels but might break + // in the future. + guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_, + "ScopedStackWithGuard"); + CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_)); + } + ~ScopedStackSpaceWithGuard() { + UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_); + } + void *Bottom() const { + return (void *)(guard_start_ + stack_size_ + guard_size_); + } + + private: + uptr stack_size_; + uptr guard_size_; + uptr guard_start_; +}; + +// We have a limitation on the stack frame size, so some stuff had to be moved +// into globals. +static __sanitizer_kernel_sigset_t blocked_sigset; +static __sanitizer_kernel_sigset_t old_sigset; +static __sanitizer_kernel_sigaction_t old_sigactions + [ARRAY_SIZE(kUnblockedSignals)]; + +class StopTheWorldScope { + public: + StopTheWorldScope() { + // Block all signals that can be blocked safely, and install + // default handlers for the remaining signals. + // We cannot allow user-defined handlers to run while the ThreadSuspender + // thread is active, because they could conceivably call some libc functions + // which modify errno (which is shared between the two threads). + internal_sigfillset(&blocked_sigset); + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + // Remove the signal from the set of blocked signals. + internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); + // Install the default handler. + __sanitizer_kernel_sigaction_t new_sigaction; + internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); + new_sigaction.handler = SIG_DFL; + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, + &old_sigactions[signal_index]); + } + int sigprocmask_status = + internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); + CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail + // Make this process dumpable. Processes that are not dumpable cannot be + // attached to. + process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0); + if (!process_was_dumpable_) + internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + old_die_callback = GetDieCallback(); + } + + ~StopTheWorldScope() { + SetDieCallback(old_die_callback); + // Restore the dumpable flag. + if (!process_was_dumpable_) + internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); + // Restore the signal handlers. + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + internal_sigaction(kUnblockedSignals[signal_index], + &old_sigactions[signal_index], NULL); + } + internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); + } + + private: + int process_was_dumpable_; +}; + +void StopTheWorld(StopTheWorldCallback callback, void *argument) { + StopTheWorldScope in_stoptheworld; + // Prepare the arguments for TracerThread. + struct TracerThreadArgument tracer_thread_argument; + tracer_thread_argument.callback = callback; + tracer_thread_argument.callback_argument = argument; + tracer_thread_argument.parent_pid = internal_getpid(); + const uptr kTracerStackSize = 2 * 1024 * 1024; + ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize); + // Block the execution of TracerThread until after we have set ptrace + // permissions. + tracer_thread_argument.mutex.Lock(); + uptr tracer_pid = internal_clone( + TracerThread, tracer_stack.Bottom(), + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, + &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0 + /* child_tidptr */); + int local_errno = 0; + if (internal_iserror(tracer_pid, &local_errno)) { + if (common_flags()->verbosity) + Report("Failed spawning a tracer thread (errno %d).\n", local_errno); + tracer_thread_argument.mutex.Unlock(); + } else { + // On some systems we have to explicitly declare that we want to be traced + // by the tracer thread. +#ifdef PR_SET_PTRACER + internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0); +#endif + // Allow the tracer thread to start. + tracer_thread_argument.mutex.Unlock(); + // Since errno is shared between this thread and the tracer thread, we + // must avoid using errno while the tracer thread is running. + // At this point, any signal will either be blocked or kill us, so waitpid + // should never return (and set errno) while the tracer thread is alive. + uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); + if (internal_iserror(waitpid_status, &local_errno)) { + if (common_flags()->verbosity) + Report("Waiting on the tracer thread failed (errno %d).\n", + local_errno); + } + } +} + +// Platform-specific methods from SuspendedThreadsList. +#if SANITIZER_ANDROID && defined(__arm__) +typedef pt_regs regs_struct; +#define REG_SP ARM_sp + +#elif SANITIZER_LINUX && defined(__arm__) +typedef user_regs regs_struct; +#define REG_SP uregs[13] + +#elif defined(__i386__) || defined(__x86_64__) +typedef user_regs_struct regs_struct; +#if defined(__i386__) +#define REG_SP esp +#else +#define REG_SP rsp +#endif + +#elif defined(__powerpc__) || defined(__powerpc64__) +typedef pt_regs regs_struct; +#define REG_SP gpr[PT_R1] + +#elif defined(__mips__) +typedef struct user regs_struct; +#define REG_SP regs[EF_REG29] + +#else +#error "Unsupported architecture" +#endif // SANITIZER_ANDROID && defined(__arm__) + +int SuspendedThreadsList::GetRegistersAndSP(uptr index, + uptr *buffer, + uptr *sp) const { + pid_t tid = GetThreadID(index); + regs_struct regs; + int pterrno; + if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s), + &pterrno)) { + if (common_flags()->verbosity) + Report("Could not get registers from thread %d (errno %d).\n", + tid, pterrno); + return -1; + } + + *sp = regs.REG_SP; + internal_memcpy(buffer, ®s, sizeof(regs)); + return 0; +} + +uptr SuspendedThreadsList::RegisterCount() { + return sizeof(regs_struct) / sizeof(uptr); +} +} // namespace __sanitizer + +#endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc new file mode 100644 index 00000000..5f3d2cee --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc @@ -0,0 +1,153 @@ +//===-- sanitizer_suppressions.cc -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Suppression parsing/matching code shared between TSan and LSan. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_suppressions.h" + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +static const char *const kTypeStrings[SuppressionTypeCount] = { + "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib" +}; + +bool TemplateMatch(char *templ, const char *str) { + if (str == 0 || str[0] == 0) + return false; + bool start = false; + if (templ && templ[0] == '^') { + start = true; + templ++; + } + bool asterisk = false; + while (templ && templ[0]) { + if (templ[0] == '*') { + templ++; + start = false; + asterisk = true; + continue; + } + if (templ[0] == '$') + return str[0] == 0 || asterisk; + if (str[0] == 0) + return false; + char *tpos = (char*)internal_strchr(templ, '*'); + char *tpos1 = (char*)internal_strchr(templ, '$'); + if (tpos == 0 || (tpos1 && tpos1 < tpos)) + tpos = tpos1; + if (tpos != 0) + tpos[0] = 0; + const char *str0 = str; + const char *spos = internal_strstr(str, templ); + str = spos + internal_strlen(templ); + templ = tpos; + if (tpos) + tpos[0] = tpos == tpos1 ? '$' : '*'; + if (spos == 0) + return false; + if (start && spos != str0) + return false; + start = false; + asterisk = false; + } + return true; +} + +bool SuppressionContext::Match(const char *str, SuppressionType type, + Suppression **s) { + can_parse_ = false; + uptr i; + for (i = 0; i < suppressions_.size(); i++) + if (type == suppressions_[i].type && + TemplateMatch(suppressions_[i].templ, str)) + break; + if (i == suppressions_.size()) return false; + *s = &suppressions_[i]; + return true; +} + +static const char *StripPrefix(const char *str, const char *prefix) { + while (str && *str == *prefix) { + str++; + prefix++; + } + if (!*prefix) + return str; + return 0; +} + +void SuppressionContext::Parse(const char *str) { + // Context must not mutate once Match has been called. + CHECK(can_parse_); + const char *line = str; + while (line) { + while (line[0] == ' ' || line[0] == '\t') + line++; + const char *end = internal_strchr(line, '\n'); + if (end == 0) + end = line + internal_strlen(line); + if (line != end && line[0] != '#') { + const char *end2 = end; + while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) + end2--; + int type; + for (type = 0; type < SuppressionTypeCount; type++) { + const char *next_char = StripPrefix(line, kTypeStrings[type]); + if (next_char && *next_char == ':') { + line = ++next_char; + break; + } + } + if (type == SuppressionTypeCount) { + Printf("%s: failed to parse suppressions\n", SanitizerToolName); + Die(); + } + Suppression s; + s.type = static_cast(type); + s.templ = (char*)InternalAlloc(end2 - line + 1); + internal_memcpy(s.templ, line, end2 - line); + s.templ[end2 - line] = 0; + s.hit_count = 0; + s.weight = 0; + suppressions_.push_back(s); + } + if (end[0] == 0) + break; + line = end + 1; + } +} + +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 *matched) { + for (uptr i = 0; i < suppressions_.size(); i++) + if (suppressions_[i].hit_count) + matched->push_back(&suppressions_[i]); +} + +const char *SuppressionTypeString(SuppressionType t) { + CHECK(t < SuppressionTypeCount); + return kTypeStrings[t]; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h new file mode 100644 index 00000000..92cb0936 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h @@ -0,0 +1,61 @@ +//===-- sanitizer_suppressions.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Suppression parsing/matching code shared between TSan and LSan. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SUPPRESSIONS_H +#define SANITIZER_SUPPRESSIONS_H + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +enum SuppressionType { + SuppressionNone, + SuppressionRace, + SuppressionMutex, + SuppressionThread, + SuppressionSignal, + SuppressionLeak, + SuppressionLib, + SuppressionTypeCount +}; + +struct Suppression { + SuppressionType type; + char *templ; + unsigned hit_count; + uptr weight; +}; + +class SuppressionContext { + public: + SuppressionContext() : suppressions_(1), can_parse_(true) {} + void Parse(const char *str); + bool Match(const char* str, SuppressionType type, Suppression **s); + uptr SuppressionCount() const; + const Suppression *SuppressionAt(uptr i) const; + void GetMatched(InternalMmapVector *matched); + + private: + InternalMmapVector suppressions_; + bool can_parse_; + + friend class SuppressionContextTest; +}; + +const char *SuppressionTypeString(SuppressionType t); + +bool TemplateMatch(char *templ, const char *str); + +} // namespace __sanitizer + +#endif // SANITIZER_SUPPRESSIONS_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cc new file mode 100644 index 00000000..2290767b --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.cc @@ -0,0 +1,63 @@ +//===-- sanitizer_symbolizer.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::symbolizer_; +StaticSpinMutex Symbolizer::init_mu_; +LowLevelAllocator Symbolizer::symbolizer_allocator_; + +Symbolizer *Symbolizer::GetOrNull() { + SpinMutexLock l(&init_mu_); + return symbolizer_; +} + +Symbolizer *Symbolizer::Get() { + SpinMutexLock l(&init_mu_); + RAW_CHECK_MSG(symbolizer_ != 0, "Using uninitialized symbolizer!"); + return symbolizer_; +} + +Symbolizer *Symbolizer::Disable() { + CHECK_EQ(0, symbolizer_); + // Initialize a dummy symbolizer. + symbolizer_ = new(symbolizer_allocator_) Symbolizer; + return symbolizer_; +} + +void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook, + Symbolizer::EndSymbolizationHook end_hook) { + CHECK(start_hook_ == 0 && end_hook_ == 0); + start_hook_ = start_hook; + end_hook_ = end_hook; +} + +Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {} + +Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym) + : sym_(sym) { + if (sym_->start_hook_) + sym_->start_hook_(); +} + +Symbolizer::SymbolizerScope::~SymbolizerScope() { + if (sym_->end_hook_) + sym_->end_hook_(); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h new file mode 100644 index 00000000..886fed20 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h @@ -0,0 +1,146 @@ +//===-- sanitizer_symbolizer.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Symbolizer is used by sanitizers to map instruction address to a location in +// source code at run-time. Symbolizer either uses __sanitizer_symbolize_* +// defined in the program, or (if they are missing) tries to find and +// launch "llvm-symbolizer" commandline tool in a separate process and +// communicate with it. +// +// Generally we should try to avoid calling system library functions during +// symbolization (and use their replacements from sanitizer_libc.h instead). +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_H +#define SANITIZER_SYMBOLIZER_H + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +struct AddressInfo { + uptr address; + char *module; + uptr module_offset; + char *function; + char *file; + int line; + int column; + + AddressInfo() { + internal_memset(this, 0, sizeof(AddressInfo)); + } + + // Deletes all strings and sets all fields to zero. + void Clear() { + InternalFree(module); + InternalFree(function); + InternalFree(file); + internal_memset(this, 0, sizeof(AddressInfo)); + } + + void FillAddressAndModuleInfo(uptr addr, const char *mod_name, + uptr mod_offset) { + address = addr; + module = internal_strdup(mod_name); + module_offset = mod_offset; + } +}; + +struct DataInfo { + uptr address; + char *module; + uptr module_offset; + char *name; + uptr start; + uptr size; +}; + +class Symbolizer { + public: + /// Returns platform-specific implementation of Symbolizer. The symbolizer + /// must be initialized (with init or disable) before calling this function. + static Symbolizer *Get(); + /// Returns platform-specific implementation of Symbolizer, or null if not + /// initialized. + static Symbolizer *GetOrNull(); + /// Returns platform-specific implementation of Symbolizer. Will + /// automatically initialize symbolizer as if by calling Init(0) if needed. + static Symbolizer *GetOrInit(); + /// Initialize and return the symbolizer, given an optional path to an + /// external symbolizer. The path argument is only required for legacy + /// reasons as this function will check $PATH for an external symbolizer. Not + /// thread safe. + static Symbolizer *Init(const char* path_to_external = 0); + /// Initialize the symbolizer in a disabled state. Not thread safe. + static Symbolizer *Disable(); + // Fills at most "max_frames" elements of "frames" with descriptions + // for a given address (in all inlined functions). Returns the number + // of descriptions actually filled. + virtual uptr SymbolizeCode(uptr address, AddressInfo *frames, + uptr max_frames) { + return 0; + } + virtual bool SymbolizeData(uptr address, DataInfo *info) { + return false; + } + virtual bool IsAvailable() { + return false; + } + virtual bool IsExternalAvailable() { + return false; + } + // Release internal caches (if any). + virtual void Flush() {} + // Attempts to demangle the provided C++ mangled name. + virtual const char *Demangle(const char *name) { + return name; + } + virtual void PrepareForSandboxing() {} + + // Allow user to install hooks that would be called before/after Symbolizer + // does the actual file/line info fetching. Specific sanitizers may need this + // to distinguish system library calls made in user code from calls made + // during in-process symbolization. + typedef void (*StartSymbolizationHook)(); + typedef void (*EndSymbolizationHook)(); + // May be called at most once. + void AddHooks(StartSymbolizationHook start_hook, + EndSymbolizationHook end_hook); + + private: + /// Platform-specific function for creating a Symbolizer object. + static Symbolizer *PlatformInit(const char *path_to_external); + /// Create a symbolizer and store it to symbolizer_ without checking if one + /// already exists. Not thread safe. + static Symbolizer *CreateAndStore(const char *path_to_external); + + static Symbolizer *symbolizer_; + static StaticSpinMutex init_mu_; + + protected: + Symbolizer(); + + static LowLevelAllocator symbolizer_allocator_; + + StartSymbolizationHook start_hook_; + EndSymbolizationHook end_hook_; + class SymbolizerScope { + public: + explicit SymbolizerScope(const Symbolizer *sym); + ~SymbolizerScope(); + private: + const Symbolizer *sym_; + }; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_H diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc new file mode 100644 index 00000000..b431e51e --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -0,0 +1,39 @@ +//===-- sanitizer_symbolizer_libcdep.cc -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_internal_defs.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::CreateAndStore(const char *path_to_external) { + Symbolizer *platform_symbolizer = PlatformInit(path_to_external); + if (!platform_symbolizer) + return Disable(); + symbolizer_ = platform_symbolizer; + return platform_symbolizer; +} + +Symbolizer *Symbolizer::Init(const char *path_to_external) { + CHECK_EQ(0, symbolizer_); + return CreateAndStore(path_to_external); +} + +Symbolizer *Symbolizer::GetOrInit() { + SpinMutexLock l(&init_mu_); + if (symbolizer_ == 0) + return CreateAndStore(0); + return symbolizer_; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc new file mode 100644 index 00000000..de118328 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -0,0 +1,601 @@ +//===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// POSIX-specific implementation of symbolizer parts. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_POSIX +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_linux.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_symbolizer.h" + +#include +#include +#include +#include + +// C++ demangling function, as required by Itanium C++ ABI. This is weak, +// because we do not require a C++ ABI library to be linked to a program +// using sanitizers; if it's not present, we'll just use the mangled name. +namespace __cxxabiv1 { + extern "C" SANITIZER_WEAK_ATTRIBUTE + char *__cxa_demangle(const char *mangled, char *buffer, + size_t *length, int *status); +} + +namespace __sanitizer { + +// Attempts to demangle the name via __cxa_demangle from __cxxabiv1. +static const char *DemangleCXXABI(const char *name) { + // FIXME: __cxa_demangle aggressively insists on allocating memory. + // There's not much we can do about that, short of providing our + // own demangler (libc++abi's implementation could be adapted so that + // it does not allocate). For now, we just call it anyway, and we leak + // the returned value. + if (__cxxabiv1::__cxa_demangle) + if (const char *demangled_name = + __cxxabiv1::__cxa_demangle(name, 0, 0, 0)) + return demangled_name; + + return name; +} + +#if defined(__x86_64__) +static const char* const kSymbolizerArch = "--default-arch=x86_64"; +#elif defined(__i386__) +static const char* const kSymbolizerArch = "--default-arch=i386"; +#elif defined(__powerpc64__) +static const char* const kSymbolizerArch = "--default-arch=powerpc64"; +#else +static const char* const kSymbolizerArch = "--default-arch=unknown"; +#endif + +static const int kSymbolizerStartupTimeMillis = 10; + +// Creates external symbolizer connected via pipe, user should write +// to output_fd and read from input_fd. +static bool StartSymbolizerSubprocess(const char *path_to_symbolizer, + int *input_fd, int *output_fd) { + if (!FileExists(path_to_symbolizer)) { + Report("WARNING: invalid path to external symbolizer!\n"); + return false; + } + + int *infd = NULL; + int *outfd = NULL; + // The client program may close its stdin and/or stdout and/or stderr + // thus allowing socketpair to reuse file descriptors 0, 1 or 2. + // In this case the communication between the forked processes may be + // broken if either the parent or the child tries to close or duplicate + // these descriptors. The loop below produces two pairs of file + // descriptors, each greater than 2 (stderr). + int sock_pair[5][2]; + for (int i = 0; i < 5; i++) { + if (pipe(sock_pair[i]) == -1) { + for (int j = 0; j < i; j++) { + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + Report("WARNING: Can't create a socket pair to start " + "external symbolizer (errno: %d)\n", errno); + return false; + } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { + if (infd == NULL) { + infd = sock_pair[i]; + } else { + outfd = sock_pair[i]; + for (int j = 0; j < i; j++) { + if (sock_pair[j] == infd) continue; + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + break; + } + } + } + CHECK(infd); + CHECK(outfd); + + int pid = fork(); + if (pid == -1) { + // Fork() failed. + internal_close(infd[0]); + internal_close(infd[1]); + internal_close(outfd[0]); + internal_close(outfd[1]); + Report("WARNING: failed to fork external symbolizer " + " (errno: %d)\n", errno); + return false; + } else if (pid == 0) { + // Child subprocess. + internal_close(STDOUT_FILENO); + internal_close(STDIN_FILENO); + internal_dup2(outfd[0], STDIN_FILENO); + internal_dup2(infd[1], STDOUT_FILENO); + internal_close(outfd[0]); + internal_close(outfd[1]); + internal_close(infd[0]); + internal_close(infd[1]); + for (int fd = getdtablesize(); fd > 2; fd--) + internal_close(fd); + execl(path_to_symbolizer, path_to_symbolizer, kSymbolizerArch, (char*)0); + internal__exit(1); + } + + // Continue execution in parent process. + internal_close(outfd[0]); + internal_close(infd[1]); + *input_fd = infd[0]; + *output_fd = outfd[1]; + + // Check that symbolizer subprocess started successfully. + int pid_status; + SleepForMillis(kSymbolizerStartupTimeMillis); + int exited_pid = waitpid(pid, &pid_status, WNOHANG); + if (exited_pid != 0) { + // Either waitpid failed, or child has already exited. + Report("WARNING: external symbolizer didn't start up correctly!\n"); + return false; + } + + return true; +} + +// Extracts the prefix of "str" that consists of any characters not +// present in "delims" string, and copies this prefix to "result", allocating +// space for it. +// Returns a pointer to "str" after skipping extracted prefix and first +// delimiter char. +static const char *ExtractToken(const char *str, const char *delims, + char **result) { + uptr prefix_len = internal_strcspn(str, delims); + *result = (char*)InternalAlloc(prefix_len + 1); + internal_memcpy(*result, str, prefix_len); + (*result)[prefix_len] = '\0'; + const char *prefix_end = str + prefix_len; + if (*prefix_end != '\0') prefix_end++; + return prefix_end; +} + +// Same as ExtractToken, but converts extracted token to integer. +static const char *ExtractInt(const char *str, const char *delims, + int *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (int)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +static const char *ExtractUptr(const char *str, const char *delims, + uptr *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (uptr)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +// ExternalSymbolizer encapsulates communication between the tool and +// external symbolizer program, running in a different subprocess, +// For now we assume the following protocol: +// For each request of the form +// +// passed to STDIN, external symbolizer prints to STDOUT response: +// +// :: +// +// :: +// ... +// +class ExternalSymbolizer { + public: + ExternalSymbolizer(const char *path, int input_fd, int output_fd) + : path_(path), + input_fd_(input_fd), + output_fd_(output_fd), + times_restarted_(0) { + CHECK(path_); + CHECK_NE(input_fd_, kInvalidFd); + CHECK_NE(output_fd_, kInvalidFd); + } + + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + CHECK(module_name); + internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", + is_data ? "DATA " : "", module_name, module_offset); + if (!writeToSymbolizer(buffer_, internal_strlen(buffer_))) + return 0; + if (!readFromSymbolizer(buffer_, kBufferSize)) + return 0; + return buffer_; + } + + bool Restart() { + if (times_restarted_ >= kMaxTimesRestarted) return false; + times_restarted_++; + internal_close(input_fd_); + internal_close(output_fd_); + return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_); + } + + void Flush() { + } + + private: + bool readFromSymbolizer(char *buffer, uptr max_length) { + if (max_length == 0) + return true; + uptr read_len = 0; + while (true) { + uptr just_read = internal_read(input_fd_, buffer + read_len, + max_length - read_len); + // We can't read 0 bytes, as we don't expect external symbolizer to close + // its stdout. + if (just_read == 0 || just_read == (uptr)-1) { + Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); + return false; + } + read_len += just_read; + // Empty line marks the end of symbolizer output. + if (read_len >= 2 && buffer[read_len - 1] == '\n' && + buffer[read_len - 2] == '\n') { + break; + } + } + return true; + } + + bool writeToSymbolizer(const char *buffer, uptr length) { + if (length == 0) + return true; + uptr write_len = internal_write(output_fd_, buffer, length); + if (write_len == 0 || write_len == (uptr)-1) { + Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); + return false; + } + return true; + } + + const char *path_; + int input_fd_; + int output_fd_; + + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; + + static const uptr kMaxTimesRestarted = 5; + uptr times_restarted_; +}; + +#if SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_symbolize_flush(); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __sanitizer_symbolize_demangle(const char *Name, char *Buffer, + int MaxLength); +} // extern "C" + +class InternalSymbolizer { + public: + typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); + + static InternalSymbolizer *get(LowLevelAllocator *alloc) { + if (__sanitizer_symbolize_code != 0 && + __sanitizer_symbolize_data != 0) { + return new(*alloc) InternalSymbolizer(); + } + return 0; + } + + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data + : __sanitizer_symbolize_code; + if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) + return buffer_; + return 0; + } + + void Flush() { + if (__sanitizer_symbolize_flush) + __sanitizer_symbolize_flush(); + } + + const char *Demangle(const char *name) { + if (__sanitizer_symbolize_demangle) { + for (uptr res_length = 1024; + res_length <= InternalSizeClassMap::kMaxSize;) { + char *res_buff = static_cast(InternalAlloc(res_length)); + uptr req_length = + __sanitizer_symbolize_demangle(name, res_buff, res_length); + if (req_length > res_length) { + res_length = req_length + 1; + InternalFree(res_buff); + continue; + } + return res_buff; + } + } + return name; + } + + private: + InternalSymbolizer() { } + + static const int kBufferSize = 16 * 1024; + static const int kMaxDemangledNameSize = 1024; + char buffer_[kBufferSize]; +}; +#else // SANITIZER_SUPPORTS_WEAK_HOOKS + +class InternalSymbolizer { + public: + static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; } + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + return 0; + } + void Flush() { } + const char *Demangle(const char *name) { return name; } +}; + +#endif // SANITIZER_SUPPORTS_WEAK_HOOKS + +class POSIXSymbolizer : public Symbolizer { + public: + POSIXSymbolizer(ExternalSymbolizer *external_symbolizer, + InternalSymbolizer *internal_symbolizer) + : Symbolizer(), + external_symbolizer_(external_symbolizer), + internal_symbolizer_(internal_symbolizer) {} + + uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { + BlockingMutexLock l(&mu_); + if (max_frames == 0) + return 0; + LoadedModule *module = FindModuleForAddress(addr); + if (module == 0) + return 0; + const char *module_name = module->full_name(); + uptr module_offset = addr - module->base_address(); + const char *str = SendCommand(false, module_name, module_offset); + if (str == 0) { + // External symbolizer was not initialized or failed. Fill only data + // about module name and offset. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + return 1; + } + uptr frame_id = 0; + for (frame_id = 0; frame_id < max_frames; frame_id++) { + AddressInfo *info = &frames[frame_id]; + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + break; + } + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + info->function = function_name; + // Parse :: buffer. + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + const char *line_info = ExtractToken(file_line_info, ":", &info->file); + line_info = ExtractInt(line_info, ":", &info->line); + line_info = ExtractInt(line_info, "", &info->column); + InternalFree(file_line_info); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; + } + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; + } + } + if (frame_id == 0) { + // Make sure we return at least one frame. + AddressInfo *info = &frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, module_name, module_offset); + frame_id = 1; + } + return frame_id; + } + + bool SymbolizeData(uptr addr, DataInfo *info) { + BlockingMutexLock l(&mu_); + LoadedModule *module = FindModuleForAddress(addr); + if (module == 0) + return false; + const char *module_name = module->full_name(); + uptr module_offset = addr - module->base_address(); + internal_memset(info, 0, sizeof(*info)); + info->address = addr; + info->module = internal_strdup(module_name); + info->module_offset = module_offset; + const char *str = SendCommand(true, module_name, module_offset); + if (str == 0) + return true; + str = ExtractToken(str, "\n", &info->name); + str = ExtractUptr(str, " ", &info->start); + str = ExtractUptr(str, "\n", &info->size); + info->start += module->base_address(); + return true; + } + + bool IsAvailable() { + return internal_symbolizer_ != 0 || external_symbolizer_ != 0; + } + + bool IsExternalAvailable() { + return external_symbolizer_ != 0; + } + + void Flush() { + BlockingMutexLock l(&mu_); + if (internal_symbolizer_ != 0) { + SymbolizerScope sym_scope(this); + internal_symbolizer_->Flush(); + } + if (external_symbolizer_ != 0) + external_symbolizer_->Flush(); + } + + const char *Demangle(const char *name) { + BlockingMutexLock l(&mu_); + // Run hooks even if we don't use internal symbolizer, as cxxabi + // demangle may call system functions. + SymbolizerScope sym_scope(this); + if (internal_symbolizer_ != 0) + return internal_symbolizer_->Demangle(name); + return DemangleCXXABI(name); + } + + void PrepareForSandboxing() { +#if SANITIZER_LINUX && !SANITIZER_ANDROID + BlockingMutexLock l(&mu_); + // Cache /proc/self/exe on Linux. + CacheBinaryName(); +#endif + } + + private: + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + mu_.CheckLocked(); + // First, try to use internal symbolizer. + if (internal_symbolizer_) { + SymbolizerScope sym_scope(this); + return internal_symbolizer_->SendCommand(is_data, module_name, + module_offset); + } + // Otherwise, fall back to external symbolizer. + if (external_symbolizer_ == 0) { + ReportExternalSymbolizerError( + "WARNING: Trying to symbolize code, but external " + "symbolizer is not initialized!\n"); + return 0; + } + for (;;) { + char *reply = external_symbolizer_->SendCommand(is_data, module_name, + module_offset); + if (reply) + return reply; + // Try to restart symbolizer subprocess. If we don't succeed, forget + // about it and don't try to use it later. + if (!external_symbolizer_->Restart()) { + ReportExternalSymbolizerError( + "WARNING: Failed to use and restart external symbolizer!\n"); + external_symbolizer_ = 0; + return 0; + } + } + } + + LoadedModule *FindModuleForAddress(uptr address) { + mu_.CheckLocked(); + bool modules_were_reloaded = false; + if (modules_ == 0 || !modules_fresh_) { + modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate( + kMaxNumberOfModuleContexts * sizeof(LoadedModule))); + CHECK(modules_); + n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts, + /* filter */ 0); + // FIXME: Return this check when GetListOfModules is implemented on Mac. + // CHECK_GT(n_modules_, 0); + CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); + modules_fresh_ = true; + modules_were_reloaded = true; + } + for (uptr i = 0; i < n_modules_; i++) { + if (modules_[i].containsAddress(address)) { + return &modules_[i]; + } + } + // Reload the modules and look up again, if we haven't tried it yet. + if (!modules_were_reloaded) { + // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors. + // It's too aggressive to reload the list of modules each time we fail + // to find a module for a given address. + modules_fresh_ = false; + return FindModuleForAddress(address); + } + return 0; + } + + void ReportExternalSymbolizerError(const char *msg) { + // Don't use atomics here for now, as SymbolizeCode can't be called + // from multiple threads anyway. + static bool reported; + if (!reported) { + Report(msg); + reported = true; + } + } + + // 16K loaded modules should be enough for everyone. + static const uptr kMaxNumberOfModuleContexts = 1 << 14; + LoadedModule *modules_; // Array of module descriptions is leaked. + uptr n_modules_; + // If stale, need to reload the modules before looking up addresses. + bool modules_fresh_; + BlockingMutex mu_; + + ExternalSymbolizer *external_symbolizer_; // Leaked. + InternalSymbolizer *const internal_symbolizer_; // Leaked. +}; + +Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { + InternalSymbolizer* internal_symbolizer = + InternalSymbolizer::get(&symbolizer_allocator_); + ExternalSymbolizer *external_symbolizer = 0; + + if (!internal_symbolizer) { + if (!path_to_external || path_to_external[0] == '\0') + path_to_external = FindPathToBinary("llvm-symbolizer"); + + int input_fd, output_fd; + if (path_to_external && + StartSymbolizerSubprocess(path_to_external, &input_fd, &output_fd)) { + external_symbolizer = new(symbolizer_allocator_) + ExternalSymbolizer(path_to_external, input_fd, output_fd); + } + } + + return new(symbolizer_allocator_) + POSIXSymbolizer(external_symbolizer, internal_symbolizer); +} + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc new file mode 100644 index 00000000..5d451eff --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_win.cc @@ -0,0 +1,25 @@ +//===-- sanitizer_symbolizer_win.cc ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Windows-specific implementation of symbolizer parts. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_WINDOWS +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { return 0; } + +} // namespace __sanitizer + +#endif // _WIN32 diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_syscall_generic.inc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_syscall_generic.inc new file mode 100644 index 00000000..aac20a5f --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_syscall_generic.inc @@ -0,0 +1,24 @@ +//===-- sanitizer_syscall_generic.inc ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generic implementations of internal_syscall and internal_iserror. +// +//===----------------------------------------------------------------------===// + +#define internal_syscall syscall + +bool internal_iserror(uptr retval, int *rverrno) { + if (retval == (uptr)-1) { + if (rverrno) + *rverrno = errno; + return true; + } else { + return false; + } +} diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc new file mode 100644 index 00000000..5ccb83a2 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc @@ -0,0 +1,89 @@ +//===-- sanitizer_syscall_linux_x86_64.inc ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementations of internal_syscall and internal_iserror for Linux/x86_64. +// +//===----------------------------------------------------------------------===// + +static uptr internal_syscall(u64 nr) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11", + "memory", "cc"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1) : + "rcx", "r11", "memory", "cc"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2) : "rcx", "r11", "memory", "cc"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3) : "rcx", "r11", "memory", "cc"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { + u64 retval; + asm volatile("mov %5, %%r10;" + "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4) : + "rcx", "r11", "r10", "memory", "cc"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, + T5 arg5) { + u64 retval; + asm volatile("mov %5, %%r10;" + "mov %6, %%r8;" + "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5) : + "rcx", "r11", "r10", "r8", "memory", "cc"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, + T5 arg5, T6 arg6) { + u64 retval; + asm volatile("mov %5, %%r10;" + "mov %6, %%r8;" + "mov %7, %%r9;" + "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5), + "r"((u64)arg6) : "rcx", "r11", "r10", "r8", "r9", + "memory", "cc"); + return retval; +} + +bool internal_iserror(uptr retval, int *rverrno) { + if (retval >= (uptr)-4095) { + if (rverrno) + *rverrno = -retval; + return true; + } + return false; +} diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cc new file mode 100644 index 00000000..bfa29a19 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cc @@ -0,0 +1,291 @@ +//===-- sanitizer_thread_registry.cc --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between sanitizer tools. +// +// General thread bookkeeping functionality. +//===----------------------------------------------------------------------===// + +#include "sanitizer_thread_registry.h" + +namespace __sanitizer { + +ThreadContextBase::ThreadContextBase(u32 tid) + : tid(tid), unique_id(0), os_id(0), user_id(0), status(ThreadStatusInvalid), + detached(false), reuse_count(0), parent_tid(0), next(0) { + name[0] = '\0'; +} + +ThreadContextBase::~ThreadContextBase() { + // ThreadContextBase should never be deleted. + CHECK(0); +} + +void ThreadContextBase::SetName(const char *new_name) { + name[0] = '\0'; + if (new_name) { + internal_strncpy(name, new_name, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } +} + +void ThreadContextBase::SetDead() { + CHECK(status == ThreadStatusRunning || + status == ThreadStatusFinished); + status = ThreadStatusDead; + user_id = 0; + OnDead(); +} + +void ThreadContextBase::SetJoined(void *arg) { + // FIXME(dvyukov): print message and continue (it's user error). + CHECK_EQ(false, detached); + CHECK_EQ(ThreadStatusFinished, status); + status = ThreadStatusDead; + user_id = 0; + OnJoined(arg); +} + +void ThreadContextBase::SetFinished() { + if (!detached) + status = ThreadStatusFinished; + OnFinished(); +} + +void ThreadContextBase::SetStarted(uptr _os_id, void *arg) { + status = ThreadStatusRunning; + os_id = _os_id; + OnStarted(arg); +} + +void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id, + bool _detached, u32 _parent_tid, void *arg) { + status = ThreadStatusCreated; + user_id = _user_id; + unique_id = _unique_id; + detached = _detached; + // Parent tid makes no sense for the main thread. + if (tid != 0) + parent_tid = _parent_tid; + OnCreated(arg); +} + +void ThreadContextBase::Reset() { + status = ThreadStatusInvalid; + reuse_count++; + SetName(0); + OnReset(); +} + +// ThreadRegistry implementation. + +const u32 ThreadRegistry::kUnknownTid = ~0U; + +ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, + u32 thread_quarantine_size) + : context_factory_(factory), + max_threads_(max_threads), + thread_quarantine_size_(thread_quarantine_size), + mtx_(), + n_contexts_(0), + total_threads_(0), + alive_threads_(0), + max_alive_threads_(0), + running_threads_(0) { + threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]), + "ThreadRegistry"); + dead_threads_.clear(); + invalid_threads_.clear(); +} + +void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running, + uptr *alive) { + BlockingMutexLock l(&mtx_); + if (total) *total = n_contexts_; + if (running) *running = running_threads_; + if (alive) *alive = alive_threads_; +} + +uptr ThreadRegistry::GetMaxAliveThreads() { + BlockingMutexLock l(&mtx_); + return max_alive_threads_; +} + +u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, + void *arg) { + BlockingMutexLock l(&mtx_); + u32 tid = kUnknownTid; + ThreadContextBase *tctx = QuarantinePop(); + if (tctx) { + tid = tctx->tid; + } else if (n_contexts_ < max_threads_) { + // Allocate new thread context and tid. + tid = n_contexts_++; + tctx = context_factory_(tid); + threads_[tid] = tctx; + } else { + Report("%s: Thread limit (%u threads) exceeded. Dying.\n", + SanitizerToolName, max_threads_); + Die(); + } + CHECK_NE(tctx, 0); + CHECK_NE(tid, kUnknownTid); + CHECK_LT(tid, max_threads_); + CHECK_EQ(tctx->status, ThreadStatusInvalid); + alive_threads_++; + if (max_alive_threads_ < alive_threads_) { + max_alive_threads_++; + CHECK_EQ(alive_threads_, max_alive_threads_); + } + tctx->SetCreated(user_id, total_threads_++, detached, + parent_tid, arg); + return tid; +} + +void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb, + void *arg) { + CheckLocked(); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx == 0) + continue; + cb(tctx, arg); + } +} + +u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) { + BlockingMutexLock l(&mtx_); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx != 0 && cb(tctx, arg)) + return tctx->tid; + } + return kUnknownTid; +} + +ThreadContextBase * +ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) { + CheckLocked(); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx != 0 && cb(tctx, arg)) + return tctx; + } + return 0; +} + +static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx, + void *arg) { + return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid && + tctx->status != ThreadStatusDead); +} + +ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(uptr os_id) { + return FindThreadContextLocked(FindThreadContextByOsIdCallback, + (void *)os_id); +} + +void ThreadRegistry::SetThreadName(u32 tid, const char *name) { + BlockingMutexLock l(&mtx_); + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + CHECK_EQ(ThreadStatusRunning, tctx->status); + tctx->SetName(name); +} + +void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) { + BlockingMutexLock l(&mtx_); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx != 0 && tctx->user_id == user_id && + tctx->status != ThreadStatusInvalid) { + tctx->SetName(name); + return; + } + } +} + +void ThreadRegistry::DetachThread(u32 tid) { + BlockingMutexLock l(&mtx_); + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + if (tctx->status == ThreadStatusInvalid) { + Report("%s: Detach of non-existent thread\n", SanitizerToolName); + return; + } + if (tctx->status == ThreadStatusFinished) { + tctx->SetDead(); + QuarantinePush(tctx); + } else { + tctx->detached = true; + } +} + +void ThreadRegistry::JoinThread(u32 tid, void *arg) { + BlockingMutexLock l(&mtx_); + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + if (tctx->status == ThreadStatusInvalid) { + Report("%s: Join of non-existent thread\n", SanitizerToolName); + return; + } + tctx->SetJoined(arg); + QuarantinePush(tctx); +} + +void ThreadRegistry::FinishThread(u32 tid) { + BlockingMutexLock l(&mtx_); + CHECK_GT(alive_threads_, 0); + alive_threads_--; + CHECK_GT(running_threads_, 0); + running_threads_--; + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + CHECK_EQ(ThreadStatusRunning, tctx->status); + tctx->SetFinished(); + if (tctx->detached) { + tctx->SetDead(); + QuarantinePush(tctx); + } +} + +void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) { + BlockingMutexLock l(&mtx_); + running_threads_++; + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + CHECK_EQ(ThreadStatusCreated, tctx->status); + tctx->SetStarted(os_id, arg); +} + +void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { + dead_threads_.push_back(tctx); + if (dead_threads_.size() <= thread_quarantine_size_) + return; + tctx = dead_threads_.front(); + dead_threads_.pop_front(); + CHECK_EQ(tctx->status, ThreadStatusDead); + tctx->Reset(); + invalid_threads_.push_back(tctx); +} + +ThreadContextBase *ThreadRegistry::QuarantinePop() { + if (invalid_threads_.size() == 0) + return 0; + ThreadContextBase *tctx = invalid_threads_.front(); + invalid_threads_.pop_front(); + return tctx; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h b/projects/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h new file mode 100644 index 00000000..a59bba57 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h @@ -0,0 +1,146 @@ +//===-- sanitizer_thread_registry.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between sanitizer tools. +// +// General thread bookkeeping functionality. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_THREAD_REGISTRY_H +#define SANITIZER_THREAD_REGISTRY_H + +#include "sanitizer_common.h" +#include "sanitizer_list.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +enum ThreadStatus { + ThreadStatusInvalid, // Non-existent thread, data is invalid. + ThreadStatusCreated, // Created but not yet running. + ThreadStatusRunning, // The thread is currently running. + ThreadStatusFinished, // Joinable thread is finished but not yet joined. + ThreadStatusDead // Joined, but some info is still available. +}; + +// Generic thread context. Specific sanitizer tools may inherit from it. +// If thread is dead, context may optionally be reused for a new thread. +class ThreadContextBase { + public: + explicit ThreadContextBase(u32 tid); + ~ThreadContextBase(); // Should never be called. + + const u32 tid; // Thread ID. Main thread should have tid = 0. + u64 unique_id; // Unique thread ID. + uptr os_id; // PID (used for reporting). + uptr user_id; // Some opaque user thread id (e.g. pthread_t). + char name[64]; // As annotated by user. + + ThreadStatus status; + bool detached; + int reuse_count; + + u32 parent_tid; + ThreadContextBase *next; // For storing thread contexts in a list. + + void SetName(const char *new_name); + + void SetDead(); + void SetJoined(void *arg); + void SetFinished(); + void SetStarted(uptr _os_id, void *arg); + void SetCreated(uptr _user_id, u64 _unique_id, bool _detached, + u32 _parent_tid, void *arg); + void Reset(); + + // The following methods may be overriden by subclasses. + // Some of them take opaque arg that may be optionally be used + // by subclasses. + virtual void OnDead() {} + virtual void OnJoined(void *arg) {} + virtual void OnFinished() {} + virtual void OnStarted(void *arg) {} + virtual void OnCreated(void *arg) {} + virtual void OnReset() {} +}; + +typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid); + +class ThreadRegistry { + public: + static const u32 kUnknownTid; + + ThreadRegistry(ThreadContextFactory factory, u32 max_threads, + u32 thread_quarantine_size); + void GetNumberOfThreads(uptr *total = 0, uptr *running = 0, uptr *alive = 0); + uptr GetMaxAliveThreads(); + + void Lock() { mtx_.Lock(); } + void CheckLocked() { mtx_.CheckLocked(); } + void Unlock() { mtx_.Unlock(); } + + // Should be guarded by ThreadRegistryLock. + ThreadContextBase *GetThreadLocked(u32 tid) { + DCHECK_LT(tid, n_contexts_); + return threads_[tid]; + } + + u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg); + + typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg); + // Invokes callback with a specified arg for each thread context. + // Should be guarded by ThreadRegistryLock. + void RunCallbackForEachThreadLocked(ThreadCallback cb, void *arg); + + typedef bool (*FindThreadCallback)(ThreadContextBase *tctx, void *arg); + // Finds a thread using the provided callback. Returns kUnknownTid if no + // thread is found. + u32 FindThread(FindThreadCallback cb, void *arg); + // Should be guarded by ThreadRegistryLock. Return 0 if no thread + // is found. + ThreadContextBase *FindThreadContextLocked(FindThreadCallback cb, + void *arg); + ThreadContextBase *FindThreadContextByOsIDLocked(uptr os_id); + + void SetThreadName(u32 tid, const char *name); + void SetThreadNameByUserId(uptr user_id, const char *name); + void DetachThread(u32 tid); + void JoinThread(u32 tid, void *arg); + void FinishThread(u32 tid); + void StartThread(u32 tid, uptr os_id, void *arg); + + private: + const ThreadContextFactory context_factory_; + const u32 max_threads_; + const u32 thread_quarantine_size_; + + BlockingMutex mtx_; + + u32 n_contexts_; // Number of created thread contexts, + // at most max_threads_. + u64 total_threads_; // Total number of created threads. May be greater than + // max_threads_ if contexts were reused. + uptr alive_threads_; // Created or running. + uptr max_alive_threads_; + uptr running_threads_; + + ThreadContextBase **threads_; // Array of thread contexts is leaked. + IntrusiveList dead_threads_; + IntrusiveList invalid_threads_; + + void QuarantinePush(ThreadContextBase *tctx); + ThreadContextBase *QuarantinePop(); +}; + +typedef GenericScopedLock ThreadRegistryLock; + +} // namespace __sanitizer + +#endif // SANITIZER_THREAD_REGISTRY_H + diff --git a/projects/compiler-rt/lib/sanitizer_common/sanitizer_win.cc b/projects/compiler-rt/lib/sanitizer_common/sanitizer_win.cc new file mode 100644 index 00000000..362c8c99 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/sanitizer_win.cc @@ -0,0 +1,410 @@ +//===-- sanitizer_win.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and implements windows-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_WINDOWS + +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#include +#include +#include + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_mutex.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_stacktrace.h" + +namespace __sanitizer { + +#include "sanitizer_syscall_generic.inc" + +// --------------------- sanitizer_common.h +uptr GetPageSize() { + return 1U << 14; // FIXME: is this configurable? +} + +uptr GetMmapGranularity() { + return 1U << 16; // FIXME: is this configurable? +} + +uptr GetMaxVirtualAddress() { + SYSTEM_INFO si; + GetSystemInfo(&si); + return (uptr)si.lpMaximumApplicationAddress; +} + +bool FileExists(const char *filename) { + UNIMPLEMENTED(); +} + +uptr internal_getpid() { + return GetProcessId(GetCurrentProcess()); +} + +// In contrast to POSIX, on Windows GetCurrentThreadId() +// returns a system-unique identifier. +uptr GetTid() { + return GetCurrentThreadId(); +} + +uptr GetThreadSelf() { + return GetTid(); +} + +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom) { + CHECK(stack_top); + CHECK(stack_bottom); + MEMORY_BASIC_INFORMATION mbi; + CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0); + // FIXME: is it possible for the stack to not be a single allocation? + // Are these values what ASan expects to get (reserved, not committed; + // including stack guard page) ? + *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize; + *stack_bottom = (uptr)mbi.AllocationBase; +} + +void *MmapOrDie(uptr size, const char *mem_type) { + void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (rv == 0) { + Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n", + size, size, mem_type); + CHECK("unable to mmap" && 0); + } + return rv; +} + +void UnmapOrDie(void *addr, uptr size) { + if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { + Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n", + size, size, addr); + CHECK("unable to unmap" && 0); + } +} + +void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { + // FIXME: is this really "NoReserve"? On Win32 this does not matter much, + // but on Win64 it does. + void *p = VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (p == 0) + Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at %p (%d)\n", + size, size, fixed_addr, GetLastError()); + return p; +} + +void *MmapFixedOrDie(uptr fixed_addr, uptr size) { + return MmapFixedNoReserve(fixed_addr, size); +} + +void *Mprotect(uptr fixed_addr, uptr size) { + return VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); +} + +void FlushUnneededShadowMemory(uptr addr, uptr size) { + // This is almost useless on 32-bits. + // FIXME: add madvice-analog when we move to 64-bits. +} + +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { + // FIXME: shall we do anything here on Windows? + return true; +} + +void *MapFileToMemory(const char *file_name, uptr *buff_size) { + UNIMPLEMENTED(); +} + +static const int kMaxEnvNameLength = 128; +static const DWORD kMaxEnvValueLength = 32767; + +namespace { + +struct EnvVariable { + char name[kMaxEnvNameLength]; + char value[kMaxEnvValueLength]; +}; + +} // namespace + +static const int kEnvVariables = 5; +static EnvVariable env_vars[kEnvVariables]; +static int num_env_vars; + +const char *GetEnv(const char *name) { + // Note: this implementation caches the values of the environment variables + // and limits their quantity. + for (int i = 0; i < num_env_vars; i++) { + if (0 == internal_strcmp(name, env_vars[i].name)) + return env_vars[i].value; + } + CHECK_LT(num_env_vars, kEnvVariables); + DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value, + kMaxEnvValueLength); + if (rv > 0 && rv < kMaxEnvValueLength) { + CHECK_LT(internal_strlen(name), kMaxEnvNameLength); + internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength); + num_env_vars++; + return env_vars[num_env_vars - 1].value; + } + return 0; +} + +const char *GetPwd() { + UNIMPLEMENTED(); +} + +u32 GetUid() { + UNIMPLEMENTED(); +} + +void DumpProcessMap() { + UNIMPLEMENTED(); +} + +void DisableCoreDumper() { + UNIMPLEMENTED(); +} + +void ReExec() { + UNIMPLEMENTED(); +} + +void PrepareForSandboxing() { + // Nothing here for now. +} + +bool StackSizeIsUnlimited() { + UNIMPLEMENTED(); +} + +void SetStackSizeLimitInBytes(uptr limit) { + UNIMPLEMENTED(); +} + +char *FindPathToBinary(const char *name) { + // Nothing here for now. + return 0; +} + +void SleepForSeconds(int seconds) { + Sleep(seconds * 1000); +} + +void SleepForMillis(int millis) { + Sleep(millis); +} + +u64 NanoTime() { + return 0; +} + +void Abort() { + abort(); + _exit(-1); // abort is not NORETURN on Windows. +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + UNIMPLEMENTED(); +}; + +#ifndef SANITIZER_GO +int Atexit(void (*function)(void)) { + return atexit(function); +} +#endif + +// ------------------ sanitizer_libc.h +uptr internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset) { + UNIMPLEMENTED(); +} + +uptr internal_munmap(void *addr, uptr length) { + UNIMPLEMENTED(); +} + +uptr internal_close(fd_t fd) { + UNIMPLEMENTED(); +} + +int internal_isatty(fd_t fd) { + return _isatty(fd); +} + +uptr internal_open(const char *filename, int flags) { + UNIMPLEMENTED(); +} + +uptr internal_open(const char *filename, int flags, u32 mode) { + UNIMPLEMENTED(); +} + +uptr OpenFile(const char *filename, bool write) { + UNIMPLEMENTED(); +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + UNIMPLEMENTED(); +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + if (fd != kStderrFd) + UNIMPLEMENTED(); + HANDLE err = GetStdHandle(STD_ERROR_HANDLE); + if (err == 0) + return 0; // FIXME: this might not work on some apps. + DWORD ret; + if (!WriteFile(err, buf, count, &ret, 0)) + return 0; + return ret; +} + +uptr internal_stat(const char *path, void *buf) { + UNIMPLEMENTED(); +} + +uptr internal_lstat(const char *path, void *buf) { + UNIMPLEMENTED(); +} + +uptr internal_fstat(fd_t fd, void *buf) { + UNIMPLEMENTED(); +} + +uptr internal_filesize(fd_t fd) { + UNIMPLEMENTED(); +} + +uptr internal_dup2(int oldfd, int newfd) { + UNIMPLEMENTED(); +} + +uptr internal_readlink(const char *path, char *buf, uptr bufsize) { + UNIMPLEMENTED(); +} + +uptr internal_sched_yield() { + Sleep(0); + return 0; +} + +void internal__exit(int exitcode) { + _exit(exitcode); +} + +// ---------------------- BlockingMutex ---------------- {{{1 +const uptr LOCK_UNINITIALIZED = 0; +const uptr LOCK_READY = (uptr)-1; + +BlockingMutex::BlockingMutex(LinkerInitialized li) { + // FIXME: see comments in BlockingMutex::Lock() for the details. + CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); + + CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); + InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + owner_ = LOCK_READY; +} + +BlockingMutex::BlockingMutex() { + CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); + InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + owner_ = LOCK_READY; +} + +void BlockingMutex::Lock() { + if (owner_ == LOCK_UNINITIALIZED) { + // FIXME: hm, global BlockingMutex objects are not initialized?!? + // This might be a side effect of the clang+cl+link Frankenbuild... + new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1)); + + // FIXME: If it turns out the linker doesn't invoke our + // constructors, we should probably manually Lock/Unlock all the global + // locks while we're starting in one thread to avoid double-init races. + } + EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + CHECK_EQ(owner_, LOCK_READY); + owner_ = GetThreadSelf(); +} + +void BlockingMutex::Unlock() { + CHECK_EQ(owner_, GetThreadSelf()); + owner_ = LOCK_READY; + LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); +} + +void BlockingMutex::CheckLocked() { + CHECK_EQ(owner_, GetThreadSelf()); +} + +uptr GetTlsSize() { + return 0; +} + +void InitTlsSize() { +} + +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size) { +#ifdef SANITIZER_GO + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#else + uptr stack_top, stack_bottom; + GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); + *stk_addr = stack_bottom; + *stk_size = stack_top - stack_bottom; + *tls_addr = 0; + *tls_size = 0; +#endif +} + +void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { + // FIXME: CaptureStackBackTrace might be too slow for us. + // FIXME: Compare with StackWalk64. + // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc + size = CaptureStackBackTrace(2, Min(max_depth, kStackTraceMax), + (void**)trace, 0); + // Skip the RTL frames by searching for the PC in the stacktrace. + uptr pc_location = LocatePcInTrace(pc); + PopStackFrames(pc_location); +} + +void MaybeOpenReportFile() { + // Windows doesn't have native fork, and we don't support Cygwin or other + // environments that try to fake it, so the initial report_fd will always be + // correct. +} + +void RawWrite(const char *buffer) { + static const char *kRawWriteError = + "RawWrite can't output requested buffer!\n"; + uptr length = (uptr)internal_strlen(buffer); + if (length != internal_write(report_fd, buffer, length)) { + // stderr may be closed, but we may be able to print to the debugger + // instead. This is the case when launching a program from Visual Studio, + // and the following routine should write to its console. + OutputDebugStringA(buffer); + } +} + +} // namespace __sanitizer + +#endif // _WIN32 diff --git a/projects/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh b/projects/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh new file mode 100755 index 00000000..5f1bd4ba --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# Guess path to LLVM_CHECKOUT if not provided +if [ "${LLVM_CHECKOUT}" == "" ]; then + LLVM_CHECKOUT="${SCRIPT_DIR}/../../../../../" +fi + +# Cpplint setup +CPPLINT=${SCRIPT_DIR}/cpplint.py +if [ "${PYTHON_EXECUTABLE}" != "" ]; then + CPPLINT="${PYTHON_EXECUTABLE} ${CPPLINT}" +fi + +# Filters +# TODO: remove some of these filters +LLVM_LINT_FILTER=-,+whitespace +COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\ +-build/namespaces +ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int +ASAN_TEST_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/sizeof,-runtime/int,-runtime/printf +ASAN_LIT_TEST_LINT_FILTER=${ASAN_TEST_LINT_FILTER},-whitespace/line_length +TSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} +TSAN_TEST_LINT_FILTER=${TSAN_RTL_LINT_FILTER},-runtime/threadsafe_fn,-runtime/int +TSAN_LIT_TEST_LINT_FILTER=${TSAN_TEST_LINT_FILTER},-whitespace/line_length +MSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} +LSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} +LSAN_LIT_TEST_LINT_FILTER=${LSAN_RTL_LINT_FILTER},-whitespace/line_length +COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/sizeof,-runtime/printf +SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int +MKTEMP="mktemp -q /tmp/tmp.XXXXXXXXXX" +cd ${LLVM_CHECKOUT} + +EXITSTATUS=0 +ERROR_LOG=$(${MKTEMP}) + +run_lint() { + FILTER=$1 + shift + TASK_LOG=$(${MKTEMP}) + ${CPPLINT} --filter=${FILTER} "$@" 2>$TASK_LOG + if [ "$?" != "0" ]; then + cat $TASK_LOG | grep -v "Done processing" | grep -v "Total errors found" \ + | grep -v "Skipping input" >> $ERROR_LOG + fi + if [[ "${SILENT}" != "1" ]]; then + cat $TASK_LOG + fi +} + +run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \ + lib/Transforms/Instrumentation/*Sanitizer.cpp \ + lib/Transforms/Utils/SpecialCaseList.cpp & + +COMPILER_RT=projects/compiler-rt +# Headers +SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer +run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h & + +# Sanitizer_common +COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common +run_lint ${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.{cc,h} \ + ${COMMON_RTL}/tests/*.cc & + +# Interception +INTERCEPTION=${COMPILER_RT}/lib/interception +run_lint ${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.{cc,h} & + +# ASan +ASAN_RTL=${COMPILER_RT}/lib/asan +run_lint ${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.{cc,h} & +run_lint ${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.{cc,h} & +run_lint ${ASAN_LIT_TEST_LINT_FILTER} ${ASAN_RTL}/lit_tests/*/*.cc & + +# TSan +TSAN_RTL=${COMPILER_RT}/lib/tsan +run_lint ${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.{cc,h} & +run_lint ${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.{cc,h} \ + ${TSAN_RTL}/tests/unit/*.cc & +run_lint ${TSAN_LIT_TEST_LINT_FILTER} ${TSAN_RTL}/lit_tests/*.cc & + +# MSan +MSAN_RTL=${COMPILER_RT}/lib/msan +run_lint ${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.{cc,h} & + +# LSan +LSAN_RTL=${COMPILER_RT}/lib/lsan +run_lint ${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.{cc,h} \ + ${LSAN_RTL}/tests/*.{cc,h} & +run_lint ${LSAN_LIT_TEST_LINT_FILTER} ${LSAN_RTL}/lit_tests/*/*.cc & + +# Misc files +FILES=${COMMON_RTL}/*.inc +TMPFILES="" +for FILE in $FILES; do + TMPFILE="$(${MKTEMP}).$(basename ${FILE}).cc" + cp -f $FILE $TMPFILE + run_lint ${COMMON_RTL_INC_LINT_FILTER} $TMPFILE & + TMPFILES="$TMPFILES $TMPFILE" +done + +wait + +for temp in $TMPFILES; do + rm -f $temp +done + +if [[ -s $ERROR_LOG ]]; then + cat $ERROR_LOG + exit 1 +fi + +exit 0 diff --git a/projects/compiler-rt/lib/sanitizer_common/scripts/cpplint.py b/projects/compiler-rt/lib/sanitizer_common/scripts/cpplint.py new file mode 100755 index 00000000..a8c9f678 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/scripts/cpplint.py @@ -0,0 +1,4024 @@ +#!/usr/bin/python +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Here are some issues that I've had people identify in my code during reviews, +# that I think are possible to flag automatically in a lint tool. If these were +# caught by lint, it would save time both for myself and that of my reviewers. +# Most likely, some of these are beyond the scope of the current lint framework, +# but I think it is valuable to retain these wish-list items even if they cannot +# be immediately implemented. +# +# Suggestions +# ----------- +# - Check for no 'explicit' for multi-arg ctor +# - Check for boolean assign RHS in parens +# - Check for ctor initializer-list colon position and spacing +# - Check that if there's a ctor, there should be a dtor +# - Check accessors that return non-pointer member variables are +# declared const +# - Check accessors that return non-const pointer member vars are +# *not* declared const +# - Check for using public includes for testing +# - Check for spaces between brackets in one-line inline method +# - Check for no assert() +# - Check for spaces surrounding operators +# - Check for 0 in pointer context (should be NULL) +# - Check for 0 in char context (should be '\0') +# - Check for camel-case method name conventions for methods +# that are not simple inline getters and setters +# - Do not indent namespace contents +# - Avoid inlining non-trivial constructors in header files +# - Check for old-school (void) cast for call-sites of functions +# ignored return value +# - Check gUnit usage of anonymous namespace +# - Check for class declaration order (typedefs, consts, enums, +# ctor(s?), dtor, friend declarations, methods, member vars) +# + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import copy +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] + [file] ... + + The style guidelines this tries to follow are those in + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. + + root=subdir + The root directory used for deriving header guard CPP variable. + By default, the header guard CPP variable is calculated as the relative + path to the directory that contains .git, .hg, or .svn. When this flag + is specified, the relative path is calculated from the specified + directory. If the specified directory does not exist, this flag is + ignored. + + Examples: + Assuing that src/.git exists, the header guard CPP variables for + src/chrome/browser/ui/browser.h are: + + No flag => CHROME_BROWSER_UI_BROWSER_H_ + --root=chrome => BROWSER_UI_BROWSER_H_ + --root=chrome/browser => UI_BROWSER_H_ +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +# \ used for clearer layout -- pylint: disable-msg=C6013 +_ERROR_CATEGORIES = [ + 'build/class', + 'build/deprecated', + 'build/endif_comment', + 'build/explicit_make_pair', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/alt_tokens', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/function', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/namespace', + 'readability/nolint', + 'readability/streams', + 'readability/todo', + 'readability/utf8', + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/rtti', + 'runtime/sizeof', + 'runtime/string', + 'runtime/threadsafe_fn', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/empty_loop_body', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/forcolon', + 'whitespace/indent', + 'whitespace/labels', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo' + ] + +# The default state of the category filter. This is overrided by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = ['-build/include_alpha'] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a separate i18n file. + +# Headers that we consider STL headers. +_STL_HEADERS = frozenset([ + 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', + 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', + 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', + 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', + 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', + 'utility', 'vector', 'vector.h', + ]) + + +# Non-STL C++ system headers. +_CPP_HEADERS = frozenset([ + 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', + 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', + 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', + 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', + 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', + 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', + 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', + 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', + 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', + 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', + 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', + 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', + 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', + 'valarray', + ]) + + +# Assertion macros. These are defined in base/logging.h and +# testing/base/gunit.h. Note that the _M versions need to come first +# for substring matching to work. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE_M', 'EXPECT_TRUE', + 'ASSERT_TRUE_M', 'ASSERT_TRUE', + 'EXPECT_FALSE_M', 'EXPECT_FALSE', + 'ASSERT_FALSE_M', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement + +# Alternative tokens and their replacements. For full list, see section 2.5 +# Alternative tokens [lex.digraph] in the C++ standard. +# +# Digraphs (such as '%:') are not included here since it's a mess to +# match those on a word boundary. +_ALT_TOKEN_REPLACEMENT = { + 'and': '&&', + 'bitor': '|', + 'or': '||', + 'xor': '^', + 'compl': '~', + 'bitand': '&', + 'and_eq': '&=', + 'or_eq': '|=', + 'xor_eq': '^=', + 'not': '!', + 'not_eq': '!=' + } + +# Compile regular expression that matches all the above keywords. The "[ =()]" +# bit is meant to avoid matching these keywords outside of boolean expressions. +# +# False positives include C-style multi-line comments (http://go/nsiut ) +# and multi-line strings (http://go/beujw ), but those have always been +# troublesome for cpplint. +_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( + r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + +# These constants define the current inline assembly state +_NO_ASM = 0 # Outside of inline assembly block +_INSIDE_ASM = 1 # Inside inline assembly block +_END_ASM = 2 # Last line of inline assembly block +_BLOCK_ASM = 3 # The whole block is an inline assembly block + +# Match start of assembly blocks +_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' + r'(?:\s+(volatile|__volatile__))?' + r'\s*[{(]') + + +_regexp_compile_cache = {} + +# Finds occurrences of NOLINT or NOLINT(...). +_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +# The root directory used for deriving header guard CPP variable. +# This is set by --root flag. +_root = None + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). + matched = _RE_SUPPRESSION.search(raw_line) + if matched: + category = matched.group(1) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(linenum) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(linenum) + else: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ResetNolintSuppressions(): + "Resets the set of NOLINT suppressions to empty." + _error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment. + """ + return (linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +class _IncludeState(dict): + """Tracks line numbers for includes, and the order in which includes appear. + + As a dict, an _IncludeState object serves as a mapping between include + filename and line number on which that file was included. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + dict.__init__(self) + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + header_path: Header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) + if self._last_header > canonical_header: + return False + self._last_header = canonical_header + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analyzing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo: + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by + # searching up from the current path. + root_dir = os.path.dirname(fullname) + while (root_dir != os.path.dirname(root_dir) and + not os.path.exists(os.path.join(root_dir, ".git")) and + not os.path.exists(os.path.join(root_dir, ".hg")) and + not os.path.exists(os.path.join(root_dir, ".svn"))): + root_dir = os.path.dirname(root_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg")) or + os.path.exists(os.path.join(root_dir, ".svn"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') + + +def _ShouldPrintError(category, confidence, linenum): + """If confidence >= verbose, category passes filter and is not suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + elif _cpplint_state.output_format == 'eclipse': + sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Matches strings. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') +# Matches characters. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") +# Matches multi-line C++ comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r"""(\s*/\*.*\*/\s*$| + /\*.*\*/\s+| + \s+/\*.*\*/(?=\W)| + /\*.*\*/)""", re.VERBOSE) + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '// dummy' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos].rstrip() + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 3 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments, + 2) lines member contains lines without comments, and + 3) raw_lines member contains all the lines without processing. + All these three members are of , and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + for linenum in range(len(lines)): + self.lines.append(CleanseComments(lines[linenum])) + elided = self._CollapseStrings(lines[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if not _RE_PATTERN_INCLUDE.match(elided): + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) + elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) + return elided + + +def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): + """Find the position just after the matching endchar. + + Args: + line: a CleansedLines line. + startpos: start searching at this position. + depth: nesting level at startpos. + startchar: expression opening character. + endchar: expression closing character. + + Returns: + Index just after endchar. + """ + for i in xrange(startpos, len(line)): + if line[i] == startchar: + depth += 1 + elif line[i] == endchar: + depth -= 1 + if depth == 0: + return i + 1 + return -1 + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[', finds the + linenum/pos that correspond to the closing of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + startchar = line[pos] + if startchar not in '({[': + return (line, clean_lines.NumLines(), -1) + if startchar == '(': endchar = ')' + if startchar == '[': endchar = ']' + if startchar == '{': endchar = '}' + + # Check first line + end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) + if end_pos > -1: + return (line, linenum, end_pos) + tail = line[pos:] + num_open = tail.count(startchar) - tail.count(endchar) + while linenum < clean_lines.NumLines() - 1: + linenum += 1 + line = clean_lines.elided[linenum] + delta = line.count(startchar) - line.count(endchar) + if num_open + delta <= 0: + return (line, linenum, + FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) + num_open += delta + + # Did not find endchar before end of file, give up + return (line, clean_lines.NumLines(), -1) + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Copyright', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Copyright [year] "') + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) + + fileinfo = FileInfo(filename) + file_path_from_root = fileinfo.RepositoryName() + if _root: + file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) + return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' + + +def CheckForHeaderGuard(filename, lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = None + ifndef_linenum = 0 + define = None + endif = None + endif_linenum = 0 + for linenum, line in enumerate(lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + if not define: + error(filename, 0, 'build/header_guard', 5, + 'No #define header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + if define != ifndef: + error(filename, 0, 'build/header_guard', 5, + '#ifndef and #define don\'t match, suggested CPP variable is: %s' % + cppvar) + return + + if endif != ('#endif // %s' % cppvar): + error_level = 0 + if endif != ('#endif // %s' % (cppvar + '_')): + error_level = 5 + + ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, + error) + error(filename, endif_linenum, 'build/header_guard', error_level, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckForUnicodeReplacementCharacters(filename, lines, error): + """Logs an error for each line containing Unicode replacement characters. + + These indicate that either the file contained invalid UTF-8 (likely) + or Unicode replacement characters (which it shouldn't). Note that + it's possible for this to throw off line numbering if the invalid + UTF-8 occurred adjacent to a newline. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. They\'re ' + 'ugly and unnecessary, and you should use concatenation instead".') + + +threading_list = ( + ('asctime(', 'asctime_r('), + ('ctime(', 'ctime_r('), + ('getgrgid(', 'getgrgid_r('), + ('getgrnam(', 'getgrnam_r('), + ('getlogin(', 'getlogin_r('), + ('getpwnam(', 'getpwnam_r('), + ('getpwuid(', 'getpwuid_r('), + ('gmtime(', 'gmtime_r('), + ('localtime(', 'localtime_r('), + ('rand(', 'rand_r('), + ('readdir(', 'readdir_r('), + ('strtok(', 'strtok_r('), + ('ttyname(', 'ttyname_r('), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_function, multithread_safe_function in threading_list: + ix = line.find(single_thread_function) + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and + line[ix - 1] not in ('_', '.', '>'))): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_function + + '...) instead of ' + single_thread_function + + '...) for improved thread safety.') + + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +class _BlockInfo(object): + """Stores information about a generic block of code.""" + + def __init__(self, seen_open_brace): + self.seen_open_brace = seen_open_brace + self.open_parentheses = 0 + self.inline_asm = _NO_ASM + + def CheckBegin(self, filename, clean_lines, linenum, error): + """Run checks that applies to text up to the opening brace. + + This is mostly for checking the text after the class identifier + and the "{", usually where the base class is specified. For other + blocks, there isn't much to check, so we always pass. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Run checks that applies to text after the closing brace. + + This is mostly used for checking end of namespace comments. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + +class _ClassInfo(_BlockInfo): + """Stores information about a class.""" + + def __init__(self, name, class_or_struct, clean_lines, linenum): + _BlockInfo.__init__(self, False) + self.name = name + self.starting_linenum = linenum + self.is_derived = False + if class_or_struct == 'struct': + self.access = 'public' + else: + self.access = 'private' + + # Try to find the end of the class. This will be confused by things like: + # class A { + # } *x = { ... + # + # But it's still good enough for CheckSectionSpacing. + self.last_line = 0 + depth = 0 + for i in range(linenum, clean_lines.NumLines()): + line = clean_lines.elided[i] + depth += line.count('{') - line.count('}') + if not depth: + self.last_line = i + break + + def CheckBegin(self, filename, clean_lines, linenum, error): + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): + self.is_derived = True + + +class _NamespaceInfo(_BlockInfo): + """Stores information about a namespace.""" + + def __init__(self, name, linenum): + _BlockInfo.__init__(self, False) + self.name = name or '' + self.starting_linenum = linenum + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Check end of namespace comments.""" + line = clean_lines.raw_lines[linenum] + + # Check how many lines is enclosed in this namespace. Don't issue + # warning for missing namespace comments if there aren't enough + # lines. However, do apply checks if there is already an end of + # namespace comment and it's incorrect. + # + # TODO(unknown): We always want to check end of namespace comments + # if a namespace is large, but sometimes we also want to apply the + # check if a short namespace contained nontrivial things (something + # other than forward declarations). There is currently no logic on + # deciding what these nontrivial things are, so this check is + # triggered by namespace size only, which works most of the time. + if (linenum - self.starting_linenum < 10 + and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): + return + + # Look for matching comment at end of namespace. + # + # Note that we accept C style "/* */" comments for terminating + # namespaces, so that code that terminate namespaces inside + # preprocessor macros can be cpplint clean. Example: http://go/nxpiz + # + # We also accept stuff like "// end of namespace ." with the + # period at the end. + # + # Besides these, we don't accept anything else, otherwise we might + # get false negatives when existing comment is a substring of the + # expected namespace. Example: http://go/ldkdc, http://cl/23548205 + if self.name: + # Named namespace + if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + + r'[\*/\.\\\s]*$'), + line): + error(filename, linenum, 'readability/namespace', 5, + 'Namespace should be terminated with "// namespace %s"' % + self.name) + else: + # Anonymous namespace + if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): + error(filename, linenum, 'readability/namespace', 5, + 'Namespace should be terminated with "// namespace"') + + +class _PreprocessorInfo(object): + """Stores checkpoints of nesting stacks when #if/#else is seen.""" + + def __init__(self, stack_before_if): + # The entire nesting stack before #if + self.stack_before_if = stack_before_if + + # The entire nesting stack up to #else + self.stack_before_else = [] + + # Whether we have already seen #else or #elif + self.seen_else = False + + +class _NestingState(object): + """Holds states related to parsing braces.""" + + def __init__(self): + # Stack for tracking all braces. An object is pushed whenever we + # see a "{", and popped when we see a "}". Only 3 types of + # objects are possible: + # - _ClassInfo: a class or struct. + # - _NamespaceInfo: a namespace. + # - _BlockInfo: some other type of block. + self.stack = [] + + # Stack of _PreprocessorInfo objects. + self.pp_stack = [] + + def SeenOpenBrace(self): + """Check if we have seen the opening brace for the innermost block. + + Returns: + True if we have seen the opening brace, False if the innermost + block is still expecting an opening brace. + """ + return (not self.stack) or self.stack[-1].seen_open_brace + + def InNamespaceBody(self): + """Check if we are currently one level inside a namespace body. + + Returns: + True if top of the stack is a namespace block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _NamespaceInfo) + + def UpdatePreprocessor(self, line): + """Update preprocessor stack. + + We need to handle preprocessors due to classes like this: + #ifdef SWIG + struct ResultDetailsPageElementExtensionPoint { + #else + struct ResultDetailsPageElementExtensionPoint : public Extension { + #endif + (see http://go/qwddn for original example) + + We make the following assumptions (good enough for most files): + - Preprocessor condition evaluates to true from #if up to first + #else/#elif/#endif. + + - Preprocessor condition evaluates to false from #else/#elif up + to #endif. We still perform lint checks on these lines, but + these do not affect nesting stack. + + Args: + line: current line to check. + """ + if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + # Beginning of #if block, save the nesting stack here. The saved + # stack will allow us to restore the parsing state in the #else case. + self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) + elif Match(r'^\s*#\s*(else|elif)\b', line): + # Beginning of #else block + if self.pp_stack: + if not self.pp_stack[-1].seen_else: + # This is the first #else or #elif block. Remember the + # whole nesting stack up to this point. This is what we + # keep after the #endif. + self.pp_stack[-1].seen_else = True + self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) + + # Restore the stack to how it was before the #if + self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) + else: + # TODO(unknown): unexpected #else, issue warning? + pass + elif Match(r'^\s*#\s*endif\b', line): + # End of #if or #else blocks. + if self.pp_stack: + # If we saw an #else, we will need to restore the nesting + # stack to its former state before the #else, otherwise we + # will just continue from where we left off. + if self.pp_stack[-1].seen_else: + # Here we can just use a shallow copy since we are the last + # reference to it. + self.stack = self.pp_stack[-1].stack_before_else + # Drop the corresponding #if + self.pp_stack.pop() + else: + # TODO(unknown): unexpected #endif, issue warning? + pass + + def Update(self, filename, clean_lines, linenum, error): + """Update nesting state with current line. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Update pp_stack first + self.UpdatePreprocessor(line) + + # Count parentheses. This is to avoid adding struct arguments to + # the nesting stack. + if self.stack: + inner_block = self.stack[-1] + depth_change = line.count('(') - line.count(')') + inner_block.open_parentheses += depth_change + + # Also check if we are starting or ending an inline assembly block. + if inner_block.inline_asm in (_NO_ASM, _END_ASM): + if (depth_change != 0 and + inner_block.open_parentheses == 1 and + _MATCH_ASM.match(line)): + # Enter assembly block + inner_block.inline_asm = _INSIDE_ASM + else: + # Not entering assembly block. If previous line was _END_ASM, + # we will now shift to _NO_ASM state. + inner_block.inline_asm = _NO_ASM + elif (inner_block.inline_asm == _INSIDE_ASM and + inner_block.open_parentheses == 0): + # Exit assembly block + inner_block.inline_asm = _END_ASM + + # Consume namespace declaration at the beginning of the line. Do + # this in a loop so that we catch same line declarations like this: + # namespace proto2 { namespace bridge { class MessageSet; } } + while True: + # Match start of namespace. The "\b\s*" below catches namespace + # declarations even if it weren't followed by a whitespace, this + # is so that we don't confuse our namespace checker. The + # missing spaces will be flagged by CheckSpacing. + namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) + if not namespace_decl_match: + break + + new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) + self.stack.append(new_namespace) + + line = namespace_decl_match.group(2) + if line.find('{') != -1: + new_namespace.seen_open_brace = True + line = line[line.find('{') + 1:] + + # Look for a class declaration in whatever is left of the line + # after parsing namespaces. The regexp accounts for decorated classes + # such as in: + # class LOCKABLE API Object { + # }; + # + # Templates with class arguments may confuse the parser, for example: + # template , + # class Vector = vector > + # class HeapQueue { + # + # Because this parser has no nesting state about templates, by the + # time it saw "class Comparator", it may think that it's a new class. + # Nested templates have a similar problem: + # template < + # typename ExportedType, + # typename TupleType, + # template class ImplTemplate> + # + # To avoid these cases, we ignore classes that are followed by '=' or '>' + class_decl_match = Match( + r'\s*(template\s*<[\w\s<>,:]*>\s*)?' + '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' + '(([^=>]|<[^<>]*>)*)$', line) + if (class_decl_match and + (not self.stack or self.stack[-1].open_parentheses == 0)): + self.stack.append(_ClassInfo( + class_decl_match.group(4), class_decl_match.group(2), + clean_lines, linenum)) + line = class_decl_match.group(5) + + # If we have not yet seen the opening brace for the innermost block, + # run checks here. + if not self.SeenOpenBrace(): + self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) + + # Update access control if we are inside a class/struct + if self.stack and isinstance(self.stack[-1], _ClassInfo): + access_match = Match(r'\s*(public|private|protected)\s*:', line) + if access_match: + self.stack[-1].access = access_match.group(1) + + # Consume braces or semicolons from what's left of the line + while True: + # Match first brace, semicolon, or closed parenthesis. + matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + if not matched: + break + + token = matched.group(1) + if token == '{': + # If namespace or class hasn't seen a opening brace yet, mark + # namespace/class head as complete. Push a new block onto the + # stack otherwise. + if not self.SeenOpenBrace(): + self.stack[-1].seen_open_brace = True + else: + self.stack.append(_BlockInfo(True)) + if _MATCH_ASM.match(line): + self.stack[-1].inline_asm = _BLOCK_ASM + elif token == ';' or token == ')': + # If we haven't seen an opening brace yet, but we already saw + # a semicolon, this is probably a forward declaration. Pop + # the stack for these. + # + # Similarly, if we haven't seen an opening brace yet, but we + # already saw a closing parenthesis, then these are probably + # function arguments with extra "class" or "struct" keywords. + # Also pop these stack for these. + if not self.SeenOpenBrace(): + self.stack.pop() + else: # token == '}' + # Perform end of block checks and pop the stack. + if self.stack: + self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) + self.stack.pop() + line = matched.group(2) + + def InnermostClass(self): + """Get class info on the top of the stack. + + Returns: + A _ClassInfo object if we are inside a class, or None otherwise. + """ + for i in range(len(self.stack), 0, -1): + classinfo = self.stack[i - 1] + if isinstance(classinfo, _ClassInfo): + return classinfo + return None + + def CheckClassFinished(self, filename, error): + """Checks that all classes have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + for obj in self.stack: + if isinstance(obj, _ClassInfo): + error(filename, obj.starting_linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + obj.name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + nesting_state, error): + """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Everything else in this function operates on class declarations. + # Return early if the top of the nesting stack is not a class, or if + # the class head is not completed yet. + classinfo = nesting_state.InnermostClass() + if not classinfo or not classinfo.seen_open_brace: + return + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. + args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' + % re.escape(base_classname), + line) + if (args and + args.group(1) != 'void' and + not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), + args.group(1).strip())): + error(filename, linenum, 'runtime/explicit', 5, + 'Single-argument constructors should be marked explicit.') + + +def CheckSpacingForFunctionCall(filename, line, linenum, error): + """Checks for the correctness of various spacing around function calls. + + Args: + filename: The name of the current file. + line: The text of the line to check. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Since function calls often occur inside if/for/while/switch + # expressions - which have their own, more liberal conventions - we + # first see if we should be looking inside such an expression for a + # function call, to which we can apply more strict standards. + fncall = line # if there's no control flow construct, look at whole line + for pattern in (r'\bif\s*\((.*)\)\s*{', + r'\bfor\s*\((.*)\)\s*{', + r'\bwhile\s*\((.*)\)\s*[{;]', + r'\bswitch\s*\((.*)\)\s*{'): + match = Search(pattern, line) + if match: + fncall = match.group(1) # look inside the parens for function calls + break + + # Except in if/for/while/switch, there should never be space + # immediately inside parens (eg "f( 3, 4 )"). We make an exception + # for nested parens ( (a+b) + c ). Likewise, there should never be + # a space before a ( when it's a function argument. I assume it's a + # function argument when the char before the whitespace is legal in + # a function name (alnum + _) and we're not starting a macro. Also ignore + # pointers and references to arrays and functions coz they're too tricky: + # we use a very simple way to recognize these: + # " (something)(maybe-something)" or + # " (something)(maybe-something," or + # " (something)[something]" + # Note that we assume the contents of [] to be short enough that + # they'll never need to wrap. + if ( # Ignore control structures. + not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and + # Ignore pointers/references to functions. + not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + # Ignore pointers/references to arrays. + not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): + if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space after ( in function call') + elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space after (') + if (Search(r'\w\s+\(', fncall) and + not Search(r'#\s*define|typedef', fncall) and + not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') + # If the ) is followed only by a newline or a { + newline, assume it's + # part of a control statement (if/while/etc), and don't complain + if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') + + +def IsBlankLine(line): + """Returns true if the given line is blank. + + We consider a line to be blank if the line is empty or consists of + only white spaces. + + Args: + line: A line of a string. + + Returns: + True, if the given line is blank. + """ + return not line or line.isspace() + + +def CheckForFunctionLengths(filename, clean_lines, linenum, + function_state, error): + """Reports for long function bodies. + + For an overview why this is done, see: + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Only checks unindented functions, so class members are unchecked. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + Blank/comment lines are not counted so as to avoid encouraging the removal + of vertical space and comments just to get through a lint check. + NOLINT *on the last line of a function* disables this check. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + lines = clean_lines.lines + line = lines[linenum] + raw = clean_lines.raw_lines + raw_line = raw[linenum] + joined_line = '' + + starting_func = False + regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... + match_result = Match(regexp, line) + if match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, clean_lines.NumLines()): + start_line = lines[start_linenum] + joined_line += ' ' + start_line.lstrip() + if Search(r'(;|})', start_line): # Declarations and trivial functions + body_found = True + break # ... ignore + elif Search(r'{', start_line): + body_found = True + function = Search(r'((\w|:)*)\(', line).group(1) + if Match(r'TEST', function): # Handle TEST... macros + parameter_regexp = Search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + function_state.Begin(function) + break + if not body_found: + # No body for the function (or evidence of a non-function) was found. + error(filename, linenum, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + elif Match(r'^\}\s*$', line): # function end + function_state.Check(error, filename, linenum) + function_state.End() + elif not Match(r'^\s*$', line): + function_state.Count() # Count non-blank/non-comment lines. + + +_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') + + +def CheckComment(comment, filename, linenum, error): + """Checks for common mistakes in TODO comments. + + Args: + comment: The text of the comment from the line in question. + filename: The name of the current file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + +def CheckAccess(filename, clean_lines, linenum, nesting_state, error): + """Checks for improper use of DISALLOW* macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] # get rid of comments and strings + + matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' + r'DISALLOW_EVIL_CONSTRUCTORS|' + r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) + if not matched: + return + if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): + if nesting_state.stack[-1].access != 'private': + error(filename, linenum, 'readability/constructors', 3, + '%s must be in the private: section' % matched.group(1)) + + else: + # Found DISALLOW* macro outside a class declaration, or perhaps it + # was used inside a function when it should have been part of the + # class declaration. We could issue a warning here, but it + # probably resulted in a compiler error already. + pass + + +def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): + """Find the corresponding > to close a template. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + init_suffix: Remainder of the current line after the initial <. + + Returns: + True if a matching bracket exists. + """ + line = init_suffix + nesting_stack = ['<'] + while True: + # Find the next operator that can tell us whether < is used as an + # opening bracket or as a less-than operator. We only want to + # warn on the latter case. + # + # We could also check all other operators and terminate the search + # early, e.g. if we got something like this "a(),;\[\]]*([<>(),;\[\]])(.*)$', line) + if match: + # Found an operator, update nesting stack + operator = match.group(1) + line = match.group(2) + + if nesting_stack[-1] == '<': + # Expecting closing angle bracket + if operator in ('<', '(', '['): + nesting_stack.append(operator) + elif operator == '>': + nesting_stack.pop() + if not nesting_stack: + # Found matching angle bracket + return True + elif operator == ',': + # Got a comma after a bracket, this is most likely a template + # argument. We have not seen a closing angle bracket yet, but + # it's probably a few lines later if we look for it, so just + # return early here. + return True + else: + # Got some other operator. + return False + + else: + # Expecting closing parenthesis or closing bracket + if operator in ('<', '(', '['): + nesting_stack.append(operator) + elif operator in (')', ']'): + # We don't bother checking for matching () or []. If we got + # something like (] or [), it would have been a syntax error. + nesting_stack.pop() + + else: + # Scan the next line + linenum += 1 + if linenum >= len(clean_lines.elided): + break + line = clean_lines.elided[linenum] + + # Exhausted all remaining lines and still no matching angle bracket. + # Most likely the input was incomplete, otherwise we should have + # seen a semicolon and returned early. + return True + + +def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): + """Find the corresponding < that started a template. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: Current line number. + init_prefix: Part of the current line before the initial >. + + Returns: + True if a matching bracket exists. + """ + line = init_prefix + nesting_stack = ['>'] + while True: + # Find the previous operator + match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) + if match: + # Found an operator, update nesting stack + operator = match.group(2) + line = match.group(1) + + if nesting_stack[-1] == '>': + # Expecting opening angle bracket + if operator in ('>', ')', ']'): + nesting_stack.append(operator) + elif operator == '<': + nesting_stack.pop() + if not nesting_stack: + # Found matching angle bracket + return True + elif operator == ',': + # Got a comma before a bracket, this is most likely a + # template argument. The opening angle bracket is probably + # there if we look for it, so just return early here. + return True + else: + # Got some other operator. + return False + + else: + # Expecting opening parenthesis or opening bracket + if operator in ('>', ')', ']'): + nesting_stack.append(operator) + elif operator in ('(', '['): + nesting_stack.pop() + + else: + # Scan the previous line + linenum -= 1 + if linenum < 0: + break + line = clean_lines.elided[linenum] + + # Exhausted all earlier lines and still no matching angle bracket. + return False + + +def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + raw = clean_lines.raw_lines + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + # + # Skip all the blank line checks if we are immediately inside a + # namespace body. In other words, don't issue blank line warnings + # for this block: + # namespace { + # + # } + # + # A warning about missing end of namespace comments will be issued instead. + if IsBlankLine(line) and not nesting_state.InNamespaceBody(): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the parameters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Blank line at the start of a code block. Is this needed?') + # Ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Blank line at the end of a code block. Is this needed?') + + matched = Match(r'\s*(public|protected|private):', prev_line) + if matched: + error(filename, linenum, 'whitespace/blank_line', 3, + 'Do not leave a blank line after "%s:"' % matched.group(1)) + + # Next, we complain if there's a comment too near the text + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if (line.count('"', 0, commentpos) - + line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes + # Allow one space for new scopes, two spaces otherwise: + if (not Match(r'^\s*{ //', line) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace) or + (commentpos >= 2 and + line[commentpos-2] not in string.whitespace))): + error(filename, linenum, 'whitespace/comments', 2, + 'At least two spaces is best between code and comments') + # There should always be a space between the // and the comment + commentend = commentpos + 2 + if commentend < len(line) and not line[commentend] == ' ': + # but some lines are exceptions -- e.g. if they're big + # comment delimiters like: + # //---------------------------------------------------------- + # or are an empty C++ style Doxygen comment, like: + # /// + # or they begin with multiple slashes followed by a space: + # //////// Header comment + match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or + Search(r'^/$', line[commentend:]) or + Search(r'^/+ ', line[commentend:])) + if not match: + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + CheckComment(line[commentpos:], filename, linenum, error) + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Don't try to do spacing checks for operator methods + line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Missing spaces around =') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # + # Check <= and >= first to avoid false positives with < and >, then + # check non-include lines for spacing around < and >. + match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + # We allow no-spaces around << when used like this: 10<<20, but + # not otherwise (particularly, not when used as streams) + match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) + if match and not (match.group(1).isdigit() and match.group(2).isdigit()): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <<') + elif not Match(r'#.*include', line): + # Avoid false positives on -> + reduced_line = line.replace('->', '') + + # Look for < that is not surrounded by spaces. This is only + # triggered if both sides are missing spaces, even though + # technically should should flag if at least one side is missing a + # space. This is done to avoid some false positives with shifts. + match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) + if (match and + not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around <') + + # Look for > that is not surrounded by spaces. Similar to the + # above, we only trigger if both sides are missing spaces to avoid + # false positives with shifts. + match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) + if (match and + not FindPreviousMatchingAngleBracket(clean_lines, linenum, + match.group(1))): + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >') + + # We allow no-spaces around >> for almost anything. This is because + # C++11 allows ">>" to close nested templates, which accounts for + # most cases when ">>" is not followed by a space. + # + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha + # + # When ">>" is used to close templates, the alphanumeric letter that + # follows would be part of an identifier, and there should still be + # a space separating the template type and the identifier. + # type> alpha + match = Search(r'>>[a-zA-Z_]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >>') + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + + # A pet peeve of mine: no spaces after an if, while, switch, or for + match = Search(r' (if\(|for\(|while\(|switch\()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Missing space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if not len(match.group(2)) in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + # You should always have a space after a comma (either as fn arg or operator) + if Search(r',[^\s]', line): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + # Next we will look for issues with function calls. + CheckSpacingForFunctionCall(filename, line, linenum, error) + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces. And since you should never have braces at the beginning of a line, + # this is an easy test. + if Search(r'[^ ({]{', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + if Search(r'}else', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before else') + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'new char * []'. + if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use {} instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use {} instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use {} instead.') + + # In range-based for, we wanted spaces before and after the colon, but + # not around "::" tokens that might appear. + if (Search('for *\(.*[^:]:[^: ]', line) or + Search('for *\(.*[^: ]:[^:]', line)): + error(filename, linenum, 'whitespace/forcolon', 2, + 'Missing space around colon in range-based for loop') + + +def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): + """Checks for additional blank line issues related to sections. + + Currently the only thing checked here is blank line before protected/private. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + class_info: A _ClassInfo objects. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Skip checks if the class is small, where small means 25 lines or less. + # 25 lines seems like a good cutoff since that's the usual height of + # terminals, and any class that can't fit in one screen can't really + # be considered "small". + # + # Also skip checks if we are on the first line. This accounts for + # classes that look like + # class Foo { public: ... }; + # + # If we didn't find the end of the class, last_line would be zero, + # and the check will be skipped by the first condition. + if (class_info.last_line - class_info.starting_linenum <= 24 or + linenum <= class_info.starting_linenum): + return + + matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + if matched: + # Issue warning if the line before public/protected/private was + # not a blank line, but don't do this if the previous line contains + # "class" or "struct". This can happen two ways: + # - We are at the beginning of the class. + # - We are forward-declaring an inner class that is semantically + # private, but needed to be public for implementation reasons. + # Also ignores cases where the previous line ends with a backslash as can be + # common when defining classes in C macros. + prev_line = clean_lines.lines[linenum - 1] + if (not IsBlankLine(prev_line) and + not Search(r'\b(class|struct)\b', prev_line) and + not Search(r'\\$', prev_line)): + # Try a bit harder to find the beginning of the class. This is to + # account for multi-line base-specifier lists, e.g.: + # class Derived + # : public Base { + end_class_head = class_info.starting_linenum + for i in range(class_info.starting_linenum, linenum): + if Search(r'\{\s*$', clean_lines.lines[i]): + end_class_head = i + break + if end_class_head < linenum - 1: + error(filename, linenum, 'whitespace/blank_line', 3, + '"%s:" should be preceded by a blank line' % matched.group(1)) + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + if Match(r'\s*{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, + # which is commonly used to control the lifetime of + # stack-allocated variables. We don't detect this perfectly: we + # just don't complain if the last non-whitespace character on the + # previous non-blank line is ';', ':', '{', or '}', or if the previous + # line starts a preprocessor block. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if (not Search(r'[;:}{]\s*$', prevline) and + not Match(r'\s*#', prevline)): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end of the previous line') + + # An else clause should be on the same line as the preceding closing brace. + if Match(r'\s*else\s*', line): + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if Match(r'\s*}\s*$', prevline): + error(filename, linenum, 'whitespace/newline', 4, + 'An else should appear on the same line as the preceding }') + + # If braces come on one side of an else, they should be on both. + # However, we have to worry about "else if" that spans multiple lines! + if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) + if endline[endpos:].find('{') == -1: # must be brace after if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + else: # common case: else not followed by a multi-line if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + + # Likewise, an else should never have the else clause on the same line + if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + error(filename, linenum, 'whitespace/newline', 4, + 'Else clause should never be on same line as else (use 2 lines)') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Braces shouldn't be followed by a ; unless they're defining a struct + # or initializing an array. + # We can't tell in general, but we can for some common cases. + prevlinenum = linenum + while True: + (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) + if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): + line = prevline + line + else: + break + if (Search(r'{.*}\s*;', line) and + line.count('{') == line.count('}') and + not Search(r'struct|class|enum|\s*=\s*{', line)): + error(filename, linenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def CheckEmptyLoopBody(filename, clean_lines, linenum, error): + """Loop for empty loop body with only a single semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Search for loop keywords at the beginning of the line. Because only + # whitespaces are allowed before the keywords, this will also ignore most + # do-while-loops, since those lines should start with closing brace. + line = clean_lines.elided[linenum] + if Match(r'\s*(for|while)\s*\(', line): + # Find the end of the conditional expression + (end_line, end_linenum, end_pos) = CloseExpression( + clean_lines, linenum, line.find('(')) + + # Output warning if what follows the condition expression is a semicolon. + # No warning for all other cases, including whitespace or newline, since we + # have a separate check for semicolons preceded by whitespace. + if end_pos >= 0 and Match(r';', end_line[end_pos:]): + error(filename, end_linenum, 'whitespace/empty_loop_body', 5, + 'Empty loop bodies should use {} or continue') + + +def ReplaceableCheck(operator, macro, line): + """Determine whether a basic CHECK can be replaced with a more specific one. + + For example suggest using CHECK_EQ instead of CHECK(a == b) and + similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. + + Args: + operator: The C++ operator used in the CHECK. + macro: The CHECK or EXPECT macro being called. + line: The current source line. + + Returns: + True if the CHECK can be replaced with a more specific one. + """ + + # This matches decimal and hex integers, strings, and chars (in that order). + match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' + + # Expression to match two sides of the operator with something that + # looks like a literal, since CHECK(x == iterator) won't compile. + # This means we can't catch all the cases where a more specific + # CHECK is possible, but it's less annoying than dealing with + # extraneous warnings. + match_this = (r'\s*' + macro + r'\((\s*' + + match_constant + r'\s*' + operator + r'[^<>].*|' + r'.*[^<>]' + operator + r'\s*' + match_constant + + r'\s*\))') + + # Don't complain about CHECK(x == NULL) or similar because + # CHECK_EQ(x, NULL) won't compile (requires a cast). + # Also, don't complain about more complex boolean expressions + # involving && or || such as CHECK(a == b || c == d). + return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + raw_lines = clean_lines.raw_lines + current_macro = '' + for macro in _CHECK_MACROS: + if raw_lines[linenum].find(macro) >= 0: + current_macro = macro + break + if not current_macro: + # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' + return + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. + for operator in ['==', '!=', '>=', '>', '<=', '<']: + if ReplaceableCheck(operator, current_macro, line): + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[current_macro][operator], + current_macro, operator)) + break + + +def CheckAltTokens(filename, clean_lines, linenum, error): + """Check alternative keywords being used in boolean expressions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Avoid preprocessor lines + if Match(r'^\s*#', line): + return + + # Last ditch effort to avoid multi-line comments. This will not help + # if the comment started before the current line or ended after the + # current line, but it catches most of the false positives. At least, + # it provides a way to workaround this warning for people who use + # multi-line comments in preprocessor macros. + # + # TODO(unknown): remove this once cpplint has better support for + # multi-line comments. + if line.find('/*') >= 0 or line.find('*/') >= 0: + return + + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + error(filename, linenum, 'readability/alt_tokens', 2, + 'Use operator %s instead of %s' % ( + _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(uc): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, + error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + raw_lines = clean_lines.raw_lines + line = raw_lines[linenum] + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found; better to use spaces') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + # There are certain situations we allow one space, notably for labels + elif ((initial_spaces == 1 or initial_spaces == 3) and + not Match(r'\s*\w+\s*:\s*$', cleansed_line)): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + # Labels should always be indented at least one space. + elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', + line): + error(filename, linenum, 'whitespace/labels', 4, + 'Labels should always be indented at least one space. ' + 'If this is a member-initializer list in a constructor or ' + 'the base class list in a class definition, the colon should ' + 'be on the following line.') + + + # Check if the line is a header guard. + is_header_guard = False + if file_extension == 'h': + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + line_width = GetLineWidth(line) + if line_width > 100: + error(filename, linenum, 'whitespace/line_length', 4, + 'Lines should very rarely be longer than 100 characters') + elif line_width > 80: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= 80 characters long') + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 0, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckEmptyLoopBody(filename, clean_lines, linenum, error) + CheckAccess(filename, clean_lines, linenum, nesting_state, error) + CheckSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckCheck(filename, clean_lines, linenum, error) + CheckAltTokens(filename, clean_lines, linenum, error) + classinfo = nesting_state.InnermostClass() + if classinfo: + CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) + + +_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _IsTestFilename(filename): + """Determines if the given filename has a suffix that identifies it as a test. + + Args: + filename: The input filename. + + Returns: + True if 'filename' looks like a test, False otherwise. + """ + if (filename.endswith('_test.cc') or + filename.endswith('_unittest.cc') or + filename.endswith('_regtest.cc')): + return True + else: + return False + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_stl_h = include in _STL_HEADERS + is_cpp_h = is_stl_h or include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): + error(filename, linenum, 'build/include', 4, + 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + if include in include_state: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, include_state[include])) + else: + include_state[include] = linenum + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: %s.h, c system, c++ system, other.' % + (error_message, fileinfo.BaseName())) + if not include_state.IsInAlphabeticalOrder(include): + error(filename, linenum, 'build/include_alpha', 4, + 'Include "%s" not in alphabetical order' % include) + + # Look for any of the stream classes that are part of standard C++. + match = _RE_PATTERN_INCLUDE.match(line) + if match: + include = match.group(2) + if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): + # Many unit tests use cout, so we exempt them. + if not _IsTestFilename(filename): + error(filename, linenum, 'readability/streams', 3, + 'Streams are highly discouraged.') + + +def _GetTextInside(text, start_pattern): + """Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the text + following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation could not be found. + """ + # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.itervalues()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, + error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Create an extended_line, which is the concatenation of the current and + # next lines, for more effective checking of code that may span more than one + # line. + if linenum + 1 < clean_lines.NumLines(): + extended_line = line + clean_lines.elided[linenum + 1] + else: + extended_line = line + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # TODO(unknown): figure out if they're using default arguments in fn proto. + + # Check for non-const references in functions. This is tricky because & + # is also used to take the address of something. We allow <> for templates, + # (ignoring whatever is between the braces) and : for classes. + # These are complicated re's. They try to capture the following: + # paren (for fn-prototype start), typename, &, varname. For the const + # version, we're willing for const to be before typename or after + # Don't check the implementation on same line. + fnline = line.split('{', 1)[0] + if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > + len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' + r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', + fnline))): + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". We also filter + # out for loops, which lint otherwise mistakenly thinks are functions. + if not Search( + r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' + r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', + fnline): + error(filename, linenum, 'runtime/references', 2, + 'Is this a non-const reference? ' + 'If so, make const or use a pointer.') + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there + r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) + if match: + # gMock methods are defined using some variant of MOCK_METHODx(name, type) + # where type may be float(), int(string), etc. Without context they are + # virtually indistinguishable from int(x) casts. Likewise, gMock's + # MockCallback takes a template parameter of the form return_type(arg_type), + # which looks much like the cast we're trying to detect. + if (match.group(1) is None and # If new operator, then this isn't a cast + not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + Match(r'^\s*MockCallback<.*>', line))): + # Try a bit harder to catch gmock lines: the only place where + # something looks like an old-style cast is where we declare the + # return type of the mocked method, and the only time when we + # are missing context is if MOCK_METHOD was split across + # multiple lines (for example http://go/hrfhr ), so we only need + # to check the previous line for MOCK_METHOD. + if (linenum == 0 or + not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', + clean_lines.elided[linenum - 1])): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + match.group(2)) + + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + + # This doesn't catch all cases. Consider (const char * const)"hello". + # + # (char *) "foo" should always be a const_cast (reinterpret_cast won't + # compile). + if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): + pass + else: + # Check pointer casts for other than string constants + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + if Search( + r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access. + match = Match( + r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', + line) + # Make sure it's not a function. + # Function template specialization looks like: "string foo(...". + # Class template definitions look like: "string Foo::Method(...". + if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', + match.group(3)): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string instead: ' + '"%schar %s[]".' % + (match.group(1), match.group(2))) + + # Check that we're not using RTTI outside of testing code. + if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): + error(filename, linenum, 'runtime/rtti', 5, + 'Do not use dynamic_cast<>. If you need to cast within a class ' + "hierarchy, use static_cast<> to upcast. Google doesn't support " + 'RTTI.') + + if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + if file_extension == 'h': + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. + if Search(r'\bshort port\b', line): + if not Search(r'\bunsigned short port\b', line): + error(filename, linenum, 'runtime/int', 4, + 'Use "unsigned short" for ports, not "short"') + else: + match = Search(r'\b(short|long(?! +double)|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\b', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + if Search(r'\bsscanf\b', line): + error(filename, linenum, 'runtime/printf', 1, + 'sscanf can be ok, but is slow and can overflow buffers.') + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(sugawarayu): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match and match.group(1) != '__VA_ARGS__': + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays. Use an appropriately named ' + "('k' followed by CamelCase) compile-time constant for the size.") + + # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or + # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing + # in the class declaration. + match = Match( + (r'\s*' + r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' + r'\(.*\);$'), + line) + if match and linenum + 1 < clean_lines.NumLines(): + next_line = clean_lines.elided[linenum + 1] + # We allow some, but not all, declarations of variables to be present + # in the statement that defines the class. The [\w\*,\s]* fragment of + # the regular expression below allows users to declare instances of + # the class or pointers to instances, but not less common types such + # as function pointers or arrays. It's a tradeoff between allowing + # reasonable code and avoiding trying to parse more C++ using regexps. + if not Search(r'^\s*}[\w\*,\s]*;', next_line): + error(filename, linenum, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (file_extension == 'h' + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + +def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, + error): + """Checks for a C-style cast by looking for the pattern. + + This also handles sizeof(type) warnings, due to similarity of content. + + Args: + filename: The name of the current file. + linenum: The number of the line to check. + line: The line of code to check. + raw_line: The raw line of code to check, with comments. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast, static_cast, or const_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + + Returns: + True if an error was emitted. + False otherwise. + """ + match = Search(pattern, line) + if not match: + return False + + # e.g., sizeof(int) + sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) + if sizeof_match: + error(filename, linenum, 'runtime/sizeof', 1, + 'Using sizeof(type). Use sizeof(varname) instead if possible') + return True + + # operator++(int) and operator--(int) + if (line[0:match.start(1) - 1].endswith(' operator++') or + line[0:match.start(1) - 1].endswith(' operator--')): + return False + + remainder = line[match.end(0):] + + # The close paren is for function pointers as arguments to a function. + # eg, void foo(void (*bar)(int)); + # The semicolon check is a more basic function check; also possibly a + # function pointer typedef. + # eg, void foo(int); or void foo(int) const; + # The equals check is for function pointer assignment. + # eg, void *(*foo)(int) = ... + # The > is for MockCallback<...> ... + # + # Right now, this will only catch cases where there's a single argument, and + # it's unnamed. It should probably be expanded to check for multiple + # arguments with some unnamed. + function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) + if function_match: + if (not function_match.group(3) or + function_match.group(3) == ';' or + ('MockCallback<' not in raw_line and + '/*' not in raw_line)): + error(filename, linenum, 'readability/function', 3, + 'All parameters should be named in a function') + return True + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + return True + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('', ('deque',)), + ('', ('unary_function', 'binary_function', + 'plus', 'minus', 'multiplies', 'divides', 'modulus', + 'negate', + 'equal_to', 'not_equal_to', 'greater', 'less', + 'greater_equal', 'less_equal', + 'logical_and', 'logical_or', 'logical_not', + 'unary_negate', 'not1', 'binary_negate', 'not2', + 'bind1st', 'bind2nd', + 'pointer_to_unary_function', + 'pointer_to_binary_function', + 'ptr_fun', + 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', + 'mem_fun_ref_t', + 'const_mem_fun_t', 'const_mem_fun1_t', + 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', + 'mem_fun_ref', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator',)), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_RE_PATTERN_STRING = re.compile(r'\bstring\b') + +_re_pattern_algorithm_header = [] +for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', + 'transform'): + # Match max(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_algorithm_header.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + '')) + +_re_pattern_templates = [] +for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: + for _template in _templates: + _re_pattern_templates.append( + (re.compile(r'(\<|\b)' + _template + r'\s*\<'), + _template + '<>', + _header)) + + +def FilesBelongToSameModule(filename_cc, filename_h): + """Check if these two filenames belong to the same module. + + The concept of a 'module' here is a as follows: + foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the + same 'module' if they are in the same directory. + some/path/public/xyzzy and some/path/internal/xyzzy are also considered + to belong to the same module here. + + If the filename_cc contains a longer path than the filename_h, for example, + '/absolute/path/to/base/sysinfo.cc', and this file would include + 'base/sysinfo.h', this function also produces the prefix needed to open the + header. This is used by the caller of this function to more robustly open the + header file. We don't have access to the real include paths in this context, + so we need this guesswork here. + + Known bugs: tools/base/bar.cc and base/bar.h belong to the same module + according to this implementation. Because of this, this function gives + some false positives. This should be sufficiently rare in practice. + + Args: + filename_cc: is the path for the .cc file + filename_h: is the path for the header path + + Returns: + Tuple with a bool and a string: + bool: True if filename_cc and filename_h belong to the same module. + string: the additional prefix needed to open the header file. + """ + + if not filename_cc.endswith('.cc'): + return (False, '') + filename_cc = filename_cc[:-len('.cc')] + if filename_cc.endswith('_unittest'): + filename_cc = filename_cc[:-len('_unittest')] + elif filename_cc.endswith('_test'): + filename_cc = filename_cc[:-len('_test')] + filename_cc = filename_cc.replace('/public/', '/') + filename_cc = filename_cc.replace('/internal/', '/') + + if not filename_h.endswith('.h'): + return (False, '') + filename_h = filename_h[:-len('.h')] + if filename_h.endswith('-inl'): + filename_h = filename_h[:-len('-inl')] + filename_h = filename_h.replace('/public/', '/') + filename_h = filename_h.replace('/internal/', '/') + + files_belong_to_same_module = filename_cc.endswith(filename_h) + common_path = '' + if files_belong_to_same_module: + common_path = filename_cc[:-len(filename_h)] + return files_belong_to_same_module, common_path + + +def UpdateIncludeState(filename, include_state, io=codecs): + """Fill up the include_state with new includes found from the file. + + Args: + filename: the name of the header to read. + include_state: an _IncludeState instance in which the headers are inserted. + io: The io factory to use to read the file. Provided for testability. + + Returns: + True if a header was succesfully added. False otherwise. + """ + headerfile = None + try: + headerfile = io.open(filename, 'r', 'utf8', 'replace') + except IOError: + return False + linenum = 0 + for line in headerfile: + linenum += 1 + clean_line = CleanseComments(line) + match = _RE_PATTERN_INCLUDE.search(clean_line) + if match: + include = match.group(2) + # The value formatting is cute, but not really used right now. + # What matters here is that the key is in include_state. + include_state.setdefault(include, '%s:%d' % (filename, linenum)) + return True + + +def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, + io=codecs): + """Reports for missing stl includes. + + This function will output warnings to make sure you are including the headers + necessary for the stl containers and functions that you use. We only give one + reason to include a header. For example, if you use both equal_to<> and + less<> in a .h file, only one (the latter in the file) of these will be + reported as a reason to include the . + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + include_state: An _IncludeState instance. + error: The function to call with any errors found. + io: The IO factory to use to read the header file. Provided for unittest + injection. + """ + required = {} # A map of header name to linenumber and the template entity. + # Example of required: { '': (1219, 'less<>') } + + for linenum in xrange(clean_lines.NumLines()): + line = clean_lines.elided[linenum] + if not line or line[0] == '#': + continue + + # String is special -- it is a non-templatized type in STL. + matched = _RE_PATTERN_STRING.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[''] = (linenum, 'string') + + for pattern, template, header in _re_pattern_algorithm_header: + if pattern.search(line): + required[header] = (linenum, template) + + # The following function is just a speed up, no semantics are changed. + if not '<' in line: # Reduces the cpu time usage by skipping lines. + continue + + for pattern, template, header in _re_pattern_templates: + if pattern.search(line): + required[header] = (linenum, template) + + # The policy is that if you #include something in foo.h you don't need to + # include it again in foo.cc. Here, we will look at possible includes. + # Let's copy the include_state so it is only messed up within this function. + include_state = include_state.copy() + + # Did we find the header for this file (if any) and succesfully load it? + header_found = False + + # Use the absolute path so that matching works properly. + abs_filename = FileInfo(filename).FullName() + + # For Emacs's flymake. + # If cpplint is invoked from Emacs's flymake, a temporary file is generated + # by flymake and that file name might end with '_flymake.cc'. In that case, + # restore original file name here so that the corresponding header file can be + # found. + # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' + # instead of 'foo_flymake.h' + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) + + # include_state is modified during iteration, so we iterate over a copy of + # the keys. + header_keys = include_state.keys() + for header in header_keys: + (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) + fullpath = common_path + header + if same_module and UpdateIncludeState(fullpath, include_state, io): + header_found = True + + # If we can't find the header file for a .cc, assume it's because we don't + # know where to look. In that case we'll give up as we're not sure they + # didn't include it in the .h file. + # TODO(unknown): Do a better job of finding .h files so we are confident that + # not having the .h file means there isn't one. + if filename.endswith('.cc') and not header_found: + return + + # All the lines have been processed, report the errors found. + for required_header_unstripped in required: + template = required[required_header_unstripped][1] + if required_header_unstripped.strip('<>"') not in include_state: + error(filename, required[required_header_unstripped][0], + 'build/include_what_you_use', 4, + 'Add #include ' + required_header_unstripped + ' for ' + template) + + +_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') + + +def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): + """Check that make_pair's template arguments are deduced. + + G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are + specified explicitly, and such use isn't intended in any case. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + raw = clean_lines.raw_lines + line = raw[linenum] + match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) + if match: + error(filename, linenum, 'build/explicit_make_pair', + 4, # 4 = high confidence + 'For C++11-compatibility, omit template arguments from make_pair' + ' OR use pair directly OR if appropriate, construct a pair directly') + + +def ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions=[]): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + nesting_state: A _NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + nesting_state.Update(filename, clean_lines, line, error) + if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: + return + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + error) + CheckForNonStandardConstructs(filename, clean_lines, line, + nesting_state, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + CheckMakePairUsesDeduction(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + nesting_state = _NestingState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions) + nesting_state.CheckClassFinished(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForUnicodeReplacementCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessFile(filename, vlevel, extra_check_functions=[]): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + + _SetVerboseLevel(vlevel) + + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. If it is not expected to be present (i.e. os.linesep != + # '\r\n' as in Windows), a warning is issued below if this file + # is processed. + + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + carriage_return_found = False + # Remove trailing '\r'. + for linenum in range(len(lines)): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + carriage_return_found = True + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if (filename != '-' and file_extension != 'cc' and file_extension != 'h' + and file_extension != 'cpp'): + sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) + else: + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + if carriage_return_found and os.linesep != '\r\n': + # Use 0 for linenum since outputting only one error for potentially + # several lines. + Error(filename, 0, 'whitespace/newline', 1, + 'One or more unexpected \\r (^M) found;' + 'better to use only a \\n') + + sys.stderr.write('Done processing %s\n' % filename) + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=', + 'root=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if not val in ('emacs', 'vs7', 'eclipse'): + PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + elif opt == '--root': + global _root + _root = val + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/projects/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py b/projects/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py new file mode 100755 index 00000000..32ba9226 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +#===- lib/sanitizer_common/scripts/gen_dynamic_list.py ---------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# +# Generates the list of functions that should be exported from sanitizer +# runtimes. The output format is recognized by --dynamic-list linker option. +# Usage: +# gen_dynamic_list.py libclang_rt.*san*.a [ files ... ] +# +#===------------------------------------------------------------------------===# +import os +import re +import subprocess +import sys + +new_delete = set(['_ZdaPv', '_ZdaPvRKSt9nothrow_t', + '_ZdlPv', '_ZdlPvRKSt9nothrow_t', + '_Znam', '_ZnamRKSt9nothrow_t', + '_Znwm', '_ZnwmRKSt9nothrow_t']) + +versioned_functions = set(['memcpy', 'pthread_attr_getaffinity_np', + 'pthread_cond_broadcast', + 'pthread_cond_destroy', 'pthread_cond_init', + 'pthread_cond_signal', 'pthread_cond_timedwait', + 'pthread_cond_wait', 'realpath', + 'sched_getaffinity']) + +def get_global_functions(library): + functions = [] + nm_proc = subprocess.Popen(['nm', library], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + nm_out = nm_proc.communicate()[0].split('\n') + if nm_proc.returncode != 0: + raise subprocess.CalledProcessError(nm_proc.returncode, 'nm') + for line in nm_out: + cols = line.split(' ') + if (len(cols) == 3 and cols[1] in ('T', 'W')) : + functions.append(cols[2]) + return functions + +def main(argv): + result = [] + + library = argv[1] + all_functions = get_global_functions(library) + function_set = set(all_functions) + for func in all_functions: + # Export new/delete operators. + if func in new_delete: + result.append(func) + continue + # Export interceptors. + match = re.match('__interceptor_(.*)', func) + if match: + result.append(func) + # We have to avoid exporting the interceptors for versioned library + # functions due to gold internal error. + orig_name = match.group(1) + if orig_name in function_set and orig_name not in versioned_functions: + result.append(orig_name) + continue + # Export sanitizer interface functions. + if re.match('__sanitizer_(.*)', func): + result.append(func) + + # Additional exported functions from files. + for fname in argv[2:]: + f = open(fname, 'r') + for line in f: + result.append(line.rstrip()) + # Print the resulting list in the format recognized by ld. + print '{' + result.sort() + for f in result: + print ' ' + f + ';' + print '};' + +if __name__ == '__main__': + main(sys.argv) diff --git a/projects/compiler-rt/lib/sanitizer_common/scripts/sancov.py b/projects/compiler-rt/lib/sanitizer_common/scripts/sancov.py new file mode 100755 index 00000000..aa791bc4 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/scripts/sancov.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# Merge or print the coverage data collected by asan's coverage. +# Input files are sequences of 4-byte integers. +# We need to merge these integers into a set and then +# either print them (as hex) or dump them into another file. +import array +import sys + +prog_name = ""; + +def Usage(): + print >> sys.stderr, "Usage: \n" + \ + " " + prog_name + " merge file1 [file2 ...] > output\n" \ + " " + prog_name + " print file1 [file2 ...]\n" + exit(1) + +def ReadOneFile(path): + f = open(path, mode="rb") + f.seek(0, 2) + size = f.tell() + f.seek(0, 0) + s = set(array.array('I', f.read(size))) + f.close() + print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path) + return s + +def Merge(files): + s = set() + for f in files: + s = s.union(ReadOneFile(f)) + print >> sys.stderr, "%s: %d files merged; %d PCs total" % \ + (prog_name, len(files), len(s)) + return sorted(s) + +def PrintFiles(files): + s = Merge(files) + for i in s: + print "0x%x" % i + +def MergeAndPrint(files): + if sys.stdout.isatty(): + Usage() + s = Merge(files) + a = array.array('I', s) + a.tofile(sys.stdout) + +if __name__ == '__main__': + prog_name = sys.argv[0] + if len(sys.argv) <= 2: + Usage(); + if sys.argv[1] == "print": + PrintFiles(sys.argv[2:]) + elif sys.argv[1] == "merge": + MergeAndPrint(sys.argv[2:]) + else: + Usage() diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/projects/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt new file mode 100644 index 00000000..5b66917b --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt @@ -0,0 +1,169 @@ +include(CompilerRTCompile) + +set(SANITIZER_UNITTESTS + sanitizer_allocator_test.cc + sanitizer_atomic_test.cc + sanitizer_common_test.cc + sanitizer_flags_test.cc + sanitizer_ioctl_test.cc + sanitizer_libc_test.cc + sanitizer_linux_test.cc + sanitizer_list_test.cc + sanitizer_mutex_test.cc + sanitizer_nolibc_test.cc + sanitizer_posix_test.cc + sanitizer_printf_test.cc + sanitizer_scanf_interceptor_test.cc + sanitizer_stackdepot_test.cc + sanitizer_stacktrace_test.cc + sanitizer_stoptheworld_test.cc + sanitizer_suppressions_test.cc + sanitizer_test_main.cc + sanitizer_thread_registry_test.cc) + +set(SANITIZER_TEST_HEADERS + sanitizer_test_utils.h) +foreach(header ${SANITIZER_HEADERS}) + list(APPEND SANITIZER_TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header}) +endforeach() + +set(SANITIZER_TEST_CFLAGS_COMMON + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common + -DGTEST_HAS_RTTI=0 + -O2 -g -fno-rtti + -Wall -Werror -Werror=sign-compare) + +set(SANITIZER_TEST_LINK_FLAGS_COMMON + -lstdc++ -ldl) + +include_directories(..) +include_directories(../..) + +# Adds static library which contains sanitizer_common object file +# (universal binary on Mac and arch-specific object files on Linux). +macro(add_sanitizer_common_lib library) + add_library(${library} STATIC ${ARGN}) + set_target_properties(${library} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +endmacro() + +function(get_sanitizer_common_lib_for_arch arch lib lib_name) + if(APPLE) + set(tgt_name "RTSanitizerCommon.test.osx") + else() + set(tgt_name "RTSanitizerCommon.test.${arch}") + endif() + set(${lib} "${tgt_name}" PARENT_SCOPE) + set(${lib_name} "lib${tgt_name}.a" PARENT_SCOPE) +endfunction() + +# Sanitizer_common unit tests testsuite. +add_custom_target(SanitizerUnitTests) +set_target_properties(SanitizerUnitTests PROPERTIES + FOLDER "Sanitizer unittests") + +# Adds sanitizer tests for architecture. +macro(add_sanitizer_tests_for_arch arch) + get_target_flags_for_arch(${arch} TARGET_FLAGS) + set(SANITIZER_TEST_SOURCES ${SANITIZER_UNITTESTS} + ${COMPILER_RT_GTEST_SOURCE}) + set(SANITIZER_TEST_OBJECTS) + foreach(source ${SANITIZER_TEST_SOURCES}) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + clang_compile(${output_obj} ${source} + CFLAGS ${SANITIZER_TEST_CFLAGS_COMMON} ${TARGET_FLAGS} + DEPS gtest ${SANITIZER_RUNTIME_LIBRARIES} + ${SANITIZER_TEST_HEADERS}) + list(APPEND SANITIZER_TEST_OBJECTS ${output_obj}) + endforeach() + get_sanitizer_common_lib_for_arch(${arch} SANITIZER_COMMON_LIB + SANITIZER_COMMON_LIB_NAME) + # Add unittest target. + set(SANITIZER_TEST_NAME "Sanitizer-${arch}-Test") + add_compiler_rt_test(SanitizerUnitTests ${SANITIZER_TEST_NAME} + OBJECTS ${SANITIZER_TEST_OBJECTS} + ${SANITIZER_COMMON_LIB_NAME} + DEPS ${SANITIZER_TEST_OBJECTS} ${SANITIZER_COMMON_LIB} + LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS_COMMON} + -lpthread ${TARGET_FLAGS}) + + if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${arch}" STREQUAL "x86_64") + # Test that the libc-independent part of sanitizer_common is indeed + # independent of libc, by linking this binary without libc (here) and + # executing it (unit test in sanitizer_nolibc_test.cc). + clang_compile(sanitizer_nolibc_test_main.${arch}.o + sanitizer_nolibc_test_main.cc + CFLAGS ${SANITIZER_TEST_CFLAGS_COMMON} ${TARGET_FLAGS} + DEPS ${SANITIZER_RUNTIME_LIBRARIES} ${SANITIZER_TEST_HEADERS}) + add_compiler_rt_test(SanitizerUnitTests "Sanitizer-${arch}-Test-Nolibc" + OBJECTS sanitizer_nolibc_test_main.${arch}.o + -Wl,-whole-archive + libRTSanitizerCommon.test.nolibc.${arch}.a + -Wl,-no-whole-archive + DEPS sanitizer_nolibc_test_main.${arch}.o + RTSanitizerCommon.test.nolibc.${arch} + LINK_FLAGS -nostdlib ${TARGET_FLAGS}) + endif() +endmacro() + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + # We use just-built clang to build sanitizer_common unittests, so we must + # be sure that produced binaries would work. + if(APPLE) + add_sanitizer_common_lib("RTSanitizerCommon.test.osx" + $) + else() + if(CAN_TARGET_x86_64) + add_sanitizer_common_lib("RTSanitizerCommon.test.x86_64" + $ + $) + add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64" + $) + endif() + if(CAN_TARGET_i386) + add_sanitizer_common_lib("RTSanitizerCommon.test.i386" + $ + $) + endif() + endif() + if(CAN_TARGET_x86_64) + add_sanitizer_tests_for_arch(x86_64) + endif() + if(CAN_TARGET_i386) + add_sanitizer_tests_for_arch(i386) + endif() + + # Run unittests as a part of lit testsuite. + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + + add_lit_testsuite(check-sanitizer "Running sanitizer library unittests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS SanitizerUnitTests + ) + set_target_properties(check-sanitizer PROPERTIES FOLDER "Sanitizer unittests") +endif() + +if(ANDROID) + # We assume that unit tests on Android are built in a build + # tree with fresh Clang as a host compiler. + add_executable(SanitizerTest + ${SANITIZER_UNITTESTS} + ${COMPILER_RT_GTEST_SOURCE} + $) + set_target_compile_flags(SanitizerTest + ${SANITIZER_COMMON_CFLAGS} + ${SANITIZER_TEST_CFLAGS_COMMON}) + # Setup correct output directory and link flags. + set_target_properties(SanitizerTest PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + set_target_link_flags(SanitizerTest ${SANITIZER_TEST_LINK_FLAGS_COMMON}) + # Add unit test to test suite. + add_dependencies(SanitizerUnitTests SanitizerTest) +endif() diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/lit.site.cfg.in b/projects/compiler-rt/lib/sanitizer_common/tests/lit.site.cfg.in new file mode 100644 index 00000000..5ceb9e4c --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/lit.site.cfg.in @@ -0,0 +1,14 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") + +# Setup config name. +config.name = 'SanitizerCommon-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with sanitizer_common tests. +config.test_exec_root = os.path.join("@COMPILER_RT_BINARY_DIR@", "lib", + "sanitizer_common", "tests") +config.test_source_root = config.test_exec_root diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc new file mode 100644 index 00000000..d92a07fe --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc @@ -0,0 +1,797 @@ +//===-- sanitizer_allocator_test.cc ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Tests for sanitizer_allocator.h. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" + +#include "sanitizer_test_utils.h" + +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include + +// Too slow for debug build +#if TSAN_DEBUG == 0 + +#if SANITIZER_WORDSIZE == 64 +static const uptr kAllocatorSpace = 0x700000000000ULL; +static const uptr kAllocatorSize = 0x010000000000ULL; // 1T. +static const u64 kAddressSpaceSize = 1ULL << 47; + +typedef SizeClassAllocator64< + kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap> Allocator64; + +typedef SizeClassAllocator64< + kAllocatorSpace, kAllocatorSize, 16, CompactSizeClassMap> Allocator64Compact; +#else +static const u64 kAddressSpaceSize = 1ULL << 32; +#endif + +static const uptr kRegionSizeLog = FIRST_32_SECOND_64(20, 24); +static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog; + +typedef SizeClassAllocator32< + 0, kAddressSpaceSize, + /*kMetadataSize*/16, + CompactSizeClassMap, + kRegionSizeLog, + FlatByteMap > + Allocator32Compact; + +template +void TestSizeClassMap() { + typedef SizeClassMap SCMap; + // SCMap::Print(); + SCMap::Validate(); +} + +TEST(SanitizerCommon, DefaultSizeClassMap) { + TestSizeClassMap(); +} + +TEST(SanitizerCommon, CompactSizeClassMap) { + TestSizeClassMap(); +} + +TEST(SanitizerCommon, InternalSizeClassMap) { + TestSizeClassMap(); +} + +template +void TestSizeClassAllocator() { + Allocator *a = new Allocator; + a->Init(); + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + + static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, + 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; + + std::vector allocated; + + uptr last_total_allocated = 0; + for (int i = 0; i < 3; i++) { + // Allocate a bunch of chunks. + for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { + uptr size = sizes[s]; + if (!a->CanAllocate(size, 1)) continue; + // printf("s = %ld\n", size); + uptr n_iter = std::max((uptr)6, 8000000 / size); + // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); + for (uptr i = 0; i < n_iter; i++) { + uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); + char *x = (char*)cache.Allocate(a, class_id0); + x[0] = 0; + x[size - 1] = 0; + x[size / 2] = 0; + allocated.push_back(x); + CHECK_EQ(x, a->GetBlockBegin(x)); + CHECK_EQ(x, a->GetBlockBegin(x + size - 1)); + CHECK(a->PointerIsMine(x)); + CHECK(a->PointerIsMine(x + size - 1)); + CHECK(a->PointerIsMine(x + size / 2)); + CHECK_GE(a->GetActuallyAllocatedSize(x), size); + uptr class_id = a->GetSizeClass(x); + CHECK_EQ(class_id, Allocator::SizeClassMapT::ClassID(size)); + uptr *metadata = reinterpret_cast(a->GetMetaData(x)); + metadata[0] = reinterpret_cast(x) + 1; + metadata[1] = 0xABCD; + } + } + // Deallocate all. + for (uptr i = 0; i < allocated.size(); i++) { + void *x = allocated[i]; + uptr *metadata = reinterpret_cast(a->GetMetaData(x)); + CHECK_EQ(metadata[0], reinterpret_cast(x) + 1); + CHECK_EQ(metadata[1], 0xABCD); + cache.Deallocate(a, a->GetSizeClass(x), x); + } + allocated.clear(); + uptr total_allocated = a->TotalMemoryUsed(); + if (last_total_allocated == 0) + last_total_allocated = total_allocated; + CHECK_EQ(last_total_allocated, total_allocated); + } + + // Check that GetBlockBegin never crashes. + for (uptr x = 0, step = kAddressSpaceSize / 100000; + x < kAddressSpaceSize - step; x += step) + if (a->PointerIsMine(reinterpret_cast(x))) + Ident(a->GetBlockBegin(reinterpret_cast(x))); + + a->TestOnlyUnmap(); + delete a; +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64) { + TestSizeClassAllocator(); +} + +TEST(SanitizerCommon, SizeClassAllocator64Compact) { + TestSizeClassAllocator(); +} +#endif + +TEST(SanitizerCommon, SizeClassAllocator32Compact) { + TestSizeClassAllocator(); +} + +template +void SizeClassAllocatorMetadataStress() { + Allocator *a = new Allocator; + a->Init(); + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + + const uptr kNumAllocs = 1 << 13; + void *allocated[kNumAllocs]; + void *meta[kNumAllocs]; + for (uptr i = 0; i < kNumAllocs; i++) { + void *x = cache.Allocate(a, 1 + i % 50); + allocated[i] = x; + meta[i] = a->GetMetaData(x); + } + // Get Metadata kNumAllocs^2 times. + for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { + uptr idx = i % kNumAllocs; + void *m = a->GetMetaData(allocated[idx]); + EXPECT_EQ(m, meta[idx]); + } + for (uptr i = 0; i < kNumAllocs; i++) { + cache.Deallocate(a, 1 + i % 50, allocated[i]); + } + + a->TestOnlyUnmap(); + delete a; +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { + SizeClassAllocatorMetadataStress(); +} + +TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) { + SizeClassAllocatorMetadataStress(); +} +#endif // SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) { + SizeClassAllocatorMetadataStress(); +} + +template +void SizeClassAllocatorGetBlockBeginStress() { + Allocator *a = new Allocator; + a->Init(); + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + + uptr max_size_class = Allocator::kNumClasses - 1; + uptr size = Allocator::SizeClassMapT::Size(max_size_class); + u64 G8 = 1ULL << 33; + // Make sure we correctly compute GetBlockBegin() w/o overflow. + for (size_t i = 0; i <= G8 / size; i++) { + void *x = cache.Allocate(a, max_size_class); + void *beg = a->GetBlockBegin(x); + // if ((i & (i - 1)) == 0) + // fprintf(stderr, "[%zd] %p %p\n", i, x, beg); + EXPECT_EQ(x, beg); + } + + a->TestOnlyUnmap(); + delete a; +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64GetBlockBegin) { + SizeClassAllocatorGetBlockBeginStress(); +} +TEST(SanitizerCommon, SizeClassAllocator64CompactGetBlockBegin) { + SizeClassAllocatorGetBlockBeginStress(); +} +TEST(SanitizerCommon, SizeClassAllocator32CompactGetBlockBegin) { + SizeClassAllocatorGetBlockBeginStress(); +} +#endif // SANITIZER_WORDSIZE == 64 + +struct TestMapUnmapCallback { + static int map_count, unmap_count; + void OnMap(uptr p, uptr size) const { map_count++; } + void OnUnmap(uptr p, uptr size) const { unmap_count++; } +}; +int TestMapUnmapCallback::map_count; +int TestMapUnmapCallback::unmap_count; + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) { + TestMapUnmapCallback::map_count = 0; + TestMapUnmapCallback::unmap_count = 0; + typedef SizeClassAllocator64< + kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap, + TestMapUnmapCallback> Allocator64WithCallBack; + Allocator64WithCallBack *a = new Allocator64WithCallBack; + a->Init(); + EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state. + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + AllocatorStats stats; + stats.Init(); + a->AllocateBatch(&stats, &cache, 32); + EXPECT_EQ(TestMapUnmapCallback::map_count, 3); // State + alloc + metadata. + a->TestOnlyUnmap(); + EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing. + delete a; +} +#endif + +TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) { + TestMapUnmapCallback::map_count = 0; + TestMapUnmapCallback::unmap_count = 0; + typedef SizeClassAllocator32< + 0, kAddressSpaceSize, + /*kMetadataSize*/16, + CompactSizeClassMap, + kRegionSizeLog, + FlatByteMap, + TestMapUnmapCallback> + Allocator32WithCallBack; + Allocator32WithCallBack *a = new Allocator32WithCallBack; + a->Init(); + EXPECT_EQ(TestMapUnmapCallback::map_count, 0); + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + AllocatorStats stats; + stats.Init(); + a->AllocateBatch(&stats, &cache, 32); + EXPECT_EQ(TestMapUnmapCallback::map_count, 1); + a->TestOnlyUnmap(); + EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); + delete a; + // fprintf(stderr, "Map: %d Unmap: %d\n", + // TestMapUnmapCallback::map_count, + // TestMapUnmapCallback::unmap_count); +} + +TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) { + TestMapUnmapCallback::map_count = 0; + TestMapUnmapCallback::unmap_count = 0; + LargeMmapAllocator a; + a.Init(); + AllocatorStats stats; + stats.Init(); + void *x = a.Allocate(&stats, 1 << 20, 1); + EXPECT_EQ(TestMapUnmapCallback::map_count, 1); + a.Deallocate(&stats, x); + EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); +} + +template +void FailInAssertionOnOOM() { + Allocator a; + a.Init(); + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + AllocatorStats stats; + stats.Init(); + for (int i = 0; i < 1000000; i++) { + a.AllocateBatch(&stats, &cache, 52); + } + + a.TestOnlyUnmap(); +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64Overflow) { + EXPECT_DEATH(FailInAssertionOnOOM(), "Out of memory"); +} +#endif + +TEST(SanitizerCommon, LargeMmapAllocator) { + LargeMmapAllocator<> a; + a.Init(); + AllocatorStats stats; + stats.Init(); + + static const int kNumAllocs = 1000; + char *allocated[kNumAllocs]; + static const uptr size = 4000; + // Allocate some. + for (int i = 0; i < kNumAllocs; i++) { + allocated[i] = (char *)a.Allocate(&stats, size, 1); + CHECK(a.PointerIsMine(allocated[i])); + } + // Deallocate all. + CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); + for (int i = 0; i < kNumAllocs; i++) { + char *p = allocated[i]; + CHECK(a.PointerIsMine(p)); + a.Deallocate(&stats, p); + } + // Check that non left. + CHECK_EQ(a.TotalMemoryUsed(), 0); + + // Allocate some more, also add metadata. + for (int i = 0; i < kNumAllocs; i++) { + char *x = (char *)a.Allocate(&stats, size, 1); + CHECK_GE(a.GetActuallyAllocatedSize(x), size); + uptr *meta = reinterpret_cast(a.GetMetaData(x)); + *meta = i; + allocated[i] = x; + } + for (int i = 0; i < kNumAllocs * kNumAllocs; i++) { + char *p = allocated[i % kNumAllocs]; + CHECK(a.PointerIsMine(p)); + CHECK(a.PointerIsMine(p + 2000)); + } + CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); + // Deallocate all in reverse order. + for (int i = 0; i < kNumAllocs; i++) { + int idx = kNumAllocs - i - 1; + char *p = allocated[idx]; + uptr *meta = reinterpret_cast(a.GetMetaData(p)); + CHECK_EQ(*meta, idx); + CHECK(a.PointerIsMine(p)); + a.Deallocate(&stats, p); + } + CHECK_EQ(a.TotalMemoryUsed(), 0); + + // Test alignments. + uptr max_alignment = SANITIZER_WORDSIZE == 64 ? (1 << 28) : (1 << 24); + for (uptr alignment = 8; alignment <= max_alignment; alignment *= 2) { + const uptr kNumAlignedAllocs = 100; + for (uptr i = 0; i < kNumAlignedAllocs; i++) { + uptr size = ((i % 10) + 1) * 4096; + char *p = allocated[i] = (char *)a.Allocate(&stats, size, alignment); + CHECK_EQ(p, a.GetBlockBegin(p)); + CHECK_EQ(p, a.GetBlockBegin(p + size - 1)); + CHECK_EQ(p, a.GetBlockBegin(p + size / 2)); + CHECK_EQ(0, (uptr)allocated[i] % alignment); + p[0] = p[size - 1] = 0; + } + for (uptr i = 0; i < kNumAlignedAllocs; i++) { + a.Deallocate(&stats, allocated[i]); + } + } + + // Regression test for boundary condition in GetBlockBegin(). + uptr page_size = GetPageSizeCached(); + char *p = (char *)a.Allocate(&stats, page_size, 1); + CHECK_EQ(p, a.GetBlockBegin(p)); + CHECK_EQ(p, (char *)a.GetBlockBegin(p + page_size - 1)); + CHECK_NE(p, (char *)a.GetBlockBegin(p + page_size)); + a.Deallocate(&stats, p); +} + +template + +void TestCombinedAllocator() { + typedef + CombinedAllocator + Allocator; + Allocator *a = new Allocator; + a->Init(); + + AllocatorCache cache; + memset(&cache, 0, sizeof(cache)); + a->InitCache(&cache); + + bool allocator_may_return_null = common_flags()->allocator_may_return_null; + common_flags()->allocator_may_return_null = true; + EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0); + EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0); + EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1), (void*)0); + EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1024), (void*)0); + EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1023, 1024), (void*)0); + + common_flags()->allocator_may_return_null = false; + EXPECT_DEATH(a->Allocate(&cache, -1, 1), + "allocator is terminating the process"); + // Restore the original value. + common_flags()->allocator_may_return_null = allocator_may_return_null; + + const uptr kNumAllocs = 100000; + const uptr kNumIter = 10; + for (uptr iter = 0; iter < kNumIter; iter++) { + std::vector allocated; + for (uptr i = 0; i < kNumAllocs; i++) { + uptr size = (i % (1 << 14)) + 1; + if ((i % 1024) == 0) + size = 1 << (10 + (i % 14)); + void *x = a->Allocate(&cache, size, 1); + uptr *meta = reinterpret_cast(a->GetMetaData(x)); + CHECK_EQ(*meta, 0); + *meta = size; + allocated.push_back(x); + } + + random_shuffle(allocated.begin(), allocated.end()); + + for (uptr i = 0; i < kNumAllocs; i++) { + void *x = allocated[i]; + uptr *meta = reinterpret_cast(a->GetMetaData(x)); + CHECK_NE(*meta, 0); + CHECK(a->PointerIsMine(x)); + *meta = 0; + a->Deallocate(&cache, x); + } + allocated.clear(); + a->SwallowCache(&cache); + } + a->DestroyCache(&cache); + a->TestOnlyUnmap(); +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, CombinedAllocator64) { + TestCombinedAllocator, + SizeClassAllocatorLocalCache > (); +} + +TEST(SanitizerCommon, CombinedAllocator64Compact) { + TestCombinedAllocator, + SizeClassAllocatorLocalCache > (); +} +#endif + +TEST(SanitizerCommon, CombinedAllocator32Compact) { + TestCombinedAllocator, + SizeClassAllocatorLocalCache > (); +} + +template +void TestSizeClassAllocatorLocalCache() { + AllocatorCache cache; + typedef typename AllocatorCache::Allocator Allocator; + Allocator *a = new Allocator(); + + a->Init(); + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + + const uptr kNumAllocs = 10000; + const int kNumIter = 100; + uptr saved_total = 0; + for (int class_id = 1; class_id <= 5; class_id++) { + for (int it = 0; it < kNumIter; it++) { + void *allocated[kNumAllocs]; + for (uptr i = 0; i < kNumAllocs; i++) { + allocated[i] = cache.Allocate(a, class_id); + } + for (uptr i = 0; i < kNumAllocs; i++) { + cache.Deallocate(a, class_id, allocated[i]); + } + cache.Drain(a); + uptr total_allocated = a->TotalMemoryUsed(); + if (it) + CHECK_EQ(saved_total, total_allocated); + saved_total = total_allocated; + } + } + + a->TestOnlyUnmap(); + delete a; +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64LocalCache) { + TestSizeClassAllocatorLocalCache< + SizeClassAllocatorLocalCache >(); +} + +TEST(SanitizerCommon, SizeClassAllocator64CompactLocalCache) { + TestSizeClassAllocatorLocalCache< + SizeClassAllocatorLocalCache >(); +} +#endif + +TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) { + TestSizeClassAllocatorLocalCache< + SizeClassAllocatorLocalCache >(); +} + +#if SANITIZER_WORDSIZE == 64 +typedef SizeClassAllocatorLocalCache AllocatorCache; +static AllocatorCache static_allocator_cache; + +void *AllocatorLeakTestWorker(void *arg) { + typedef AllocatorCache::Allocator Allocator; + Allocator *a = (Allocator*)(arg); + static_allocator_cache.Allocate(a, 10); + static_allocator_cache.Drain(a); + return 0; +} + +TEST(SanitizerCommon, AllocatorLeakTest) { + typedef AllocatorCache::Allocator Allocator; + Allocator a; + a.Init(); + uptr total_used_memory = 0; + for (int i = 0; i < 100; i++) { + pthread_t t; + EXPECT_EQ(0, pthread_create(&t, 0, AllocatorLeakTestWorker, &a)); + EXPECT_EQ(0, pthread_join(t, 0)); + if (i == 0) + total_used_memory = a.TotalMemoryUsed(); + EXPECT_EQ(a.TotalMemoryUsed(), total_used_memory); + } + + a.TestOnlyUnmap(); +} + +// Struct which is allocated to pass info to new threads. The new thread frees +// it. +struct NewThreadParams { + AllocatorCache *thread_cache; + AllocatorCache::Allocator *allocator; + uptr class_id; +}; + +// Called in a new thread. Just frees its argument. +static void *DeallocNewThreadWorker(void *arg) { + NewThreadParams *params = reinterpret_cast(arg); + params->thread_cache->Deallocate(params->allocator, params->class_id, params); + return NULL; +} + +// The allocator cache is supposed to be POD and zero initialized. We should be +// able to call Deallocate on a zeroed cache, and it will self-initialize. +TEST(Allocator, AllocatorCacheDeallocNewThread) { + AllocatorCache::Allocator allocator; + allocator.Init(); + AllocatorCache main_cache; + AllocatorCache child_cache; + memset(&main_cache, 0, sizeof(main_cache)); + memset(&child_cache, 0, sizeof(child_cache)); + + uptr class_id = DefaultSizeClassMap::ClassID(sizeof(NewThreadParams)); + NewThreadParams *params = reinterpret_cast( + main_cache.Allocate(&allocator, class_id)); + params->thread_cache = &child_cache; + params->allocator = &allocator; + params->class_id = class_id; + pthread_t t; + EXPECT_EQ(0, pthread_create(&t, 0, DeallocNewThreadWorker, params)); + EXPECT_EQ(0, pthread_join(t, 0)); +} +#endif + +TEST(Allocator, Basic) { + char *p = (char*)InternalAlloc(10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)InternalAlloc(20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + InternalFree(p); + InternalFree(p2); +} + +TEST(Allocator, Stress) { + const int kCount = 1000; + char *ptrs[kCount]; + unsigned rnd = 42; + for (int i = 0; i < kCount; i++) { + uptr sz = my_rand_r(&rnd) % 1000; + char *p = (char*)InternalAlloc(sz); + EXPECT_NE(p, (char*)0); + ptrs[i] = p; + } + for (int i = 0; i < kCount; i++) { + InternalFree(ptrs[i]); + } +} + +TEST(Allocator, InternalAllocFailure) { + EXPECT_DEATH(Ident(InternalAlloc(10 << 20)), + "Unexpected mmap in InternalAllocator!"); +} + +TEST(Allocator, ScopedBuffer) { + const int kSize = 512; + { + InternalScopedBuffer int_buf(kSize); + EXPECT_EQ(sizeof(int) * kSize, int_buf.size()); // NOLINT + } + InternalScopedBuffer char_buf(kSize); + EXPECT_EQ(sizeof(char) * kSize, char_buf.size()); // NOLINT + internal_memset(char_buf.data(), 'c', kSize); + for (int i = 0; i < kSize; i++) { + EXPECT_EQ('c', char_buf[i]); + } +} + +void IterationTestCallback(uptr chunk, void *arg) { + reinterpret_cast *>(arg)->insert(chunk); +} + +template +void TestSizeClassAllocatorIteration() { + Allocator *a = new Allocator; + a->Init(); + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + + static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, + 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; + + std::vector allocated; + + // Allocate a bunch of chunks. + for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { + uptr size = sizes[s]; + if (!a->CanAllocate(size, 1)) continue; + // printf("s = %ld\n", size); + uptr n_iter = std::max((uptr)6, 80000 / size); + // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); + for (uptr j = 0; j < n_iter; j++) { + uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); + void *x = cache.Allocate(a, class_id0); + allocated.push_back(x); + } + } + + std::set reported_chunks; + a->ForceLock(); + a->ForEachChunk(IterationTestCallback, &reported_chunks); + a->ForceUnlock(); + + for (uptr i = 0; i < allocated.size(); i++) { + // Don't use EXPECT_NE. Reporting the first mismatch is enough. + ASSERT_NE(reported_chunks.find(reinterpret_cast(allocated[i])), + reported_chunks.end()); + } + + a->TestOnlyUnmap(); + delete a; +} + +#if SANITIZER_WORDSIZE == 64 +TEST(SanitizerCommon, SizeClassAllocator64Iteration) { + TestSizeClassAllocatorIteration(); +} +#endif + +TEST(SanitizerCommon, SizeClassAllocator32Iteration) { + TestSizeClassAllocatorIteration(); +} + +TEST(SanitizerCommon, LargeMmapAllocatorIteration) { + LargeMmapAllocator<> a; + a.Init(); + AllocatorStats stats; + stats.Init(); + + static const uptr kNumAllocs = 1000; + char *allocated[kNumAllocs]; + static const uptr size = 40; + // Allocate some. + for (uptr i = 0; i < kNumAllocs; i++) + allocated[i] = (char *)a.Allocate(&stats, size, 1); + + std::set reported_chunks; + a.ForceLock(); + a.ForEachChunk(IterationTestCallback, &reported_chunks); + a.ForceUnlock(); + + for (uptr i = 0; i < kNumAllocs; i++) { + // Don't use EXPECT_NE. Reporting the first mismatch is enough. + ASSERT_NE(reported_chunks.find(reinterpret_cast(allocated[i])), + reported_chunks.end()); + } + for (uptr i = 0; i < kNumAllocs; i++) + a.Deallocate(&stats, allocated[i]); +} + +TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) { + LargeMmapAllocator<> a; + a.Init(); + AllocatorStats stats; + stats.Init(); + + static const uptr kNumAllocs = 1024; + static const uptr kNumExpectedFalseLookups = 10000000; + char *allocated[kNumAllocs]; + static const uptr size = 4096; + // Allocate some. + for (uptr i = 0; i < kNumAllocs; i++) { + allocated[i] = (char *)a.Allocate(&stats, size, 1); + } + + a.ForceLock(); + for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { + // if ((i & (i - 1)) == 0) fprintf(stderr, "[%zd]\n", i); + char *p1 = allocated[i % kNumAllocs]; + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1)); + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size / 2)); + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size - 1)); + EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 - 100)); + } + + for (uptr i = 0; i < kNumExpectedFalseLookups; i++) { + void *p = reinterpret_cast(i % 1024); + EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); + p = reinterpret_cast(~0L - (i % 1024)); + EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); + } + a.ForceUnlock(); + + for (uptr i = 0; i < kNumAllocs; i++) + a.Deallocate(&stats, allocated[i]); +} + + +#if SANITIZER_WORDSIZE == 64 +// Regression test for out-of-memory condition in PopulateFreeList(). +TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { + // In a world where regions are small and chunks are huge... + typedef SizeClassMap<63, 128, 16> SpecialSizeClassMap; + typedef SizeClassAllocator64 SpecialAllocator64; + const uptr kRegionSize = + kAllocatorSize / SpecialSizeClassMap::kNumClassesRounded; + SpecialAllocator64 *a = new SpecialAllocator64; + a->Init(); + SizeClassAllocatorLocalCache cache; + memset(&cache, 0, sizeof(cache)); + cache.Init(0); + + // ...one man is on a mission to overflow a region with a series of + // successive allocations. + const uptr kClassID = 107; + const uptr kAllocationSize = DefaultSizeClassMap::Size(kClassID); + ASSERT_LT(2 * kAllocationSize, kRegionSize); + ASSERT_GT(3 * kAllocationSize, kRegionSize); + cache.Allocate(a, kClassID); + EXPECT_DEATH(cache.Allocate(a, kClassID) && cache.Allocate(a, kClassID), + "The process has exhausted"); + a->TestOnlyUnmap(); + delete a; +} +#endif + +#endif // #if TSAN_DEBUG==0 diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc new file mode 100644 index 00000000..f6a944f6 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc @@ -0,0 +1,162 @@ +//===-- sanitizer_allocator_testlib.cc ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Malloc replacement library based on CombinedAllocator. +// The primary purpose of this file is an end-to-end integration test +// for CombinedAllocator. +//===----------------------------------------------------------------------===// +/* Usage: +clang++ -fno-exceptions -g -fPIC -I. -I../include -Isanitizer \ + sanitizer_common/tests/sanitizer_allocator_testlib.cc \ + sanitizer_common/sanitizer_*.cc -shared -lpthread -o testmalloc.so +LD_PRELOAD=`pwd`/testmalloc.so /your/app +*/ +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_common.h" +#include +#include +#include +#include +#include + +#ifndef SANITIZER_MALLOC_HOOK +# define SANITIZER_MALLOC_HOOK(p, s) +#endif + +#ifndef SANITIZER_FREE_HOOK +# define SANITIZER_FREE_HOOK(p) +#endif + +namespace { +static const uptr kAllocatorSpace = 0x600000000000ULL; +static const uptr kAllocatorSize = 0x10000000000ULL; // 1T. + +typedef SizeClassAllocator64 PrimaryAllocator; +typedef SizeClassAllocatorLocalCache AllocatorCache; +typedef LargeMmapAllocator<> SecondaryAllocator; +typedef CombinedAllocator Allocator; + +static Allocator allocator; +static bool global_inited; +static THREADLOCAL AllocatorCache cache; +static THREADLOCAL bool thread_inited; +static pthread_key_t pkey; + +static void thread_dtor(void *v) { + if ((uptr)v != 3) { + pthread_setspecific(pkey, (void*)((uptr)v + 1)); + return; + } + allocator.SwallowCache(&cache); +} + +static void NOINLINE thread_init() { + if (!global_inited) { + global_inited = true; + allocator.Init(); + pthread_key_create(&pkey, thread_dtor); + } + thread_inited = true; + pthread_setspecific(pkey, (void*)1); + cache.Init(); +} +} // namespace + +extern "C" { +void *malloc(size_t size) { + if (UNLIKELY(!thread_inited)) + thread_init(); + void *p = allocator.Allocate(&cache, size, 8); + SANITIZER_MALLOC_HOOK(p, size); + return p; +} + +void free(void *p) { + if (UNLIKELY(!thread_inited)) + thread_init(); + SANITIZER_FREE_HOOK(p); + allocator.Deallocate(&cache, p); +} + +void *calloc(size_t nmemb, size_t size) { + if (UNLIKELY(!thread_inited)) + thread_init(); + size *= nmemb; + void *p = allocator.Allocate(&cache, size, 8, false); + memset(p, 0, size); + SANITIZER_MALLOC_HOOK(p, size); + return p; +} + +void *realloc(void *p, size_t size) { + if (UNLIKELY(!thread_inited)) + thread_init(); + if (p) { + SANITIZER_FREE_HOOK(p); + } + p = allocator.Reallocate(&cache, p, size, 8); + if (p) { + SANITIZER_MALLOC_HOOK(p, size); + } + return p; +} + +void *memalign(size_t alignment, size_t size) { + if (UNLIKELY(!thread_inited)) + thread_init(); + void *p = allocator.Allocate(&cache, size, alignment); + SANITIZER_MALLOC_HOOK(p, size); + return p; +} + +int posix_memalign(void **memptr, size_t alignment, size_t size) { + if (UNLIKELY(!thread_inited)) + thread_init(); + *memptr = allocator.Allocate(&cache, size, alignment); + SANITIZER_MALLOC_HOOK(*memptr, size); + return 0; +} + +void *valloc(size_t size) { + if (UNLIKELY(!thread_inited)) + thread_init(); + if (size == 0) + size = GetPageSizeCached(); + void *p = allocator.Allocate(&cache, size, GetPageSizeCached()); + SANITIZER_MALLOC_HOOK(p, size); + return p; +} + +void cfree(void *p) ALIAS("free"); +void *pvalloc(size_t size) ALIAS("valloc"); +void *__libc_memalign(size_t alignment, size_t size) ALIAS("memalign"); + +void malloc_usable_size() { +} + +void mallinfo() { +} + +void mallopt() { +} +} // extern "C" + +namespace std { + struct nothrow_t; +} + +void *operator new(size_t size) ALIAS("malloc"); +void *operator new[](size_t size) ALIAS("malloc"); +void *operator new(size_t size, std::nothrow_t const&) ALIAS("malloc"); +void *operator new[](size_t size, std::nothrow_t const&) ALIAS("malloc"); +void operator delete(void *ptr) ALIAS("free"); +void operator delete[](void *ptr) ALIAS("free"); +void operator delete(void *ptr, std::nothrow_t const&) ALIAS("free"); +void operator delete[](void *ptr, std::nothrow_t const&) ALIAS("free"); diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_atomic_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_atomic_test.cc new file mode 100644 index 00000000..a4a97c43 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_atomic_test.cc @@ -0,0 +1,55 @@ +//===-- sanitizer_atomic_test.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_atomic.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +// Clang crashes while compiling this test for Android: +// http://llvm.org/bugs/show_bug.cgi?id=15587 +#if !SANITIZER_ANDROID +template +void CheckAtomicCompareExchange() { + typedef typename T::Type Type; + { + Type old_val = 42; + Type new_val = 24; + Type var = old_val; + EXPECT_TRUE(atomic_compare_exchange_strong((T*)&var, &old_val, new_val, + memory_order_relaxed)); + EXPECT_FALSE(atomic_compare_exchange_strong((T*)&var, &old_val, new_val, + memory_order_relaxed)); + EXPECT_EQ(new_val, old_val); + } + { + Type old_val = 42; + Type new_val = 24; + Type var = old_val; + EXPECT_TRUE(atomic_compare_exchange_weak((T*)&var, &old_val, new_val, + memory_order_relaxed)); + EXPECT_FALSE(atomic_compare_exchange_weak((T*)&var, &old_val, new_val, + memory_order_relaxed)); + EXPECT_EQ(new_val, old_val); + } +} + +TEST(SanitizerCommon, AtomicCompareExchangeTest) { + CheckAtomicCompareExchange(); + CheckAtomicCompareExchange(); + CheckAtomicCompareExchange(); + CheckAtomicCompareExchange(); + CheckAtomicCompareExchange(); +} +#endif //!SANITIZER_ANDROID + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc new file mode 100644 index 00000000..608f9048 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc @@ -0,0 +1,259 @@ +//===-- sanitizer_common_test.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_platform.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +static bool IsSorted(const uptr *array, uptr n) { + for (uptr i = 1; i < n; i++) { + if (array[i] < array[i - 1]) return false; + } + return true; +} + +TEST(SanitizerCommon, SortTest) { + uptr array[100]; + uptr n = 100; + // Already sorted. + for (uptr i = 0; i < n; i++) { + array[i] = i; + } + SortArray(array, n); + EXPECT_TRUE(IsSorted(array, n)); + // Reverse order. + for (uptr i = 0; i < n; i++) { + array[i] = n - 1 - i; + } + SortArray(array, n); + EXPECT_TRUE(IsSorted(array, n)); + // Mixed order. + for (uptr i = 0; i < n; i++) { + array[i] = (i % 2 == 0) ? i : n - 1 - i; + } + SortArray(array, n); + EXPECT_TRUE(IsSorted(array, n)); + // All equal. + for (uptr i = 0; i < n; i++) { + array[i] = 42; + } + SortArray(array, n); + EXPECT_TRUE(IsSorted(array, n)); + // All but one sorted. + for (uptr i = 0; i < n - 1; i++) { + array[i] = i; + } + array[n - 1] = 42; + SortArray(array, n); + EXPECT_TRUE(IsSorted(array, n)); + // Minimal case - sort three elements. + array[0] = 1; + array[1] = 0; + SortArray(array, 2); + EXPECT_TRUE(IsSorted(array, 2)); +} + +TEST(SanitizerCommon, MmapAlignedOrDie) { + uptr PageSize = GetPageSizeCached(); + for (uptr size = 1; size <= 32; size *= 2) { + for (uptr alignment = 1; alignment <= 32; alignment *= 2) { + for (int iter = 0; iter < 100; iter++) { + uptr res = (uptr)MmapAlignedOrDie( + size * PageSize, alignment * PageSize, "MmapAlignedOrDieTest"); + EXPECT_EQ(0U, res % (alignment * PageSize)); + internal_memset((void*)res, 1, size * PageSize); + UnmapOrDie((void*)res, size * PageSize); + } + } + } +} + +#if SANITIZER_LINUX +TEST(SanitizerCommon, SanitizerSetThreadName) { + const char *names[] = { + "0123456789012", + "01234567890123", + "012345678901234", // Larger names will be truncated on linux. + }; + + for (size_t i = 0; i < ARRAY_SIZE(names); i++) { + EXPECT_TRUE(SanitizerSetThreadName(names[i])); + char buff[100]; + EXPECT_TRUE(SanitizerGetThreadName(buff, sizeof(buff) - 1)); + EXPECT_EQ(0, internal_strcmp(buff, names[i])); + } +} +#endif + +TEST(SanitizerCommon, InternalMmapVector) { + InternalMmapVector vector(1); + for (uptr i = 0; i < 100; i++) { + EXPECT_EQ(i, vector.size()); + vector.push_back(i); + } + for (uptr i = 0; i < 100; i++) { + EXPECT_EQ(i, vector[i]); + } + for (int i = 99; i >= 0; i--) { + EXPECT_EQ((uptr)i, vector.back()); + vector.pop_back(); + EXPECT_EQ((uptr)i, vector.size()); + } +} + +void TestThreadInfo(bool main) { + uptr stk_addr = 0; + uptr stk_size = 0; + uptr tls_addr = 0; + uptr tls_size = 0; + GetThreadStackAndTls(main, &stk_addr, &stk_size, &tls_addr, &tls_size); + + int stack_var; + EXPECT_NE(stk_addr, (uptr)0); + EXPECT_NE(stk_size, (uptr)0); + EXPECT_GT((uptr)&stack_var, stk_addr); + EXPECT_LT((uptr)&stack_var, stk_addr + stk_size); + +#if SANITIZER_LINUX && defined(__x86_64__) + static __thread int thread_var; + EXPECT_NE(tls_addr, (uptr)0); + EXPECT_NE(tls_size, (uptr)0); + EXPECT_GT((uptr)&thread_var, tls_addr); + EXPECT_LT((uptr)&thread_var, tls_addr + tls_size); + + // Ensure that tls and stack do not intersect. + uptr tls_end = tls_addr + tls_size; + EXPECT_TRUE(tls_addr < stk_addr || tls_addr >= stk_addr + stk_size); + EXPECT_TRUE(tls_end < stk_addr || tls_end >= stk_addr + stk_size); + EXPECT_TRUE((tls_addr < stk_addr) == (tls_end < stk_addr)); +#endif +} + +static void *WorkerThread(void *arg) { + TestThreadInfo(false); + return 0; +} + +TEST(SanitizerCommon, ThreadStackTlsMain) { + InitTlsSize(); + TestThreadInfo(true); +} + +TEST(SanitizerCommon, ThreadStackTlsWorker) { + InitTlsSize(); + pthread_t t; + pthread_create(&t, 0, WorkerThread, 0); + pthread_join(t, 0); +} + +bool UptrLess(uptr a, uptr b) { + return a < b; +} + +TEST(SanitizerCommon, InternalBinarySearch) { + static const uptr kSize = 5; + uptr arr[kSize]; + for (uptr i = 0; i < kSize; i++) arr[i] = i * i; + + for (uptr i = 0; i < kSize; i++) + ASSERT_EQ(InternalBinarySearch(arr, 0, kSize, i * i, UptrLess), i); + + ASSERT_EQ(InternalBinarySearch(arr, 0, kSize, 7, UptrLess), kSize + 1); +} + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +TEST(SanitizerCommon, FindPathToBinary) { + char *true_path = FindPathToBinary("true"); + EXPECT_NE((char*)0, internal_strstr(true_path, "/bin/true")); + InternalFree(true_path); + EXPECT_EQ(0, FindPathToBinary("unexisting_binary.ergjeorj")); +} +#endif + +TEST(SanitizerCommon, StripPathPrefix) { + EXPECT_EQ(0, StripPathPrefix(0, "prefix")); + EXPECT_STREQ("foo", StripPathPrefix("foo", 0)); + EXPECT_STREQ("dir/file.cc", + StripPathPrefix("/usr/lib/dir/file.cc", "/usr/lib/")); + EXPECT_STREQ("/file.cc", StripPathPrefix("/usr/myroot/file.cc", "/myroot")); + EXPECT_STREQ("file.h", StripPathPrefix("/usr/lib/./file.h", "/usr/lib/")); +} + +TEST(SanitizerCommon, InternalScopedString) { + InternalScopedString str(10); + EXPECT_EQ(0U, str.length()); + EXPECT_STREQ("", str.data()); + + str.append("foo"); + EXPECT_EQ(3U, str.length()); + EXPECT_STREQ("foo", str.data()); + + int x = 1234; + str.append("%d", x); + EXPECT_EQ(7U, str.length()); + EXPECT_STREQ("foo1234", str.data()); + + str.append("%d", x); + EXPECT_EQ(9U, str.length()); + EXPECT_STREQ("foo123412", str.data()); + + str.clear(); + EXPECT_EQ(0U, str.length()); + EXPECT_STREQ("", str.data()); + + str.append("0123456789"); + EXPECT_EQ(9U, str.length()); + EXPECT_STREQ("012345678", str.data()); +} + +TEST(SanitizerCommon, PrintSourceLocation) { + InternalScopedString str(128); + PrintSourceLocation(&str, "/dir/file.cc", 10, 5); + EXPECT_STREQ("/dir/file.cc:10:5", str.data()); + + str.clear(); + PrintSourceLocation(&str, "/dir/file.cc", 11, 0); + EXPECT_STREQ("/dir/file.cc:11", str.data()); + + str.clear(); + PrintSourceLocation(&str, "/dir/file.cc", 0, 0); + EXPECT_STREQ("/dir/file.cc", str.data()); + + // Check that we strip file prefix if necessary. + const char *old_strip_path_prefix = common_flags()->strip_path_prefix; + common_flags()->strip_path_prefix = "/dir/"; + str.clear(); + PrintSourceLocation(&str, "/dir/file.cc", 10, 5); + EXPECT_STREQ("file.cc:10:5", str.data()); + common_flags()->strip_path_prefix = old_strip_path_prefix; +} + +TEST(SanitizerCommon, PrintModuleAndOffset) { + InternalScopedString str(128); + PrintModuleAndOffset(&str, "/dir/exe", 0x123); + EXPECT_STREQ("(/dir/exe+0x123)", str.data()); + + // Check that we strip file prefix if necessary. + const char *old_strip_path_prefix = common_flags()->strip_path_prefix; + common_flags()->strip_path_prefix = "/dir/"; + str.clear(); + PrintModuleAndOffset(&str, "/dir/exe", 0x123); + EXPECT_STREQ("(exe+0x123)", str.data()); + common_flags()->strip_path_prefix = old_strip_path_prefix; +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc new file mode 100644 index 00000000..cd3cac11 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc @@ -0,0 +1,86 @@ +//===-- sanitizer_flags_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "gtest/gtest.h" + +#include + +namespace __sanitizer { + +static const char kFlagName[] = "flag_name"; + +template +static void TestFlag(T start_value, const char *env, T final_value) { + T flag = start_value; + ParseFlag(env, &flag, kFlagName); + EXPECT_EQ(final_value, flag); +} + +static void TestStrFlag(const char *start_value, const char *env, + const char *final_value) { + const char *flag = start_value; + ParseFlag(env, &flag, kFlagName); + EXPECT_EQ(0, internal_strcmp(final_value, flag)); +} + +TEST(SanitizerCommon, BooleanFlags) { + TestFlag(true, "--flag_name", true); + TestFlag(false, "flag_name", false); + TestFlag(false, "--flag_name=1", true); + TestFlag(true, "asdas flag_name=0 asdas", false); + TestFlag(true, " --flag_name=0 ", false); + TestFlag(false, "flag_name=yes", true); + TestFlag(false, "flag_name=true", true); + TestFlag(true, "flag_name=no", false); + TestFlag(true, "flag_name=false", false); +} + +TEST(SanitizerCommon, IntFlags) { + TestFlag(-11, 0, -11); + TestFlag(-11, "flag_name", 0); + TestFlag(-11, "--flag_name=", 0); + TestFlag(-11, "--flag_name=42", 42); + TestFlag(-11, "--flag_name=-42", -42); +} + +TEST(SanitizerCommon, StrFlags) { + TestStrFlag("zzz", 0, "zzz"); + TestStrFlag("zzz", "flag_name", ""); + TestStrFlag("zzz", "--flag_name=", ""); + TestStrFlag("", "--flag_name=abc", "abc"); + TestStrFlag("", "--flag_name='abc zxc'", "abc zxc"); + TestStrFlag("", "--flag_name='abc zxcc'", "abc zxcc"); + TestStrFlag("", "--flag_name=\"abc qwe\" asd", "abc qwe"); + TestStrFlag("", "other_flag_name=zzz", ""); +} + +static void TestTwoFlags(const char *env, bool expected_flag1, + const char *expected_flag2) { + bool flag1 = !expected_flag1; + const char *flag2 = ""; + ParseFlag(env, &flag1, "flag1"); + ParseFlag(env, &flag2, "flag2"); + EXPECT_EQ(expected_flag1, flag1); + EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2)); +} + +TEST(SanitizerCommon, MultipleFlags) { + TestTwoFlags("flag1=1 flag2='zzz'", true, "zzz"); + TestTwoFlags("flag2='qxx' flag1=0", false, "qxx"); + TestTwoFlags("flag1=false:flag2='zzz'", false, "zzz"); + TestTwoFlags("flag2=qxx:flag1=yes", true, "qxx"); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc new file mode 100644 index 00000000..154d9860 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_ioctl_test.cc @@ -0,0 +1,77 @@ +//===-- sanitizer_ioctl_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for ioctl interceptor implementation in sanitizer_common. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include +#include + +#include "interception/interception.h" +#include "sanitizer_test_utils.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include "sanitizer_common/sanitizer_common.h" +#include "gtest/gtest.h" + + +using namespace __sanitizer; + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, sz) \ + do { \ + (void) ctx; \ + (void) ptr; \ + (void) sz; \ + } while (0) +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sz) \ + do { \ + (void) ctx; \ + (void) ptr; \ + (void) sz; \ + } while (0) + +#include "sanitizer_common/sanitizer_common_interceptors_ioctl.inc" + +static struct IoctlInit { + IoctlInit() { + ioctl_init(); + // Avoid unused function warnings. + (void)&ioctl_common_pre; + (void)&ioctl_common_post; + } +} ioctl_static_initializer; + +TEST(SanitizerIoctl, Fixup) { + EXPECT_EQ((unsigned)FIONBIO, ioctl_request_fixup(FIONBIO)); + + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(0, 16))); + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 16))); + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(1, 17))); + EXPECT_EQ(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(31, 16))); + EXPECT_NE(EVIOCGBIT(0, 0), ioctl_request_fixup(EVIOCGBIT(32, 16))); + + EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(0))); + EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(5))); + EXPECT_EQ(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(63))); + EXPECT_NE(EVIOCGABS(0), ioctl_request_fixup(EVIOCGABS(64))); + + EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(0))); + EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(5))); + EXPECT_EQ(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(63))); + EXPECT_NE(EVIOCSABS(0), ioctl_request_fixup(EVIOCSABS(64))); + + const ioctl_desc *desc = ioctl_lookup(EVIOCGKEY(16)); + EXPECT_NE((void *)0, desc); + EXPECT_EQ(EVIOCGKEY(0), desc->req); +} + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc new file mode 100644 index 00000000..c4f3d803 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc @@ -0,0 +1,124 @@ +//===-- sanitizer_libc_test.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Tests for sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_platform.h" +#include "gtest/gtest.h" + +#if SANITIZER_LINUX || SANITIZER_MAC +# define SANITIZER_TEST_HAS_STAT_H 1 +# include +#else +# define SANITIZER_TEST_HAS_STAT_H 0 +#endif + +// A regression test for internal_memmove() implementation. +TEST(SanitizerCommon, InternalMemmoveRegression) { + char src[] = "Hello World"; + char *dest = src + 6; + __sanitizer::internal_memmove(dest, src, 5); + EXPECT_EQ(dest[0], src[0]); + EXPECT_EQ(dest[4], src[4]); +} + +TEST(SanitizerCommon, mem_is_zero) { + size_t size = 128; + char *x = new char[size]; + memset(x, 0, size); + for (size_t pos = 0; pos < size; pos++) { + x[pos] = 1; + for (size_t beg = 0; beg < size; beg++) { + for (size_t end = beg; end < size; end++) { + // fprintf(stderr, "pos %zd beg %zd end %zd \n", pos, beg, end); + if (beg <= pos && pos < end) + EXPECT_FALSE(__sanitizer::mem_is_zero(x + beg, end - beg)); + else + EXPECT_TRUE(__sanitizer::mem_is_zero(x + beg, end - beg)); + } + } + x[pos] = 0; + } + delete [] x; +} + +struct stat_and_more { + struct stat st; + unsigned char z; +}; + +TEST(SanitizerCommon, FileOps) { + const char *str1 = "qwerty"; + uptr len1 = internal_strlen(str1); + const char *str2 = "zxcv"; + uptr len2 = internal_strlen(str2); + + u32 uid = GetUid(); + char temp_filename[128]; +#if SANITIZER_ANDROID + // I don't know a way to query temp directory location on Android without + // going through Java interfaces. The code below is not ideal, but should + // work. May require "adb root", but it is needed for almost any use of ASan + // on Android already. + internal_snprintf(temp_filename, sizeof(temp_filename), + "%s/sanitizer_common.tmp.%d", + GetEnv("EXTERNAL_STORAGE"), uid); +#else + internal_snprintf(temp_filename, sizeof(temp_filename), + "/tmp/sanitizer_common.tmp.%d", uid); +#endif + uptr openrv = OpenFile(temp_filename, true); + EXPECT_FALSE(internal_iserror(openrv)); + fd_t fd = openrv; + EXPECT_EQ(len1, internal_write(fd, str1, len1)); + EXPECT_EQ(len2, internal_write(fd, str2, len2)); + internal_close(fd); + + openrv = OpenFile(temp_filename, false); + EXPECT_FALSE(internal_iserror(openrv)); + fd = openrv; + uptr fsize = internal_filesize(fd); + EXPECT_EQ(len1 + len2, fsize); + +#if SANITIZER_TEST_HAS_STAT_H + struct stat st1, st2, st3; + EXPECT_EQ(0u, internal_stat(temp_filename, &st1)); + EXPECT_EQ(0u, internal_lstat(temp_filename, &st2)); + EXPECT_EQ(0u, internal_fstat(fd, &st3)); + EXPECT_EQ(fsize, (uptr)st3.st_size); + + // Verify that internal_fstat does not write beyond the end of the supplied + // buffer. + struct stat_and_more sam; + memset(&sam, 0xAB, sizeof(sam)); + EXPECT_EQ(0u, internal_fstat(fd, &sam.st)); + EXPECT_EQ(0xAB, sam.z); + EXPECT_NE(0xAB, sam.st.st_size); + EXPECT_NE(0, sam.st.st_size); +#endif + + char buf[64] = {}; + EXPECT_EQ(len1, internal_read(fd, buf, len1)); + EXPECT_EQ(0, internal_memcmp(buf, str1, len1)); + EXPECT_EQ((char)0, buf[len1 + 1]); + internal_memset(buf, 0, len1); + EXPECT_EQ(len2, internal_read(fd, buf, len2)); + EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); + internal_close(fd); +} + +TEST(SanitizerCommon, InternalStrFunctions) { + const char *haystack = "haystack"; + EXPECT_EQ(haystack + 2, internal_strchr(haystack, 'y')); + EXPECT_EQ(haystack + 2, internal_strchrnul(haystack, 'y')); + EXPECT_EQ(0, internal_strchr(haystack, 'z')); + EXPECT_EQ(haystack + 8, internal_strchrnul(haystack, 'z')); +} diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc new file mode 100644 index 00000000..592d9c3e --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_linux_test.cc @@ -0,0 +1,260 @@ +//===-- sanitizer_linux_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for sanitizer_linux.h +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_common/sanitizer_linux.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "gtest/gtest.h" + +#include +#include +#include + +#include +#include + +namespace __sanitizer { + +struct TidReporterArgument { + TidReporterArgument() { + pthread_mutex_init(&terminate_thread_mutex, NULL); + pthread_mutex_init(&tid_reported_mutex, NULL); + pthread_cond_init(&terminate_thread_cond, NULL); + pthread_cond_init(&tid_reported_cond, NULL); + terminate_thread = false; + } + + ~TidReporterArgument() { + pthread_mutex_destroy(&terminate_thread_mutex); + pthread_mutex_destroy(&tid_reported_mutex); + pthread_cond_destroy(&terminate_thread_cond); + pthread_cond_destroy(&tid_reported_cond); + } + + pid_t reported_tid; + // For signaling to spawned threads that they should terminate. + pthread_cond_t terminate_thread_cond; + pthread_mutex_t terminate_thread_mutex; + bool terminate_thread; + // For signaling to main thread that a child thread has reported its tid. + pthread_cond_t tid_reported_cond; + pthread_mutex_t tid_reported_mutex; + + private: + // Disallow evil constructors + TidReporterArgument(const TidReporterArgument &); + void operator=(const TidReporterArgument &); +}; + +class ThreadListerTest : public ::testing::Test { + protected: + virtual void SetUp() { + pthread_t pthread_id; + pid_t tid; + for (uptr i = 0; i < kThreadCount; i++) { + SpawnTidReporter(&pthread_id, &tid); + pthread_ids_.push_back(pthread_id); + tids_.push_back(tid); + } + } + + virtual void TearDown() { + pthread_mutex_lock(&thread_arg.terminate_thread_mutex); + thread_arg.terminate_thread = true; + pthread_cond_broadcast(&thread_arg.terminate_thread_cond); + pthread_mutex_unlock(&thread_arg.terminate_thread_mutex); + for (uptr i = 0; i < pthread_ids_.size(); i++) + pthread_join(pthread_ids_[i], NULL); + } + + void SpawnTidReporter(pthread_t *pthread_id, pid_t *tid); + + static const uptr kThreadCount = 20; + + std::vector pthread_ids_; + std::vector tids_; + + TidReporterArgument thread_arg; +}; + +// Writes its TID once to reported_tid and waits until signaled to terminate. +void *TidReporterThread(void *argument) { + TidReporterArgument *arg = reinterpret_cast(argument); + pthread_mutex_lock(&arg->tid_reported_mutex); + arg->reported_tid = GetTid(); + pthread_cond_broadcast(&arg->tid_reported_cond); + pthread_mutex_unlock(&arg->tid_reported_mutex); + + pthread_mutex_lock(&arg->terminate_thread_mutex); + while (!arg->terminate_thread) + pthread_cond_wait(&arg->terminate_thread_cond, + &arg->terminate_thread_mutex); + pthread_mutex_unlock(&arg->terminate_thread_mutex); + return NULL; +} + +void ThreadListerTest::SpawnTidReporter(pthread_t *pthread_id, + pid_t *tid) { + pthread_mutex_lock(&thread_arg.tid_reported_mutex); + thread_arg.reported_tid = -1; + ASSERT_EQ(0, pthread_create(pthread_id, NULL, + TidReporterThread, + &thread_arg)); + while (thread_arg.reported_tid == -1) + pthread_cond_wait(&thread_arg.tid_reported_cond, + &thread_arg.tid_reported_mutex); + pthread_mutex_unlock(&thread_arg.tid_reported_mutex); + *tid = thread_arg.reported_tid; +} + +static std::vector ReadTidsToVector(ThreadLister *thread_lister) { + std::vector listed_tids; + pid_t tid; + while ((tid = thread_lister->GetNextTID()) >= 0) + listed_tids.push_back(tid); + EXPECT_FALSE(thread_lister->error()); + return listed_tids; +} + +static bool Includes(std::vector first, std::vector second) { + std::sort(first.begin(), first.end()); + std::sort(second.begin(), second.end()); + return std::includes(first.begin(), first.end(), + second.begin(), second.end()); +} + +static bool HasElement(std::vector vector, pid_t element) { + return std::find(vector.begin(), vector.end(), element) != vector.end(); +} + +// ThreadLister's output should include the current thread's TID and the TID of +// every thread we spawned. +TEST_F(ThreadListerTest, ThreadListerSeesAllSpawnedThreads) { + pid_t self_tid = GetTid(); + ThreadLister thread_lister(getpid()); + std::vector listed_tids = ReadTidsToVector(&thread_lister); + ASSERT_TRUE(HasElement(listed_tids, self_tid)); + ASSERT_TRUE(Includes(listed_tids, tids_)); +} + +// Calling Reset() should not cause ThreadLister to forget any threads it's +// supposed to know about. +TEST_F(ThreadListerTest, ResetDoesNotForgetThreads) { + ThreadLister thread_lister(getpid()); + + // Run the loop body twice, because Reset() might behave differently if called + // on a freshly created object. + for (uptr i = 0; i < 2; i++) { + thread_lister.Reset(); + std::vector listed_tids = ReadTidsToVector(&thread_lister); + ASSERT_TRUE(Includes(listed_tids, tids_)); + } +} + +// If new threads have spawned during ThreadLister object's lifetime, calling +// Reset() should cause ThreadLister to recognize their existence. +TEST_F(ThreadListerTest, ResetMakesNewThreadsKnown) { + ThreadLister thread_lister(getpid()); + std::vector threads_before_extra = ReadTidsToVector(&thread_lister); + + pthread_t extra_pthread_id; + pid_t extra_tid; + SpawnTidReporter(&extra_pthread_id, &extra_tid); + // Register the new thread so it gets terminated in TearDown(). + pthread_ids_.push_back(extra_pthread_id); + + // It would be very bizarre if the new TID had been listed before we even + // spawned that thread, but it would also cause a false success in this test, + // so better check for that. + ASSERT_FALSE(HasElement(threads_before_extra, extra_tid)); + + thread_lister.Reset(); + + std::vector threads_after_extra = ReadTidsToVector(&thread_lister); + ASSERT_TRUE(HasElement(threads_after_extra, extra_tid)); +} + +TEST(SanitizerCommon, SetEnvTest) { + const char kEnvName[] = "ENV_FOO"; + SetEnv(kEnvName, "value"); + EXPECT_STREQ("value", getenv(kEnvName)); + unsetenv(kEnvName); + EXPECT_EQ(0, getenv(kEnvName)); +} + +#if defined(__x86_64__) || defined(__i386__) +void *thread_self_offset_test_func(void *arg) { + bool result = + *(uptr *)((char *)ThreadSelf() + ThreadSelfOffset()) == ThreadSelf(); + return (void *)result; +} + +TEST(SanitizerLinux, ThreadSelfOffset) { + EXPECT_TRUE((bool)thread_self_offset_test_func(0)); + pthread_t tid; + void *result; + ASSERT_EQ(0, pthread_create(&tid, 0, thread_self_offset_test_func, 0)); + ASSERT_EQ(0, pthread_join(tid, &result)); + EXPECT_TRUE((bool)result); +} + +// libpthread puts the thread descriptor at the end of stack space. +void *thread_descriptor_size_test_func(void *arg) { + uptr descr_addr = ThreadSelf(); + pthread_attr_t attr; + pthread_getattr_np(pthread_self(), &attr); + void *stackaddr; + size_t stacksize; + pthread_attr_getstack(&attr, &stackaddr, &stacksize); + return (void *)((uptr)stackaddr + stacksize - descr_addr); +} + +TEST(SanitizerLinux, ThreadDescriptorSize) { + pthread_t tid; + void *result; + ASSERT_EQ(0, pthread_create(&tid, 0, thread_descriptor_size_test_func, 0)); + ASSERT_EQ(0, pthread_join(tid, &result)); + EXPECT_EQ((uptr)result, ThreadDescriptorSize()); +} +#endif + +TEST(SanitizerCommon, LibraryNameIs) { + EXPECT_FALSE(LibraryNameIs("", "")); + + char full_name[256]; + const char *paths[] = { "", "/", "/path/to/" }; + const char *suffixes[] = { "", "-linux", ".1.2", "-linux.1.2" }; + const char *base_names[] = { "lib", "lib.0", "lib-i386" }; + const char *wrong_names[] = { "", "lib.9", "lib-x86_64" }; + for (uptr i = 0; i < ARRAY_SIZE(paths); i++) + for (uptr j = 0; j < ARRAY_SIZE(suffixes); j++) { + for (uptr k = 0; k < ARRAY_SIZE(base_names); k++) { + internal_snprintf(full_name, ARRAY_SIZE(full_name), "%s%s%s.so", + paths[i], base_names[k], suffixes[j]); + EXPECT_TRUE(LibraryNameIs(full_name, base_names[k])) + << "Full name " << full_name + << " doesn't match base name " << base_names[k]; + for (uptr m = 0; m < ARRAY_SIZE(wrong_names); m++) + EXPECT_FALSE(LibraryNameIs(full_name, wrong_names[m])) + << "Full name " << full_name + << " matches base name " << wrong_names[m]; + } + } +} + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_list_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_list_test.cc new file mode 100644 index 00000000..fbe53c03 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_list_test.cc @@ -0,0 +1,173 @@ +//===-- sanitizer_list_test.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_list.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +struct ListItem { + ListItem *next; +}; + +typedef IntrusiveList List; + +static List static_list; + +static void SetList(List *l, ListItem *x = 0, + ListItem *y = 0, ListItem *z = 0) { + l->clear(); + if (x) l->push_back(x); + if (y) l->push_back(y); + if (z) l->push_back(z); +} + +static void CheckList(List *l, ListItem *i1, ListItem *i2 = 0, ListItem *i3 = 0, + ListItem *i4 = 0, ListItem *i5 = 0, ListItem *i6 = 0) { + if (i1) { + CHECK_EQ(l->front(), i1); + l->pop_front(); + } + if (i2) { + CHECK_EQ(l->front(), i2); + l->pop_front(); + } + if (i3) { + CHECK_EQ(l->front(), i3); + l->pop_front(); + } + if (i4) { + CHECK_EQ(l->front(), i4); + l->pop_front(); + } + if (i5) { + CHECK_EQ(l->front(), i5); + l->pop_front(); + } + if (i6) { + CHECK_EQ(l->front(), i6); + l->pop_front(); + } + CHECK(l->empty()); +} + +TEST(SanitizerCommon, IntrusiveList) { + ListItem items[6]; + CHECK_EQ(static_list.size(), 0); + + List l; + l.clear(); + + ListItem *x = &items[0]; + ListItem *y = &items[1]; + ListItem *z = &items[2]; + ListItem *a = &items[3]; + ListItem *b = &items[4]; + ListItem *c = &items[5]; + + CHECK_EQ(l.size(), 0); + l.push_back(x); + CHECK_EQ(l.size(), 1); + CHECK_EQ(l.back(), x); + CHECK_EQ(l.front(), x); + l.pop_front(); + CHECK(l.empty()); + l.CheckConsistency(); + + l.push_front(x); + CHECK_EQ(l.size(), 1); + CHECK_EQ(l.back(), x); + CHECK_EQ(l.front(), x); + l.pop_front(); + CHECK(l.empty()); + l.CheckConsistency(); + + l.push_front(x); + l.push_front(y); + l.push_front(z); + CHECK_EQ(l.size(), 3); + CHECK_EQ(l.front(), z); + CHECK_EQ(l.back(), x); + l.CheckConsistency(); + + l.pop_front(); + CHECK_EQ(l.size(), 2); + CHECK_EQ(l.front(), y); + CHECK_EQ(l.back(), x); + l.pop_front(); + l.pop_front(); + CHECK(l.empty()); + l.CheckConsistency(); + + l.push_back(x); + l.push_back(y); + l.push_back(z); + CHECK_EQ(l.size(), 3); + CHECK_EQ(l.front(), x); + CHECK_EQ(l.back(), z); + l.CheckConsistency(); + + l.pop_front(); + CHECK_EQ(l.size(), 2); + CHECK_EQ(l.front(), y); + CHECK_EQ(l.back(), z); + l.pop_front(); + l.pop_front(); + CHECK(l.empty()); + l.CheckConsistency(); + + List l1, l2; + l1.clear(); + l2.clear(); + + l1.append_front(&l2); + CHECK(l1.empty()); + CHECK(l2.empty()); + + l1.append_back(&l2); + CHECK(l1.empty()); + CHECK(l2.empty()); + + SetList(&l1, x); + CheckList(&l1, x); + + SetList(&l1, x, y, z); + SetList(&l2, a, b, c); + l1.append_back(&l2); + CheckList(&l1, x, y, z, a, b, c); + CHECK(l2.empty()); + + SetList(&l1, x, y); + SetList(&l2); + l1.append_front(&l2); + CheckList(&l1, x, y); + CHECK(l2.empty()); +} + +TEST(SanitizerCommon, IntrusiveListAppendEmpty) { + ListItem i; + List l; + l.clear(); + l.push_back(&i); + List l2; + l2.clear(); + l.append_back(&l2); + CHECK_EQ(l.back(), &i); + CHECK_EQ(l.front(), &i); + CHECK_EQ(l.size(), 1); + l.append_front(&l2); + CHECK_EQ(l.back(), &i); + CHECK_EQ(l.front(), &i); + CHECK_EQ(l.size(), 1); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_mutex_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_mutex_test.cc new file mode 100644 index 00000000..06f742b4 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_mutex_test.cc @@ -0,0 +1,134 @@ +//===-- sanitizer_mutex_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_common.h" +#include "gtest/gtest.h" + +#include + +namespace __sanitizer { + +template +class TestData { + public: + explicit TestData(MutexType *mtx) + : mtx_(mtx) { + for (int i = 0; i < kSize; i++) + data_[i] = 0; + } + + void Write() { + Lock l(mtx_); + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + data_[i]++; + } + } + + void TryWrite() { + if (!mtx_->TryLock()) + return; + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + data_[i]++; + } + mtx_->Unlock(); + } + + void Backoff() { + volatile T data[kSize] = {}; + for (int i = 0; i < kSize; i++) { + data[i]++; + CHECK_EQ(data[i], 1); + } + } + + private: + typedef GenericScopedLock Lock; + static const int kSize = 64; + typedef u64 T; + MutexType *mtx_; + char pad_[kCacheLineSize]; + T data_[kSize]; +}; + +const int kThreads = 8; +#if SANITIZER_DEBUG +const int kIters = 16*1024; +#else +const int kIters = 64*1024; +#endif + +template +static void *lock_thread(void *param) { + TestData *data = (TestData*)param; + for (int i = 0; i < kIters; i++) { + data->Write(); + data->Backoff(); + } + return 0; +} + +template +static void *try_thread(void *param) { + TestData *data = (TestData*)param; + for (int i = 0; i < kIters; i++) { + data->TryWrite(); + data->Backoff(); + } + return 0; +} + +template +static void check_locked(MutexType *mtx) { + GenericScopedLock l(mtx); + mtx->CheckLocked(); +} + +TEST(SanitizerCommon, SpinMutex) { + SpinMutex mtx; + mtx.Init(); + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, lock_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +TEST(SanitizerCommon, SpinMutexTry) { + SpinMutex mtx; + mtx.Init(); + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, try_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +TEST(SanitizerCommon, BlockingMutex) { + u64 mtxmem[1024] = {}; + BlockingMutex *mtx = new(mtxmem) BlockingMutex(LINKER_INITIALIZED); + TestData data(mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, lock_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); + check_locked(mtx); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc new file mode 100644 index 00000000..d0d5a5e1 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test.cc @@ -0,0 +1,31 @@ +//===-- sanitizer_nolibc_test.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Tests for libc independence of sanitizer_common. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +#include "gtest/gtest.h" + +#include + +extern const char *argv0; + +#if SANITIZER_LINUX && defined(__x86_64__) +TEST(SanitizerCommon, NolibcMain) { + std::string NolibcTestPath = argv0; + NolibcTestPath += "-Nolibc"; + int status = system(NolibcTestPath.c_str()); + EXPECT_EQ(true, WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); +} +#endif diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc new file mode 100644 index 00000000..72df621d --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_nolibc_test_main.cc @@ -0,0 +1,19 @@ +//===-- sanitizer_nolibc_test_main.cc -------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// Tests for libc independence of sanitizer_common. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_libc.h" + +extern "C" void _start() { + internal__exit(0); +} diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc new file mode 100644 index 00000000..035899c8 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_posix_test.cc @@ -0,0 +1,62 @@ +//===-- sanitizer_posix_test.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for POSIX-specific code. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_POSIX + +#include "sanitizer_common/sanitizer_common.h" +#include "gtest/gtest.h" + +#include + +namespace __sanitizer { + +static pthread_key_t key; +static bool destructor_executed; + +extern "C" +void destructor(void *arg) { + uptr iter = reinterpret_cast(arg); + if (iter > 1) { + ASSERT_EQ(0, pthread_setspecific(key, reinterpret_cast(iter - 1))); + return; + } + destructor_executed = true; +} + +extern "C" +void *thread_func(void *arg) { + return reinterpret_cast(pthread_setspecific(key, arg)); +} + +static void SpawnThread(uptr iteration) { + destructor_executed = false; + pthread_t tid; + ASSERT_EQ(0, pthread_create(&tid, 0, &thread_func, + reinterpret_cast(iteration))); + void *retval; + ASSERT_EQ(0, pthread_join(tid, &retval)); + ASSERT_EQ(0, retval); +} + +TEST(SanitizerCommon, PthreadDestructorIterations) { + ASSERT_EQ(0, pthread_key_create(&key, &destructor)); + SpawnThread(kPthreadDestructorIterations); + EXPECT_TRUE(destructor_executed); + SpawnThread(kPthreadDestructorIterations + 1); + EXPECT_FALSE(destructor_executed); +} + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc new file mode 100644 index 00000000..2c478cc7 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc @@ -0,0 +1,139 @@ +//===-- sanitizer_printf_test.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for sanitizer_printf.cc +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "gtest/gtest.h" + +#include +#include + +namespace __sanitizer { + +TEST(Printf, Basic) { + char buf[1024]; + uptr len = internal_snprintf(buf, sizeof(buf), + "a%db%zdc%ue%zuf%xh%zxq%pe%sr", + (int)-1, (long)-2, // NOLINT + (unsigned)-4, (unsigned long)5, // NOLINT + (unsigned)10, (unsigned long)11, // NOLINT + (void*)0x123, "_string_"); + EXPECT_EQ(len, strlen(buf)); + void *ptr; + if (sizeof(ptr) == 4) { + EXPECT_STREQ("a-1b-2c4294967292e5fahbq" + "0x00000123e_string_r", buf); + } else { + EXPECT_STREQ("a-1b-2c4294967292e5fahbq" + "0x000000000123e_string_r", buf); + } +} + +TEST(Printf, OverflowStr) { + char buf[] = "123456789"; + uptr len = internal_snprintf(buf, 4, "%s", "abcdef"); // NOLINT + EXPECT_EQ(len, (uptr)6); + EXPECT_STREQ("abc", buf); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +TEST(Printf, OverflowInt) { + char buf[] = "123456789"; + internal_snprintf(buf, 4, "%d", -123456789); // NOLINT + EXPECT_STREQ("-12", buf); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +TEST(Printf, OverflowUint) { + char buf[] = "123456789"; + uptr val; + if (sizeof(val) == 4) { + val = (uptr)0x12345678; + } else { + val = (uptr)0x123456789ULL; + } + internal_snprintf(buf, 4, "a%zx", val); // NOLINT + EXPECT_STREQ("a12", buf); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +TEST(Printf, OverflowPtr) { + char buf[] = "123456789"; + void *p; + if (sizeof(p) == 4) { + p = (void*)0x1234567; + } else { + p = (void*)0x123456789ULL; + } + internal_snprintf(buf, 4, "%p", p); // NOLINT + EXPECT_STREQ("0x0", buf); + EXPECT_EQ(buf[3], 0); + EXPECT_EQ(buf[4], '5'); + EXPECT_EQ(buf[5], '6'); + EXPECT_EQ(buf[6], '7'); + EXPECT_EQ(buf[7], '8'); + EXPECT_EQ(buf[8], '9'); + EXPECT_EQ(buf[9], 0); +} + +template +static void TestAgainstLibc(const char *fmt, T arg1, T arg2) { + char buf[1024]; + uptr len = internal_snprintf(buf, sizeof(buf), fmt, arg1, arg2); + char buf2[1024]; + snprintf(buf2, sizeof(buf2), fmt, arg1, arg2); + EXPECT_EQ(len, strlen(buf)); + EXPECT_STREQ(buf2, buf); +} + +TEST(Printf, MinMax) { + TestAgainstLibc("%d-%d", INT_MIN, INT_MAX); // NOLINT + TestAgainstLibc("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT + TestAgainstLibc("%u-%u", 0, UINT_MAX); // NOLINT + TestAgainstLibc("%zu-%zu", 0, ULONG_MAX); // NOLINT + TestAgainstLibc("%x-%x", 0, UINT_MAX); // NOLINT + TestAgainstLibc("%zx-%zx", 0, ULONG_MAX); // NOLINT + Report("%zd\n", LONG_MIN); +} + +TEST(Printf, Padding) { + TestAgainstLibc("%3d - %3d", 1, 0); + TestAgainstLibc("%3d - %3d", -1, 123); + TestAgainstLibc("%3d - %3d", -1, -123); + TestAgainstLibc("%3d - %3d", 12, 1234); + TestAgainstLibc("%3d - %3d", -12, -1234); + TestAgainstLibc("%03d - %03d", 1, 0); + TestAgainstLibc("%03d - %03d", -1, 123); + TestAgainstLibc("%03d - %03d", -1, -123); + TestAgainstLibc("%03d - %03d", 12, 1234); + TestAgainstLibc("%03d - %03d", -12, -1234); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc new file mode 100644 index 00000000..d16e2eeb --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc @@ -0,0 +1,30 @@ +//===-- sanitizer_procmaps_test.cc ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_procmaps.h" +//#include "sanitizer_common/sanitizer_internal_defs.h" +//#include "sanitizer_common/sanitizer_libc.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +#ifdef SANITIZER_LINUX +TEST(ProcMaps, CodeRange) { + uptr start, end; + bool res = GetCodeRangeForFile("[vdso]", &start, &end); + EXPECT_EQ(res, true); + EXPECT_GT(start, (uptr)0); + EXPECT_LT(start, end); +} +#endif + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc new file mode 100644 index 00000000..e0354062 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc @@ -0,0 +1,178 @@ +//===-- sanitizer_scanf_interceptor_test.cc -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for *scanf interceptors implementation in sanitizer_common. +// +//===----------------------------------------------------------------------===// +#include + +#include "interception/interception.h" +#include "sanitizer_test_utils.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "gtest/gtest.h" + +using namespace __sanitizer; + +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + ((std::vector *)ctx)->push_back(size) + +#include "sanitizer_common/sanitizer_common_interceptors_scanf.inc" + +static const char scanf_buf[] = "Test string."; +static size_t scanf_buf_size = sizeof(scanf_buf); +static const unsigned SCANF_ARGS_MAX = 16; + +static void testScanf3(void *ctx, int result, bool allowGnuMalloc, + const char *format, ...) { + va_list ap; + va_start(ap, format); + scanf_common(ctx, result, allowGnuMalloc, format, ap); + va_end(ap); +} + +static void testScanf2(const char *format, int scanf_result, + bool allowGnuMalloc, unsigned n, + va_list expected_sizes) { + std::vector scanf_sizes; + // 16 args should be enough. + testScanf3((void *)&scanf_sizes, scanf_result, allowGnuMalloc, format, + scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, + scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, scanf_buf, + scanf_buf, scanf_buf, scanf_buf, scanf_buf); + ASSERT_EQ(n, scanf_sizes.size()) << "Unexpected number of format arguments: '" + << format << "'"; + for (unsigned i = 0; i < n; ++i) + EXPECT_EQ(va_arg(expected_sizes, unsigned), scanf_sizes[i]) + << "Unexpect write size for argument " << i << ", format string '" + << format << "'"; +} + +static void testScanf(const char *format, unsigned n, ...) { + va_list ap; + va_start(ap, n); + testScanf2(format, SCANF_ARGS_MAX, /* allowGnuMalloc */ true, n, ap); + va_end(ap); +} + +static void testScanfPartial(const char *format, int scanf_result, unsigned n, + ...) { + va_list ap; + va_start(ap, n); + testScanf2(format, scanf_result, /* allowGnuMalloc */ true, n, ap); + va_end(ap); +} + +static void testScanfNoGnuMalloc(const char *format, unsigned n, ...) { + va_list ap; + va_start(ap, n); + testScanf2(format, SCANF_ARGS_MAX, /* allowGnuMalloc */ false, n, ap); + va_end(ap); +} + +TEST(SanitizerCommonInterceptors, Scanf) { + const unsigned I = sizeof(int); // NOLINT + const unsigned L = sizeof(long); // NOLINT + const unsigned LL = sizeof(long long); // NOLINT + const unsigned S = sizeof(short); // NOLINT + const unsigned C = sizeof(char); // NOLINT + const unsigned D = sizeof(double); // NOLINT + const unsigned LD = sizeof(long double); // NOLINT + const unsigned F = sizeof(float); // NOLINT + const unsigned P = sizeof(char *); // NOLINT + + testScanf("%d", 1, I); + testScanf("%d%d%d", 3, I, I, I); + testScanf("ab%u%dc", 2, I, I); + testScanf("%ld", 1, L); + testScanf("%llu", 1, LL); + testScanf("a %hd%hhx", 2, S, C); + testScanf("%c", 1, C); + + testScanf("%%", 0); + testScanf("a%%", 0); + testScanf("a%%b", 0); + testScanf("a%%%%b", 0); + testScanf("a%%b%%", 0); + testScanf("a%%%%%%b", 0); + testScanf("a%%%%%b", 0); + testScanf("a%%%%%f", 1, F); + testScanf("a%%%lxb", 1, L); + testScanf("a%lf%%%lxb", 2, D, L); + testScanf("%nf", 1, I); + + testScanf("%10s", 1, 11); + testScanf("%10c", 1, 10); + testScanf("%%10s", 0); + testScanf("%*10s", 0); + testScanf("%*d", 0); + + testScanf("%4d%8f%c", 3, I, F, C); + testScanf("%s%d", 2, scanf_buf_size, I); + testScanf("%[abc]", 1, scanf_buf_size); + testScanf("%4[bcdef]", 1, 5); + testScanf("%[]]", 1, scanf_buf_size); + testScanf("%8[^]%d0-9-]%c", 2, 9, C); + + testScanf("%*[^:]%n:%d:%1[ ]%n", 4, I, I, 2, I); + + testScanf("%*d%u", 1, I); + + testScanf("%c%d", 2, C, I); + testScanf("%A%lf", 2, F, D); + + testScanf("%ms %Lf", 2, P, LD); + testScanf("s%Las", 1, LD); + testScanf("%ar", 1, F); + + // In the cases with std::min below the format spec can be interpreted as + // either floating-something, or (GNU extension) callee-allocated string. + // Our conservative implementation reports one of the two possibilities with + // the least store range. + testScanf("%a[", 0); + testScanf("%a[]", 0); + testScanf("%a[]]", 1, std::min(F, P)); + testScanf("%a[abc]", 1, std::min(F, P)); + testScanf("%a[^abc]", 1, std::min(F, P)); + testScanf("%a[ab%c] %d", 0); + testScanf("%a[^ab%c] %d", 0); + testScanf("%as", 1, std::min(F, P)); + testScanf("%aS", 1, std::min(F, P)); + testScanf("%a13S", 1, std::min(F, P)); + testScanf("%alS", 1, std::min(F, P)); + + testScanfNoGnuMalloc("s%Las", 1, LD); + testScanfNoGnuMalloc("%ar", 1, F); + testScanfNoGnuMalloc("%a[", 1, F); + testScanfNoGnuMalloc("%a[]", 1, F); + testScanfNoGnuMalloc("%a[]]", 1, F); + testScanfNoGnuMalloc("%a[abc]", 1, F); + testScanfNoGnuMalloc("%a[^abc]", 1, F); + testScanfNoGnuMalloc("%a[ab%c] %d", 3, F, C, I); + testScanfNoGnuMalloc("%a[^ab%c] %d", 3, F, C, I); + testScanfNoGnuMalloc("%as", 1, F); + testScanfNoGnuMalloc("%aS", 1, F); + testScanfNoGnuMalloc("%a13S", 1, F); + testScanfNoGnuMalloc("%alS", 1, F); + + testScanf("%5$d", 0); + testScanf("%md", 0); + testScanf("%m10s", 0); + + testScanfPartial("%d%d%d%d //1\n", 1, 1, I); + testScanfPartial("%d%d%d%d //2\n", 2, 2, I, I); + testScanfPartial("%d%d%d%d //3\n", 3, 3, I, I, I); + testScanfPartial("%d%d%d%d //4\n", 4, 4, I, I, I, I); + + testScanfPartial("%d%n%n%d //1\n", 1, 3, I, I, I); + testScanfPartial("%d%n%n%d //2\n", 2, 4, I, I, I, I); + + testScanfPartial("%d%n%n%d %s %s", 3, 5, I, I, I, I, scanf_buf_size); + testScanfPartial("%d%n%n%d %s %s", 4, 6, I, I, I, I, scanf_buf_size, + scanf_buf_size); +} diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc new file mode 100644 index 00000000..5c075d53 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc @@ -0,0 +1,92 @@ +//===-- sanitizer_stackdepot_test.cc --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +TEST(SanitizerCommon, StackDepotBasic) { + uptr s1[] = {1, 2, 3, 4, 5}; + u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1)); + uptr sz1 = 0; + const uptr *sp1 = StackDepotGet(i1, &sz1); + EXPECT_NE(sp1, (uptr*)0); + EXPECT_EQ(sz1, ARRAY_SIZE(s1)); + EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0); +} + +TEST(SanitizerCommon, StackDepotAbsent) { + uptr sz1 = 0; + const uptr *sp1 = StackDepotGet((1 << 30) - 1, &sz1); + EXPECT_EQ(sp1, (uptr*)0); +} + +TEST(SanitizerCommon, StackDepotEmptyStack) { + u32 i1 = StackDepotPut(0, 0); + uptr sz1 = 0; + const uptr *sp1 = StackDepotGet(i1, &sz1); + EXPECT_EQ(sp1, (uptr*)0); +} + +TEST(SanitizerCommon, StackDepotZeroId) { + uptr sz1 = 0; + const uptr *sp1 = StackDepotGet(0, &sz1); + EXPECT_EQ(sp1, (uptr*)0); +} + +TEST(SanitizerCommon, StackDepotSame) { + uptr s1[] = {1, 2, 3, 4, 6}; + u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1)); + u32 i2 = StackDepotPut(s1, ARRAY_SIZE(s1)); + EXPECT_EQ(i1, i2); + uptr sz1 = 0; + const uptr *sp1 = StackDepotGet(i1, &sz1); + EXPECT_NE(sp1, (uptr*)0); + EXPECT_EQ(sz1, ARRAY_SIZE(s1)); + EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0); +} + +TEST(SanitizerCommon, StackDepotSeveral) { + uptr s1[] = {1, 2, 3, 4, 7}; + u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1)); + uptr s2[] = {1, 2, 3, 4, 8, 9}; + u32 i2 = StackDepotPut(s2, ARRAY_SIZE(s2)); + EXPECT_NE(i1, i2); +} + +TEST(SanitizerCommon, StackDepotReverseMap) { + uptr s1[] = {1, 2, 3, 4, 5}; + uptr s2[] = {7, 1, 3, 0}; + uptr s3[] = {10, 2, 5, 3}; + uptr s4[] = {1, 3, 2, 5}; + u32 ids[4] = {0}; + ids[0] = StackDepotPut(s1, ARRAY_SIZE(s1)); + ids[1] = StackDepotPut(s2, ARRAY_SIZE(s2)); + ids[2] = StackDepotPut(s3, ARRAY_SIZE(s3)); + ids[3] = StackDepotPut(s4, ARRAY_SIZE(s4)); + + StackDepotReverseMap map; + + for (uptr i = 0; i < 4; i++) { + uptr sz_depot, sz_map; + const uptr *sp_depot, *sp_map; + sp_depot = StackDepotGet(ids[i], &sz_depot); + sp_map = map.Get(ids[i], &sz_map); + EXPECT_EQ(sz_depot, sz_map); + EXPECT_EQ(sp_depot, sp_map); + } +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc new file mode 100644 index 00000000..2b842cd8 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc @@ -0,0 +1,105 @@ +//===-- sanitizer_stacktrace_test.cc --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +class FastUnwindTest : public ::testing::Test { + protected: + virtual void SetUp(); + bool TryFastUnwind(uptr max_depth) { + if (!StackTrace::WillUseFastUnwind(true)) + return false; + trace.Unwind(max_depth, start_pc, (uptr)&fake_stack[0], fake_top, + fake_bottom, true); + return true; + } + + uptr fake_stack[10]; + uptr start_pc; + uptr fake_top; + uptr fake_bottom; + StackTrace trace; +}; + +static uptr PC(uptr idx) { + return (1<<20) + idx; +} + +void FastUnwindTest::SetUp() { + // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have + // even indices. + for (uptr i = 0; i+1 < ARRAY_SIZE(fake_stack); i += 2) { + fake_stack[i] = (uptr)&fake_stack[i+2]; // fp + fake_stack[i+1] = PC(i + 1); // retaddr + } + // Mark the last fp as zero to terminate the stack trace. + fake_stack[RoundDownTo(ARRAY_SIZE(fake_stack) - 1, 2)] = 0; + + // Top is two slots past the end because FastUnwindStack subtracts two. + fake_top = (uptr)&fake_stack[ARRAY_SIZE(fake_stack) + 2]; + // Bottom is one slot before the start because FastUnwindStack uses >. + fake_bottom = (uptr)&fake_stack[-1]; + start_pc = PC(0); +} + +TEST_F(FastUnwindTest, Basic) { + if (!TryFastUnwind(kStackTraceMax)) + return; + // Should get all on-stack retaddrs and start_pc. + EXPECT_EQ(6U, trace.size); + EXPECT_EQ(start_pc, trace.trace[0]); + for (uptr i = 1; i <= 5; i++) { + EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); + } +} + +// From: http://code.google.com/p/address-sanitizer/issues/detail?id=162 +TEST_F(FastUnwindTest, FramePointerLoop) { + // Make one fp point to itself. + fake_stack[4] = (uptr)&fake_stack[4]; + if (!TryFastUnwind(kStackTraceMax)) + return; + // Should get all on-stack retaddrs up to the 4th slot and start_pc. + EXPECT_EQ(4U, trace.size); + EXPECT_EQ(start_pc, trace.trace[0]); + for (uptr i = 1; i <= 3; i++) { + EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); + } +} + +TEST_F(FastUnwindTest, MisalignedFramePointer) { + // Make one fp misaligned. + fake_stack[4] += 3; + if (!TryFastUnwind(kStackTraceMax)) + return; + // Should get all on-stack retaddrs up to the 4th slot and start_pc. + EXPECT_EQ(4U, trace.size); + EXPECT_EQ(start_pc, trace.trace[0]); + for (uptr i = 1; i < 4U; i++) { + EXPECT_EQ(PC(i*2 - 1), trace.trace[i]); + } +} + +TEST_F(FastUnwindTest, OneFrameStackTrace) { + if (!TryFastUnwind(1)) + return; + EXPECT_EQ(1U, trace.size); + EXPECT_EQ(start_pc, trace.trace[0]); + EXPECT_EQ((uptr)&fake_stack[0], trace.top_frame_bp); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc new file mode 100644 index 00000000..b6786ba8 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc @@ -0,0 +1,194 @@ +//===-- sanitizer_stoptheworld_test.cc ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for sanitizer_stoptheworld.h +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX && defined(__x86_64__) + +#include "sanitizer_common/sanitizer_stoptheworld.h" +#include "gtest/gtest.h" + +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_common.h" + +#include +#include + +namespace __sanitizer { + +static pthread_mutex_t incrementer_thread_exit_mutex; + +struct CallbackArgument { + volatile int counter; + volatile bool threads_stopped; + volatile bool callback_executed; + CallbackArgument() + : counter(0), + threads_stopped(false), + callback_executed(false) {} +}; + +void *IncrementerThread(void *argument) { + CallbackArgument *callback_argument = (CallbackArgument *)argument; + while (true) { + __sync_fetch_and_add(&callback_argument->counter, 1); + if (pthread_mutex_trylock(&incrementer_thread_exit_mutex) == 0) { + pthread_mutex_unlock(&incrementer_thread_exit_mutex); + return NULL; + } else { + sched_yield(); + } + } +} + +// This callback checks that IncrementerThread is suspended at the time of its +// execution. +void Callback(const SuspendedThreadsList &suspended_threads_list, + void *argument) { + CallbackArgument *callback_argument = (CallbackArgument *)argument; + callback_argument->callback_executed = true; + int counter_at_init = __sync_fetch_and_add(&callback_argument->counter, 0); + for (uptr i = 0; i < 1000; i++) { + sched_yield(); + if (__sync_fetch_and_add(&callback_argument->counter, 0) != + counter_at_init) { + callback_argument->threads_stopped = false; + return; + } + } + callback_argument->threads_stopped = true; +} + +TEST(StopTheWorld, SuspendThreadsSimple) { + pthread_mutex_init(&incrementer_thread_exit_mutex, NULL); + CallbackArgument argument; + pthread_t thread_id; + int pthread_create_result; + pthread_mutex_lock(&incrementer_thread_exit_mutex); + pthread_create_result = pthread_create(&thread_id, NULL, IncrementerThread, + &argument); + ASSERT_EQ(0, pthread_create_result); + StopTheWorld(&Callback, &argument); + pthread_mutex_unlock(&incrementer_thread_exit_mutex); + EXPECT_TRUE(argument.callback_executed); + EXPECT_TRUE(argument.threads_stopped); + // argument is on stack, so we have to wait for the incrementer thread to + // terminate before we can return from this function. + ASSERT_EQ(0, pthread_join(thread_id, NULL)); + pthread_mutex_destroy(&incrementer_thread_exit_mutex); +} + +// A more comprehensive test where we spawn a bunch of threads while executing +// StopTheWorld in parallel. +static const uptr kThreadCount = 50; +static const uptr kStopWorldAfter = 10; // let this many threads spawn first + +static pthread_mutex_t advanced_incrementer_thread_exit_mutex; + +struct AdvancedCallbackArgument { + volatile uptr thread_index; + volatile int counters[kThreadCount]; + pthread_t thread_ids[kThreadCount]; + volatile bool threads_stopped; + volatile bool callback_executed; + volatile bool fatal_error; + AdvancedCallbackArgument() + : thread_index(0), + threads_stopped(false), + callback_executed(false), + fatal_error(false) {} +}; + +void *AdvancedIncrementerThread(void *argument) { + AdvancedCallbackArgument *callback_argument = + (AdvancedCallbackArgument *)argument; + uptr this_thread_index = __sync_fetch_and_add( + &callback_argument->thread_index, 1); + // Spawn the next thread. + int pthread_create_result; + if (this_thread_index + 1 < kThreadCount) { + pthread_create_result = + pthread_create(&callback_argument->thread_ids[this_thread_index + 1], + NULL, AdvancedIncrementerThread, argument); + // Cannot use ASSERT_EQ in non-void-returning functions. If there's a + // problem, defer failing to the main thread. + if (pthread_create_result != 0) { + callback_argument->fatal_error = true; + __sync_fetch_and_add(&callback_argument->thread_index, + kThreadCount - callback_argument->thread_index); + } + } + // Do the actual work. + while (true) { + __sync_fetch_and_add(&callback_argument->counters[this_thread_index], 1); + if (pthread_mutex_trylock(&advanced_incrementer_thread_exit_mutex) == 0) { + pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex); + return NULL; + } else { + sched_yield(); + } + } +} + +void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list, + void *argument) { + AdvancedCallbackArgument *callback_argument = + (AdvancedCallbackArgument *)argument; + callback_argument->callback_executed = true; + + int counters_at_init[kThreadCount]; + for (uptr j = 0; j < kThreadCount; j++) + counters_at_init[j] = __sync_fetch_and_add(&callback_argument->counters[j], + 0); + for (uptr i = 0; i < 10; i++) { + sched_yield(); + for (uptr j = 0; j < kThreadCount; j++) + if (__sync_fetch_and_add(&callback_argument->counters[j], 0) != + counters_at_init[j]) { + callback_argument->threads_stopped = false; + return; + } + } + callback_argument->threads_stopped = true; +} + +TEST(StopTheWorld, SuspendThreadsAdvanced) { + pthread_mutex_init(&advanced_incrementer_thread_exit_mutex, NULL); + AdvancedCallbackArgument argument; + + pthread_mutex_lock(&advanced_incrementer_thread_exit_mutex); + int pthread_create_result; + pthread_create_result = pthread_create(&argument.thread_ids[0], NULL, + AdvancedIncrementerThread, + &argument); + ASSERT_EQ(0, pthread_create_result); + // Wait for several threads to spawn before proceeding. + while (__sync_fetch_and_add(&argument.thread_index, 0) < kStopWorldAfter) + sched_yield(); + StopTheWorld(&AdvancedCallback, &argument); + EXPECT_TRUE(argument.callback_executed); + EXPECT_TRUE(argument.threads_stopped); + + // Wait for all threads to spawn before we start terminating them. + while (__sync_fetch_and_add(&argument.thread_index, 0) < kThreadCount) + sched_yield(); + ASSERT_FALSE(argument.fatal_error); // a pthread_create has failed + // Signal the threads to terminate. + pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex); + for (uptr i = 0; i < kThreadCount; i++) + ASSERT_EQ(0, pthread_join(argument.thread_ids[i], NULL)); + pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex); +} + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc new file mode 100644 index 00000000..d8be2afb --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc @@ -0,0 +1,53 @@ +//===-- sanitizer_stoptheworld_testlib.cc ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Dynamic library to test StopTheWorld functionality. +// When loaded with LD_PRELOAD, it will periodically suspend all threads. +//===----------------------------------------------------------------------===// +/* Usage: +clang++ -fno-exceptions -g -fPIC -I. \ + sanitizer_common/tests/sanitizer_stoptheworld_testlib.cc \ + sanitizer_common/sanitizer_*.cc -shared -lpthread -o teststoptheworld.so +LD_PRELOAD=`pwd`/teststoptheworld.so /your/app +*/ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include +#include +#include +#include +#include + +#include "sanitizer_common/sanitizer_stoptheworld.h" + +namespace { +const uptr kSuspendDuration = 3; +const uptr kRunDuration = 3; + +void Callback(const SuspendedThreadsList &suspended_threads_list, + void *argument) { + sleep(kSuspendDuration); +} + +void *SuspenderThread(void *argument) { + while (true) { + sleep(kRunDuration); + StopTheWorld(Callback, NULL); + } + return NULL; +} + +__attribute__((constructor)) void StopTheWorldTestLibConstructor(void) { + pthread_t thread_id; + pthread_create(&thread_id, NULL, SuspenderThread, NULL); +} +} // namespace + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc new file mode 100644 index 00000000..ea8741d4 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -0,0 +1,152 @@ +//===-- sanitizer_suppressions_test.cc ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_suppressions.h" +#include "gtest/gtest.h" + +#include + +namespace __sanitizer { + +static bool MyMatch(const char *templ, const char *func) { + char tmp[1024]; + strcpy(tmp, templ); // NOLINT + return TemplateMatch(tmp, func); +} + +TEST(Suppressions, Match) { + EXPECT_TRUE(MyMatch("foobar$", "foobar")); + + EXPECT_TRUE(MyMatch("foobar", "foobar")); + EXPECT_TRUE(MyMatch("*foobar*", "foobar")); + EXPECT_TRUE(MyMatch("foobar", "prefix_foobar_postfix")); + EXPECT_TRUE(MyMatch("*foobar*", "prefix_foobar_postfix")); + EXPECT_TRUE(MyMatch("foo*bar", "foo_middle_bar")); + EXPECT_TRUE(MyMatch("foo*bar", "foobar")); + EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_bar_another_baz")); + EXPECT_TRUE(MyMatch("foo*bar*baz", "foo_middle_barbaz")); + EXPECT_TRUE(MyMatch("^foobar", "foobar")); + EXPECT_TRUE(MyMatch("^foobar", "foobar_postfix")); + EXPECT_TRUE(MyMatch("^*foobar", "foobar")); + EXPECT_TRUE(MyMatch("^*foobar", "prefix_foobar")); + EXPECT_TRUE(MyMatch("foobar$", "foobar")); + EXPECT_TRUE(MyMatch("foobar$", "prefix_foobar")); + EXPECT_TRUE(MyMatch("*foobar*$", "foobar")); + EXPECT_TRUE(MyMatch("*foobar*$", "foobar_postfix")); + EXPECT_TRUE(MyMatch("^foobar$", "foobar")); + + EXPECT_FALSE(MyMatch("foo", "baz")); + EXPECT_FALSE(MyMatch("foobarbaz", "foobar")); + EXPECT_FALSE(MyMatch("foobarbaz", "barbaz")); + EXPECT_FALSE(MyMatch("foo*bar", "foobaz")); + EXPECT_FALSE(MyMatch("foo*bar", "foo_baz")); + EXPECT_FALSE(MyMatch("^foobar", "prefix_foobar")); + EXPECT_FALSE(MyMatch("foobar$", "foobar_postfix")); + EXPECT_FALSE(MyMatch("^foobar$", "prefix_foobar")); + EXPECT_FALSE(MyMatch("^foobar$", "foobar_postfix")); + EXPECT_FALSE(MyMatch("foo^bar", "foobar")); + EXPECT_FALSE(MyMatch("foo$bar", "foobar")); + EXPECT_FALSE(MyMatch("foo$^bar", "foobar")); +} + +TEST(Suppressions, TypeStrings) { + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionNone), "none")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionRace), "race")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionMutex), "mutex")); + 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, 7); +} + +class SuppressionContextTest : public ::testing::Test { + public: + virtual void SetUp() { ctx_ = new(placeholder_) SuppressionContext; } + virtual void TearDown() { ctx_->~SuppressionContext(); } + + protected: + InternalMmapVector *Suppressions() { + return &ctx_->suppressions_; + } + SuppressionContext *ctx_; + ALIGNED(64) char placeholder_[sizeof(SuppressionContext)]; +}; + +TEST_F(SuppressionContextTest, Parse) { + ctx_->Parse( + "race:foo\n" + " race:bar\n" // NOLINT + "race:baz \n" // NOLINT + "# a comment\n" + "race:quz\n" + ); // NOLINT + EXPECT_EQ((unsigned)4, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[3].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz")); + EXPECT_EQ((*Suppressions())[2].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz")); + EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); +} + +TEST_F(SuppressionContextTest, Parse2) { + ctx_->Parse( + " # first line comment\n" // NOLINT + " race:bar \n" // NOLINT + "race:baz* *baz\n" + "# a comment\n" + "# last line comment\n" + ); // NOLINT + EXPECT_EQ((unsigned)2, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "baz* *baz")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "bar")); +} + +TEST_F(SuppressionContextTest, Parse3) { + ctx_->Parse( + "# last suppression w/o line-feed\n" + "race:foo\n" + "race:bar" + ); // NOLINT + EXPECT_EQ((unsigned)2, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[1].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); +} + +TEST_F(SuppressionContextTest, ParseType) { + ctx_->Parse( + "race:foo\n" + "thread:bar\n" + "mutex:baz\n" + "signal:quz\n" + ); // NOLINT + EXPECT_EQ((unsigned)4, ctx_->SuppressionCount()); + EXPECT_EQ((*Suppressions())[3].type, SuppressionSignal); + EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz")); + EXPECT_EQ((*Suppressions())[2].type, SuppressionMutex); + EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz")); + EXPECT_EQ((*Suppressions())[1].type, SuppressionThread); + EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar")); + EXPECT_EQ((*Suppressions())[0].type, SuppressionRace); + EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo")); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_main.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_main.cc new file mode 100644 index 00000000..b7fd3daf --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_main.cc @@ -0,0 +1,22 @@ +//===-- sanitizer_test_main.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +const char *argv0; + +int main(int argc, char **argv) { + argv0 = argv[0]; + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h new file mode 100644 index 00000000..17adb264 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h @@ -0,0 +1,82 @@ +//===-- sanitizer_test_utils.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of *Sanitizer runtime. +// Common unit tests utilities. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_TEST_UTILS_H +#define SANITIZER_TEST_UTILS_H + +#if defined(_WIN32) +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +# define NOINLINE __declspec(noinline) +# define USED +#else // defined(_WIN32) +# define NOINLINE __attribute__((noinline)) +# define USED __attribute__((used)) +#include +#endif // defined(_WIN32) + +#if !defined(__has_feature) +#define __has_feature(x) 0 +#endif + +#ifndef ATTRIBUTE_NO_SANITIZE_ADDRESS +# if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +# define ATTRIBUTE_NO_SANITIZE_ADDRESS \ + __attribute__((no_sanitize_address)) +# else +# define ATTRIBUTE_NO_SANITIZE_ADDRESS +# endif +#endif // ATTRIBUTE_NO_SANITIZE_ADDRESS + +#if __LP64__ || defined(_WIN64) +# define SANITIZER_WORDSIZE 64 +#else +# define SANITIZER_WORDSIZE 32 +#endif + +// Make the compiler thinks that something is going on there. +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +// This function returns its parameter but in such a way that compiler +// can not prove it. +template +NOINLINE +static T Ident(T t) { + T ret = t; + break_optimization(&ret); + return ret; +} + +// Simple stand-alone pseudorandom number generator. +// Current algorithm is ANSI C linear congruential PRNG. +static inline uint32_t my_rand_r(uint32_t* state) { + return (*state = *state * 1103515245 + 12345) >> 16; +} + +static uint32_t global_seed = 0; + +static inline uint32_t my_rand() { + return my_rand_r(&global_seed); +} + + +#endif // SANITIZER_TEST_UTILS_H diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc new file mode 100644 index 00000000..ddc8dba5 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc @@ -0,0 +1,229 @@ +//===-- sanitizer_thread_registry_test.cc ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of shared sanitizer runtime. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_thread_registry.h" +#include "gtest/gtest.h" + +#include + +namespace __sanitizer { + +static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED); +static LowLevelAllocator tctx_allocator; + +template +static ThreadContextBase *GetThreadContext(u32 tid) { + BlockingMutexLock l(&tctx_allocator_lock); + return new(tctx_allocator) TCTX(tid); +} + +static const u32 kMaxRegistryThreads = 1000; +static const u32 kRegistryQuarantine = 2; + +static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total, + uptr exp_running, uptr exp_alive) { + uptr total, running, alive; + registry->GetNumberOfThreads(&total, &running, &alive); + EXPECT_EQ(exp_total, total); + EXPECT_EQ(exp_running, running); + EXPECT_EQ(exp_alive, alive); +} + +static bool is_detached(u32 tid) { + return (tid % 2 == 0); +} + +static uptr get_uid(u32 tid) { + return tid * 2; +} + +static bool HasName(ThreadContextBase *tctx, void *arg) { + char *name = (char*)arg; + return (tctx->name && 0 == internal_strcmp(tctx->name, name)); +} + +static bool HasUid(ThreadContextBase *tctx, void *arg) { + uptr uid = (uptr)arg; + return (tctx->user_id == uid); +} + +static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) { + bool *arr = (bool*)arg; + arr[tctx->tid] = true; +} + +static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) { + // Create and start a main thread. + EXPECT_EQ(0U, registry->CreateThread(get_uid(0), true, -1, 0)); + registry->StartThread(0, 0, 0); + // Create a bunch of threads. + for (u32 i = 1; i <= 10; i++) { + EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0)); + } + CheckThreadQuantity(registry, 11, 1, 11); + // Start some of them. + for (u32 i = 1; i <= 5; i++) { + registry->StartThread(i, 0, 0); + } + CheckThreadQuantity(registry, 11, 6, 11); + // Finish, create and start more threads. + for (u32 i = 1; i <= 5; i++) { + registry->FinishThread(i); + if (!is_detached(i)) + registry->JoinThread(i, 0); + } + for (u32 i = 6; i <= 10; i++) { + registry->StartThread(i, 0, 0); + } + std::vector new_tids; + for (u32 i = 11; i <= 15; i++) { + new_tids.push_back( + registry->CreateThread(get_uid(i), is_detached(i), 0, 0)); + } + ASSERT_LE(kRegistryQuarantine, 5U); + u32 exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine : 0); + CheckThreadQuantity(registry, exp_total, 6, 11); + // Test SetThreadName and FindThread. + registry->SetThreadName(6, "six"); + registry->SetThreadName(7, "seven"); + EXPECT_EQ(7U, registry->FindThread(HasName, (void*)"seven")); + EXPECT_EQ(ThreadRegistry::kUnknownTid, + registry->FindThread(HasName, (void*)"none")); + EXPECT_EQ(0U, registry->FindThread(HasUid, (void*)get_uid(0))); + EXPECT_EQ(10U, registry->FindThread(HasUid, (void*)get_uid(10))); + EXPECT_EQ(ThreadRegistry::kUnknownTid, + registry->FindThread(HasUid, (void*)0x1234)); + // Detach and finish and join remaining threads. + for (u32 i = 6; i <= 10; i++) { + registry->DetachThread(i); + registry->FinishThread(i); + } + for (u32 i = 0; i < new_tids.size(); i++) { + u32 tid = new_tids[i]; + registry->StartThread(tid, 0, 0); + registry->DetachThread(tid); + registry->FinishThread(tid); + } + CheckThreadQuantity(registry, exp_total, 1, 1); + // Test methods that require the caller to hold a ThreadRegistryLock. + bool has_tid[16]; + internal_memset(&has_tid[0], 0, sizeof(has_tid)); + { + ThreadRegistryLock l(registry); + registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]); + } + for (u32 i = 0; i < exp_total; i++) { + EXPECT_TRUE(has_tid[i]); + } + { + ThreadRegistryLock l(registry); + registry->CheckLocked(); + ThreadContextBase *main_thread = registry->GetThreadLocked(0); + EXPECT_EQ(main_thread, registry->FindThreadContextLocked( + HasUid, (void*)get_uid(0))); + } + EXPECT_EQ(11U, registry->GetMaxAliveThreads()); +} + +TEST(SanitizerCommon, ThreadRegistryTest) { + ThreadRegistry quarantine_registry(GetThreadContext, + kMaxRegistryThreads, + kRegistryQuarantine); + TestRegistry(&quarantine_registry, true); + + ThreadRegistry no_quarantine_registry(GetThreadContext, + kMaxRegistryThreads, + kMaxRegistryThreads); + TestRegistry(&no_quarantine_registry, false); +} + +static const int kThreadsPerShard = 20; +static const int kNumShards = 25; + +static int num_created[kNumShards + 1]; +static int num_started[kNumShards + 1]; +static int num_joined[kNumShards + 1]; + +namespace { + +struct RunThreadArgs { + ThreadRegistry *registry; + uptr shard; // started from 1. +}; + +class TestThreadContext : public ThreadContextBase { + public: + explicit TestThreadContext(int tid) : ThreadContextBase(tid) {} + void OnJoined(void *arg) { + uptr shard = (uptr)arg; + num_joined[shard]++; + } + void OnStarted(void *arg) { + uptr shard = (uptr)arg; + num_started[shard]++; + } + void OnCreated(void *arg) { + uptr shard = (uptr)arg; + num_created[shard]++; + } +}; + +} // namespace + +void *RunThread(void *arg) { + RunThreadArgs *args = static_cast(arg); + std::vector tids; + for (int i = 0; i < kThreadsPerShard; i++) + tids.push_back( + args->registry->CreateThread(0, false, 0, (void*)args->shard)); + for (int i = 0; i < kThreadsPerShard; i++) + args->registry->StartThread(tids[i], 0, (void*)args->shard); + for (int i = 0; i < kThreadsPerShard; i++) + args->registry->FinishThread(tids[i]); + for (int i = 0; i < kThreadsPerShard; i++) + args->registry->JoinThread(tids[i], (void*)args->shard); + return 0; +} + +static void ThreadedTestRegistry(ThreadRegistry *registry) { + // Create and start a main thread. + EXPECT_EQ(0U, registry->CreateThread(0, true, -1, 0)); + registry->StartThread(0, 0, 0); + pthread_t threads[kNumShards]; + RunThreadArgs args[kNumShards]; + for (int i = 0; i < kNumShards; i++) { + args[i].registry = registry; + args[i].shard = i + 1; + pthread_create(&threads[i], 0, RunThread, &args[i]); + } + for (int i = 0; i < kNumShards; i++) { + pthread_join(threads[i], 0); + } + // Check that each thread created/started/joined correct amount + // of "threads" in thread_registry. + EXPECT_EQ(1, num_created[0]); + EXPECT_EQ(1, num_started[0]); + EXPECT_EQ(0, num_joined[0]); + for (int i = 1; i <= kNumShards; i++) { + EXPECT_EQ(kThreadsPerShard, num_created[i]); + EXPECT_EQ(kThreadsPerShard, num_started[i]); + EXPECT_EQ(kThreadsPerShard, num_joined[i]); + } +} + +TEST(SanitizerCommon, ThreadRegistryThreadedTest) { + ThreadRegistry registry(GetThreadContext, + kThreadsPerShard * kNumShards + 1, 10); + ThreadedTestRegistry(®istry); +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/sanitizer_common/tests/standalone_malloc_test.cc b/projects/compiler-rt/lib/sanitizer_common/tests/standalone_malloc_test.cc new file mode 100644 index 00000000..9e6f7c93 --- /dev/null +++ b/projects/compiler-rt/lib/sanitizer_common/tests/standalone_malloc_test.cc @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +using namespace std; + +const size_t kNumThreds = 16; +const size_t kNumIters = 1 << 23; + +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +__attribute__((noinline)) +static void *MallocThread(void *t) { + size_t total_malloced = 0, total_freed = 0; + size_t max_in_use = 0; + size_t tid = reinterpret_cast(t); + vector > allocated; + allocated.reserve(kNumIters); + for (size_t i = 1; i < kNumIters; i++) { + if ((i % (kNumIters / 4)) == 0 && tid == 0) + fprintf(stderr, " T[%ld] iter %ld\n", tid, i); + bool allocate = (i % 5) <= 2; // 60% malloc, 40% free + if (i > kNumIters / 4) + allocate = i % 2; // then switch to 50% malloc, 50% free + if (allocate) { + size_t size = 1 + (i % 200); + if ((i % 10001) == 0) + size *= 4096; + total_malloced += size; + char *x = new char[size]; + x[0] = x[size - 1] = x[size / 2] = 0; + allocated.push_back(make_pair(x, size)); + max_in_use = max(max_in_use, total_malloced - total_freed); + } else { + if (allocated.empty()) continue; + size_t slot = i % allocated.size(); + char *p = allocated[slot].first; + p[0] = 0; // emulate last user touch of the block + size_t size = allocated[slot].second; + total_freed += size; + swap(allocated[slot], allocated.back()); + allocated.pop_back(); + delete [] p; + } + } + if (tid == 0) + fprintf(stderr, " T[%ld] total_malloced: %ldM in use %ldM max %ldM\n", + tid, total_malloced >> 20, (total_malloced - total_freed) >> 20, + max_in_use >> 20); + for (size_t i = 0; i < allocated.size(); i++) + delete [] allocated[i].first; + return 0; +} + +template +struct DeepStack { + __attribute__((noinline)) + static void *run(void *t) { + break_optimization(0); + DeepStack::run(t); + break_optimization(0); + return 0; + } +}; + +template<> +struct DeepStack<0> { + static void *run(void *t) { + MallocThread(t); + return 0; + } +}; + +// Build with -Dstandalone_malloc_test=main to make it a separate program. +int standalone_malloc_test() { + pthread_t t[kNumThreds]; + for (size_t i = 0; i < kNumThreds; i++) + pthread_create(&t[i], 0, DeepStack<200>::run, reinterpret_cast(i)); + for (size_t i = 0; i < kNumThreds; i++) + pthread_join(t[i], 0); + malloc_stats(); + return 0; +} diff --git a/projects/compiler-rt/lib/subdf3.c b/projects/compiler-rt/lib/subdf3.c new file mode 100644 index 00000000..66fb1a54 --- /dev/null +++ b/projects/compiler-rt/lib/subdf3.c @@ -0,0 +1,29 @@ +//===-- lib/adddf3.c - Double-precision subtraction ---------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements double-precision soft-float subtraction with the +// IEEE-754 default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define DOUBLE_PRECISION +#include "fp_lib.h" + +fp_t COMPILER_RT_ABI __adddf3(fp_t a, fp_t b); + + +ARM_EABI_FNALIAS(dsub, subdf3) + +// Subtraction; flip the sign bit of b and add. +COMPILER_RT_ABI fp_t +__subdf3(fp_t a, fp_t b) { + return __adddf3(a, fromRep(toRep(b) ^ signBit)); +} + +/* FIXME: rsub for ARM EABI */ diff --git a/projects/compiler-rt/lib/subsf3.c b/projects/compiler-rt/lib/subsf3.c new file mode 100644 index 00000000..3659cd8b --- /dev/null +++ b/projects/compiler-rt/lib/subsf3.c @@ -0,0 +1,28 @@ +//===-- lib/subsf3.c - Single-precision subtraction ---------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements single-precision soft-float subtraction with the +// IEEE-754 default rounding (to nearest, ties to even). +// +//===----------------------------------------------------------------------===// + +#define SINGLE_PRECISION +#include "fp_lib.h" + +fp_t COMPILER_RT_ABI __addsf3(fp_t a, fp_t b); + +ARM_EABI_FNALIAS(fsub, subsf3) + +// Subtraction; flip the sign bit of b and add. +COMPILER_RT_ABI fp_t +__subsf3(fp_t a, fp_t b) { + return __addsf3(a, fromRep(toRep(b) ^ signBit)); +} + +/* FIXME: rsub for ARM EABI */ diff --git a/projects/compiler-rt/lib/subvdi3.c b/projects/compiler-rt/lib/subvdi3.c new file mode 100644 index 00000000..0f1f924e --- /dev/null +++ b/projects/compiler-rt/lib/subvdi3.c @@ -0,0 +1,36 @@ +/* ===-- subvdi3.c - Implement __subvdi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __subvdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a - b */ + +/* Effects: aborts if a - b overflows */ + +COMPILER_RT_ABI di_int +__subvdi3(di_int a, di_int b) +{ + di_int s = a - b; + if (b >= 0) + { + if (s > a) + compilerrt_abort(); + } + else + { + if (s <= a) + compilerrt_abort(); + } + return s; +} diff --git a/projects/compiler-rt/lib/subvsi3.c b/projects/compiler-rt/lib/subvsi3.c new file mode 100644 index 00000000..ec4594c9 --- /dev/null +++ b/projects/compiler-rt/lib/subvsi3.c @@ -0,0 +1,36 @@ +/* ===-- subvsi3.c - Implement __subvsi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __subvsi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a - b */ + +/* Effects: aborts if a - b overflows */ + +COMPILER_RT_ABI si_int +__subvsi3(si_int a, si_int b) +{ + si_int s = a - b; + if (b >= 0) + { + if (s > a) + compilerrt_abort(); + } + else + { + if (s <= a) + compilerrt_abort(); + } + return s; +} diff --git a/projects/compiler-rt/lib/subvti3.c b/projects/compiler-rt/lib/subvti3.c new file mode 100644 index 00000000..b32df5e6 --- /dev/null +++ b/projects/compiler-rt/lib/subvti3.c @@ -0,0 +1,40 @@ +/* ===-- subvti3.c - Implement __subvti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __subvti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: a - b */ + +/* Effects: aborts if a - b overflows */ + +ti_int +__subvti3(ti_int a, ti_int b) +{ + ti_int s = a - b; + if (b >= 0) + { + if (s > a) + compilerrt_abort(); + } + else + { + if (s <= a) + compilerrt_abort(); + } + return s; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/trampoline_setup.c b/projects/compiler-rt/lib/trampoline_setup.c new file mode 100644 index 00000000..e0765b16 --- /dev/null +++ b/projects/compiler-rt/lib/trampoline_setup.c @@ -0,0 +1,47 @@ +/* ===----- trampoline_setup.c - Implement __trampoline_setup -------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +extern void __clear_cache(void* start, void* end); + +/* + * The ppc compiler generates calls to __trampoline_setup() when creating + * trampoline functions on the stack for use with nested functions. + * This function creates a custom 40-byte trampoline function on the stack + * which loads r11 with a pointer to the outer function's locals + * and then jumps to the target nested function. + */ + +#if __ppc__ && !defined(__powerpc64__) +void __trampoline_setup(uint32_t* trampOnStack, int trampSizeAllocated, + const void* realFunc, void* localsPtr) +{ + /* should never happen, but if compiler did not allocate */ + /* enough space on stack for the trampoline, abort */ + if ( trampSizeAllocated < 40 ) + compilerrt_abort(); + + /* create trampoline */ + trampOnStack[0] = 0x7c0802a6; /* mflr r0 */ + trampOnStack[1] = 0x4800000d; /* bl Lbase */ + trampOnStack[2] = (uint32_t)realFunc; + trampOnStack[3] = (uint32_t)localsPtr; + trampOnStack[4] = 0x7d6802a6; /* Lbase: mflr r11 */ + trampOnStack[5] = 0x818b0000; /* lwz r12,0(r11) */ + trampOnStack[6] = 0x7c0803a6; /* mtlr r0 */ + trampOnStack[7] = 0x7d8903a6; /* mtctr r12 */ + trampOnStack[8] = 0x816b0004; /* lwz r11,4(r11) */ + trampOnStack[9] = 0x4e800420; /* bctr */ + + /* clear instruction cache */ + __clear_cache(trampOnStack, &trampOnStack[10]); +} +#endif /* __ppc__ && !defined(__powerpc64__) */ diff --git a/projects/compiler-rt/lib/truncdfsf2.c b/projects/compiler-rt/lib/truncdfsf2.c new file mode 100644 index 00000000..61c909a2 --- /dev/null +++ b/projects/compiler-rt/lib/truncdfsf2.c @@ -0,0 +1,168 @@ +//===-- lib/truncdfsf2.c - double -> single conversion ------------*- C -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a fairly generic conversion from a wider to a narrower +// IEEE-754 floating-point type in the default (round to nearest, ties to even) +// rounding mode. The constants and types defined following the includes below +// parameterize the conversion. +// +// This routine can be trivially adapted to support conversions to +// half-precision or from quad-precision. It does not support types that don't +// use the usual IEEE-754 interchange formats; specifically, some work would be +// needed to adapt it to (for example) the Intel 80-bit format or PowerPC +// double-double format. +// +// Note please, however, that this implementation is only intended to support +// *narrowing* operations; if you need to convert to a *wider* floating-point +// type (e.g. float -> double), then this routine will not do what you want it +// to. +// +// It also requires that integer types at least as large as both formats +// are available on the target platform; this may pose a problem when trying +// to add support for quad on some 32-bit systems, for example. +// +// Finally, the following assumptions are made: +// +// 1. floating-point types and integer types have the same endianness on the +// target platform +// +// 2. quiet NaNs, if supported, are indicated by the leading bit of the +// significand field being set +// +//===----------------------------------------------------------------------===// + +#include "int_lib.h" + +typedef double src_t; +typedef uint64_t src_rep_t; +#define SRC_REP_C UINT64_C +static const int srcSigBits = 52; + +typedef float dst_t; +typedef uint32_t dst_rep_t; +#define DST_REP_C UINT32_C +static const int dstSigBits = 23; + +// End of specialization parameters. Two helper routines for conversion to and +// from the representation of floating-point data as integer values follow. + +static inline src_rep_t srcToRep(src_t x) { + const union { src_t f; src_rep_t i; } rep = {.f = x}; + return rep.i; +} + +static inline dst_t dstFromRep(dst_rep_t x) { + const union { dst_t f; dst_rep_t i; } rep = {.i = x}; + return rep.f; +} + +// End helper routines. Conversion implementation follows. + +ARM_EABI_FNALIAS(d2f, truncdfsf2) + +COMPILER_RT_ABI dst_t +__truncdfsf2(src_t a) { + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const int srcBits = sizeof(src_t)*CHAR_BIT; + const int srcExpBits = srcBits - srcSigBits - 1; + const int srcInfExp = (1 << srcExpBits) - 1; + const int srcExpBias = srcInfExp >> 1; + + const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigBits; + const src_rep_t significandMask = srcMinNormal - 1; + const src_rep_t srcInfinity = (src_rep_t)srcInfExp << srcSigBits; + const src_rep_t srcSignMask = SRC_REP_C(1) << (srcSigBits + srcExpBits); + const src_rep_t srcAbsMask = srcSignMask - 1; + const src_rep_t roundMask = (SRC_REP_C(1) << (srcSigBits - dstSigBits)) - 1; + const src_rep_t halfway = SRC_REP_C(1) << (srcSigBits - dstSigBits - 1); + + const int dstBits = sizeof(dst_t)*CHAR_BIT; + const int dstExpBits = dstBits - dstSigBits - 1; + const int dstInfExp = (1 << dstExpBits) - 1; + const int dstExpBias = dstInfExp >> 1; + + const int underflowExponent = srcExpBias + 1 - dstExpBias; + const int overflowExponent = srcExpBias + dstInfExp - dstExpBias; + const src_rep_t underflow = (src_rep_t)underflowExponent << srcSigBits; + const src_rep_t overflow = (src_rep_t)overflowExponent << srcSigBits; + + const dst_rep_t dstQNaN = DST_REP_C(1) << (dstSigBits - 1); + const dst_rep_t dstNaNCode = dstQNaN - 1; + + // Break a into a sign and representation of the absolute value + const src_rep_t aRep = srcToRep(a); + const src_rep_t aAbs = aRep & srcAbsMask; + const src_rep_t sign = aRep & srcSignMask; + dst_rep_t absResult; + + if (aAbs - underflow < aAbs - overflow) { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding and adjusting the exponent. + absResult = aAbs >> (srcSigBits - dstSigBits); + absResult -= (dst_rep_t)(srcExpBias - dstExpBias) << dstSigBits; + + const src_rep_t roundBits = aAbs & roundMask; + + // Round to nearest + if (roundBits > halfway) + absResult++; + + // Ties to even + else if (roundBits == halfway) + absResult += absResult & 1; + } + + else if (aAbs > srcInfinity) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + absResult = (dst_rep_t)dstInfExp << dstSigBits; + absResult |= dstQNaN; + absResult |= aAbs & dstNaNCode; + } + + else if (aAbs > overflow) { + // a overflows to infinity. + absResult = (dst_rep_t)dstInfExp << dstSigBits; + } + + else { + // a underflows on conversion to the destination type or is an exact + // zero. The result may be a denormal or zero. Extract the exponent + // to get the shift amount for the denormalization. + const int aExp = aAbs >> srcSigBits; + const int shift = srcExpBias - dstExpBias - aExp + 1; + + const src_rep_t significand = (aRep & significandMask) | srcMinNormal; + + // Right shift by the denormalization amount with sticky. + if (shift > srcSigBits) { + absResult = 0; + } else { + const bool sticky = significand << (srcBits - shift); + src_rep_t denormalizedSignificand = significand >> shift | sticky; + absResult = denormalizedSignificand >> (srcSigBits - dstSigBits); + const src_rep_t roundBits = denormalizedSignificand & roundMask; + // Round to nearest + if (roundBits > halfway) + absResult++; + // Ties to even + else if (roundBits == halfway) + absResult += absResult & 1; + } + } + + // Apply the signbit to (dst_t)abs(a). + const dst_rep_t result = absResult | sign >> (srcBits - dstBits); + return dstFromRep(result); + +} diff --git a/projects/compiler-rt/lib/tsan/CMakeLists.txt b/projects/compiler-rt/lib/tsan/CMakeLists.txt new file mode 100644 index 00000000..fc1944b0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/CMakeLists.txt @@ -0,0 +1,81 @@ +# Build for the ThreadSanitizer runtime support library. + +include_directories(..) + +# SANITIZER_COMMON_CFLAGS contains -fPIC, but it's performance-critical for +# TSan runtime to be built with -fPIE to reduce the number of register spills. +set(TSAN_CFLAGS + ${SANITIZER_COMMON_CFLAGS} + -fPIE + -fno-rtti) + +set(TSAN_RTL_CFLAGS + ${TSAN_CFLAGS} + -Wframe-larger-than=512) +if(SUPPORTS_GLOBAL_CONSTRUCTORS_FLAG) + list(APPEND TSAN_RTL_CFLAGS -Wglobal-constructors) +endif() +# FIXME: Add support for --sysroot=. compile flag: + +if("${CMAKE_BUILD_TYPE}" EQUAL "Release") + set(TSAN_COMMON_DEFINITIONS DEBUG=0) +else() + set(TSAN_COMMON_DEFINITIONS DEBUG=1) +endif() + +set(TSAN_SOURCES + rtl/tsan_clock.cc + rtl/tsan_flags.cc + rtl/tsan_fd.cc + rtl/tsan_interceptors.cc + rtl/tsan_interface_ann.cc + rtl/tsan_interface_atomic.cc + rtl/tsan_interface.cc + rtl/tsan_interface_java.cc + rtl/tsan_md5.cc + rtl/tsan_mman.cc + rtl/tsan_mutex.cc + rtl/tsan_mutexset.cc + rtl/tsan_report.cc + rtl/tsan_rtl.cc + rtl/tsan_rtl_mutex.cc + rtl/tsan_rtl_report.cc + rtl/tsan_rtl_thread.cc + rtl/tsan_stat.cc + rtl/tsan_suppressions.cc + rtl/tsan_symbolize.cc + rtl/tsan_sync.cc) + +if(APPLE) + list(APPEND TSAN_SOURCES rtl/tsan_platform_mac.cc) +elseif(UNIX) + # Assume Linux + list(APPEND TSAN_SOURCES + rtl/tsan_platform_linux.cc + rtl/tsan_symbolize_addr2line_linux.cc) +endif() + +set(TSAN_RUNTIME_LIBRARIES) +# TSan is currently supported on 64-bit Linux only. +if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE) + set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S) + # Pass ASM file directly to the C++ compiler. + set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES + LANGUAGE C) + set(arch "x86_64") + add_compiler_rt_static_runtime(clang_rt.tsan-${arch} ${arch} + SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} + $ + $ + $ + CFLAGS ${TSAN_RTL_CFLAGS} + DEFS ${TSAN_COMMON_DEFINITIONS}) + add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) + list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch} + clang_rt.tsan-${arch}-symbols) +endif() + +if(LLVM_INCLUDE_TESTS) + add_subdirectory(tests) +endif() +add_subdirectory(lit_tests) diff --git a/projects/compiler-rt/lib/tsan/Makefile.mk b/projects/compiler-rt/lib/tsan/Makefile.mk new file mode 100644 index 00000000..70fb610b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/Makefile.mk @@ -0,0 +1,18 @@ +#===- lib/tsan/Makefile.mk ---------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := tsan +SubDirs := rtl +Sources := +ObjNames := +Dependencies := + +Implementation := Generic + +TsanFunctions := diff --git a/projects/compiler-rt/lib/tsan/Makefile.old b/projects/compiler-rt/lib/tsan/Makefile.old new file mode 100644 index 00000000..c10d4984 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/Makefile.old @@ -0,0 +1,103 @@ +DEBUG=0 +LDFLAGS=-ldl -lpthread -pie +CXXFLAGS = -fPIE -fno-rtti -g -Wall -Werror \ + -DGTEST_HAS_RTTI=0 -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG) +CLANG=clang +FILECHECK=FileCheck +# Silence warnings that Clang produces for gtest code. +# Use -Wno-attributes so that gcc doesn't complain about unknown warning types. +CXXFLAGS += -Wno-attributes +ifeq ($(DEBUG), 0) + CXXFLAGS += -O3 +endif +ifeq ($(CXX), $(CLANG)++) + CXXFLAGS+= -Wno-unused-private-field -Wno-static-in-inline -Wgnu +endif + +LIBTSAN=rtl/libtsan.a +GTEST_ROOT=third_party/googletest +GTEST_INCLUDE=-I$(GTEST_ROOT)/include +GTEST_BUILD_DIR=$(GTEST_ROOT)/build +GTEST_LIB_NAME=gtest-all.o +GTEST_LIB=$(GTEST_BUILD_DIR)/$(GTEST_LIB_NAME) + +SANITIZER_TESTS_PATH=../sanitizer_common/tests +SANITIZER_COMMON_TESTS_SRC=$(wildcard $(SANITIZER_TESTS_PATH)/*_test.cc) +SANITIZER_COMMON_EXCLUDED_TESTS=$(SANITIZER_TESTS_PATH)/sanitizer_nolibc_test.cc +SANITIZER_COMMON_GOOD_TESTS=$(filter-out $(SANITIZER_COMMON_EXCLUDED_TESTS), $(SANITIZER_COMMON_TESTS_SRC)) +SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_GOOD_TESTS)) +RTL_TEST_SRC=$(wildcard tests/rtl/*.cc) +RTL_TEST_OBJ=$(patsubst %.cc,%.o,$(RTL_TEST_SRC)) +UNIT_TEST_SRC=$(wildcard tests/unit/*_test.cc) +UNIT_TEST_OBJ=$(patsubst %.cc,%.o,$(UNIT_TEST_SRC)) +UNIT_TEST_HDR=$(wildcard rtl/*.h) $(wildcard ../sanitizer_common/*.h) + +INCLUDES=-Irtl -I.. -I../../include $(GTEST_INCLUDE) + +all: libtsan test + +help: + @ echo "A little help is always welcome!" + @ echo "The most useful targets are:" + @ echo " make install_deps # Install third-party dependencies required for building" + @ echo " make presubmit # Run it every time before committing" + @ echo + @ echo "For more info, see http://code.google.com/p/thread-sanitizer/wiki/Development" + +$(LIBTSAN): libtsan + +libtsan: + $(MAKE) -C rtl -f Makefile.old DEBUG=$(DEBUG) + +%.o: %.cc $(UNIT_TEST_HDR) $(LIBTSAN) + $(CXX) $(CXXFLAGS) $(CFLAGS) $(INCLUDES) -o $@ -c $< + +tsan_test: $(UNIT_TEST_OBJ) $(RTL_TEST_OBJ) \ + $(SANITIZER_COMMON_TESTS_OBJ) $(LIBTSAN) $(GTEST_LIB) + $(CXX) -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@ $(LDFLAGS) + +test: libtsan tsan_test + +run: all + (ulimit -s 8192; ./tsan_test) + CC=$(CLANG) CXX=$(CLANG)++ FILECHECK=$(FILECHECK) ./lit_tests/test_output.sh + +presubmit: + ../sanitizer_common/scripts/check_lint.sh + # Debug build with clang. + $(MAKE) -f Makefile.old clean + $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=$(CLANG) CXX=$(CLANG)++ + # Release build with clang. + $(MAKE) -f Makefile.old clean + $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++ + # Debug build with gcc + $(MAKE) -f Makefile.old clean + $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++ + # Release build with gcc + $(MAKE) -f Makefile.old clean + $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++ + ./check_analyze.sh + # Sanity check for Go runtime + (cd go && ./buildgo.sh) + # Check cmake build + ./check_cmake.sh + @ echo PRESUBMIT PASSED + +install_deps: + rm -rf third_party + mkdir third_party + (cd third_party && \ + svn co -r613 http://googletest.googlecode.com/svn/trunk googletest \ + ) + +$(GTEST_LIB): + mkdir -p $(GTEST_BUILD_DIR) && \ + cd $(GTEST_BUILD_DIR) && \ + $(MAKE) -f ../make/Makefile CXXFLAGS="$(CXXFLAGS)" CFLAGS="$(CFLAGS)" CC=$(CC) CXX=$(CXX) $(GTEST_LIB_NAME) + +clean: + rm -f asm_*.s libtsan.nm libtsan.objdump */*.o tsan_test + rm -rf $(GTEST_BUILD_DIR) + $(MAKE) clean -C rtl -f Makefile.old + rm -f go/*.s + rm -rf build diff --git a/projects/compiler-rt/lib/tsan/analyze_libtsan.sh b/projects/compiler-rt/lib/tsan/analyze_libtsan.sh new file mode 100755 index 00000000..705e4c54 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/analyze_libtsan.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e +set -u + +get_asm() { + grep __tsan_$1.: -A 10000 libtsan.objdump | \ + awk "/[^:]$/ {print;} />:/ {c++; if (c == 2) {exit}}" +} + +list="write1 \ + write2 \ + write4 \ + write8 \ + read1 \ + read2 \ + read4 \ + read8 \ + func_entry \ + func_exit" + +BIN=`dirname $0`/tsan_test +objdump -d $BIN > libtsan.objdump +nm -S $BIN | grep "__tsan_" > libtsan.nm + +for f in $list; do + file=asm_$f.s + get_asm $f > $file + tot=$(wc -l < $file) + size=$(grep __tsan_$f$ libtsan.nm | awk --non-decimal-data '{print ("0x"$2)+0}') + rsp=$(grep '(%rsp)' $file | wc -l) + push=$(grep 'push' $file | wc -l) + pop=$(grep 'pop' $file | wc -l) + call=$(grep 'call' $file | wc -l) + load=$(egrep 'mov .*\,.*\(.*\)|cmp .*\,.*\(.*\)' $file | wc -l) + store=$(egrep 'mov .*\(.*\),' $file | wc -l) + mov=$(grep 'mov' $file | wc -l) + lea=$(grep 'lea' $file | wc -l) + sh=$(grep 'shr\|shl' $file | wc -l) + cmp=$(grep 'cmp\|test' $file | wc -l) + printf "%10s tot %3d; size %4d; rsp %d; push %d; pop %d; call %d; load %2d; store %2d; sh %3d; mov %3d; lea %3d; cmp %3d\n" \ + $f $tot $size $rsp $push $pop $call $load $store $sh $mov $lea $cmp; +done diff --git a/projects/compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc b/projects/compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc new file mode 100644 index 00000000..accdcb63 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/benchmarks/mini_bench_local.cc @@ -0,0 +1,49 @@ +// Mini-benchmark for tsan: non-shared memory writes. +#include +#include +#include +#include + +int len; +int *a; +const int kNumIter = 1000; + +__attribute__((noinline)) +void Run(int idx) { + for (int i = 0, n = len; i < n; i++) + a[i + idx * n] = i; +} + +void *Thread(void *arg) { + long idx = (long)arg; + printf("Thread %ld started\n", idx); + for (int i = 0; i < kNumIter; i++) + Run(idx); + printf("Thread %ld done\n", idx); + return 0; +} + +int main(int argc, char **argv) { + int n_threads = 0; + if (argc != 3) { + n_threads = 4; + len = 1000000; + } else { + n_threads = atoi(argv[1]); + assert(n_threads > 0 && n_threads <= 32); + len = atoi(argv[2]); + } + printf("%s: n_threads=%d len=%d iter=%d\n", + __FILE__, n_threads, len, kNumIter); + a = new int[n_threads * len]; + pthread_t *t = new pthread_t[n_threads]; + for (int i = 0; i < n_threads; i++) { + pthread_create(&t[i], 0, Thread, (void*)i); + } + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + delete [] t; + delete [] a; + return 0; +} diff --git a/projects/compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc b/projects/compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc new file mode 100644 index 00000000..f9b9f42f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/benchmarks/mini_bench_shared.cc @@ -0,0 +1,51 @@ +// Mini-benchmark for tsan: shared memory reads. +#include +#include +#include +#include + +int len; +int *a; +const int kNumIter = 1000; + +__attribute__((noinline)) +void Run(int idx) { + for (int i = 0, n = len; i < n; i++) + if (a[i] != i) abort(); +} + +void *Thread(void *arg) { + long idx = (long)arg; + printf("Thread %ld started\n", idx); + for (int i = 0; i < kNumIter; i++) + Run(idx); + printf("Thread %ld done\n", idx); + return 0; +} + +int main(int argc, char **argv) { + int n_threads = 0; + if (argc != 3) { + n_threads = 4; + len = 1000000; + } else { + n_threads = atoi(argv[1]); + assert(n_threads > 0 && n_threads <= 32); + len = atoi(argv[2]); + } + printf("%s: n_threads=%d len=%d iter=%d\n", + __FILE__, n_threads, len, kNumIter); + a = new int[len]; + for (int i = 0, n = len; i < n; i++) + a[i] = i; + pthread_t *t = new pthread_t[n_threads]; + for (int i = 0; i < n_threads; i++) { + pthread_create(&t[i], 0, Thread, (void*)i); + } + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + delete [] t; + delete [] a; + return 0; +} diff --git a/projects/compiler-rt/lib/tsan/benchmarks/start_many_threads.cc b/projects/compiler-rt/lib/tsan/benchmarks/start_many_threads.cc new file mode 100644 index 00000000..1e86fa6c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/benchmarks/start_many_threads.cc @@ -0,0 +1,52 @@ +// Mini-benchmark for creating a lot of threads. +// +// Some facts: +// a) clang -O1 takes <15ms to start N=500 threads, +// consuming ~4MB more RAM than N=1. +// b) clang -O1 -ftsan takes ~26s to start N=500 threads, +// eats 5GB more RAM than N=1 (which is somewhat expected but still a lot) +// but then it consumes ~4GB of extra memory when the threads shut down! +// (definitely not in the barrier_wait interceptor) +// Also, it takes 26s to run with N=500 vs just 1.1s to run with N=1. +#include +#include +#include +#include +#include + +pthread_barrier_t all_threads_ready; + +void* Thread(void *unused) { + pthread_barrier_wait(&all_threads_ready); + return 0; +} + +int main(int argc, char **argv) { + int n_threads; + if (argc == 1) { + n_threads = 100; + } else if (argc == 2) { + n_threads = atoi(argv[1]); + } else { + printf("Usage: %s n_threads\n", argv[0]); + return 1; + } + printf("%s: n_threads=%d\n", __FILE__, n_threads); + + pthread_barrier_init(&all_threads_ready, NULL, n_threads + 1); + + pthread_t *t = new pthread_t[n_threads]; + for (int i = 0; i < n_threads; i++) { + int status = pthread_create(&t[i], 0, Thread, (void*)i); + assert(status == 0); + } + // sleep(5); // FIXME: simplify measuring the memory usage. + pthread_barrier_wait(&all_threads_ready); + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + // sleep(5); // FIXME: simplify measuring the memory usage. + delete [] t; + + return 0; +} diff --git a/projects/compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc b/projects/compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc new file mode 100644 index 00000000..f1056e20 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/benchmarks/vts_many_threads_bench.cc @@ -0,0 +1,120 @@ +// Mini-benchmark for tsan VTS worst case performance +// Idea: +// 1) Spawn M + N threads (M >> N) +// We'll call the 'M' threads as 'garbage threads'. +// 2) Make sure all threads have created thus no TIDs were reused +// 3) Join the garbage threads +// 4) Do many sync operations on the remaining N threads +// +// It turns out that due to O(M+N) VTS complexity the (4) is much slower with +// when N is large. +// +// Some numbers: +// a) clang++ native O1 with n_iterations=200kk takes +// 5s regardless of M +// clang++ tsanv2 O1 with n_iterations=20kk takes +// 23.5s with M=200 +// 11.5s with M=1 +// i.e. tsanv2 is ~23x to ~47x slower than native, depends on M. +// b) g++ native O1 with n_iterations=200kk takes +// 5.5s regardless of M +// g++ tsanv1 O1 with n_iterations=2kk takes +// 39.5s with M=200 +// 20.5s with M=1 +// i.e. tsanv1 is ~370x to ~720x slower than native, depends on M. + +#include +#include +#include +#include + +class __attribute__((aligned(64))) Mutex { + public: + Mutex() { pthread_mutex_init(&m_, NULL); } + ~Mutex() { pthread_mutex_destroy(&m_); } + void Lock() { pthread_mutex_lock(&m_); } + void Unlock() { pthread_mutex_unlock(&m_); } + + private: + pthread_mutex_t m_; +}; + +const int kNumMutexes = 1024; +Mutex mutexes[kNumMutexes]; + +int n_threads, n_iterations; + +pthread_barrier_t all_threads_ready, main_threads_ready; + +void* GarbageThread(void *unused) { + pthread_barrier_wait(&all_threads_ready); + return 0; +} + +void *Thread(void *arg) { + long idx = (long)arg; + pthread_barrier_wait(&all_threads_ready); + + // Wait for the main thread to join the garbage threads. + pthread_barrier_wait(&main_threads_ready); + + printf("Thread %ld go!\n", idx); + int offset = idx * kNumMutexes / n_threads; + for (int i = 0; i < n_iterations; i++) { + mutexes[(offset + i) % kNumMutexes].Lock(); + mutexes[(offset + i) % kNumMutexes].Unlock(); + } + printf("Thread %ld done\n", idx); + return 0; +} + +int main(int argc, char **argv) { + int n_garbage_threads; + if (argc == 1) { + n_threads = 2; + n_garbage_threads = 200; + n_iterations = 20000000; + } else if (argc == 4) { + n_threads = atoi(argv[1]); + assert(n_threads > 0 && n_threads <= 32); + n_garbage_threads = atoi(argv[2]); + assert(n_garbage_threads > 0 && n_garbage_threads <= 16000); + n_iterations = atoi(argv[3]); + } else { + printf("Usage: %s n_threads n_garbage_threads n_iterations\n", argv[0]); + return 1; + } + printf("%s: n_threads=%d n_garbage_threads=%d n_iterations=%d\n", + __FILE__, n_threads, n_garbage_threads, n_iterations); + + pthread_barrier_init(&all_threads_ready, NULL, n_garbage_threads + n_threads + 1); + pthread_barrier_init(&main_threads_ready, NULL, n_threads + 1); + + pthread_t *t = new pthread_t[n_threads]; + { + pthread_t *g_t = new pthread_t[n_garbage_threads]; + for (int i = 0; i < n_garbage_threads; i++) { + int status = pthread_create(&g_t[i], 0, GarbageThread, NULL); + assert(status == 0); + } + for (int i = 0; i < n_threads; i++) { + int status = pthread_create(&t[i], 0, Thread, (void*)i); + assert(status == 0); + } + pthread_barrier_wait(&all_threads_ready); + printf("All threads started! Killing the garbage threads.\n"); + for (int i = 0; i < n_garbage_threads; i++) { + pthread_join(g_t[i], 0); + } + delete [] g_t; + } + printf("Resuming the main threads.\n"); + pthread_barrier_wait(&main_threads_ready); + + + for (int i = 0; i < n_threads; i++) { + pthread_join(t[i], 0); + } + delete [] t; + return 0; +} diff --git a/projects/compiler-rt/lib/tsan/check_analyze.sh b/projects/compiler-rt/lib/tsan/check_analyze.sh new file mode 100755 index 00000000..39d570b9 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/check_analyze.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -u + +RES=$(./analyze_libtsan.sh) +PrintRes() { + printf "%s\n" "$RES" +} + +PrintRes + +mops="write1 \ + write2 \ + write4 \ + write8 \ + read1 \ + read2 \ + read4 \ + read8" +func="func_entry \ + func_exit" + +check() { + res=$(PrintRes | egrep "$1 .* $2 $3; ") + if [ "$res" == "" ]; then + echo FAILED $1 must contain $2 $3 + exit 1 + fi +} + +for f in $mops; do + check $f rsp 1 # To read caller pc. + check $f push 0 + check $f pop 0 +done + +for f in $func; do + check $f rsp 0 + check $f push 0 + check $f pop 0 + check $f call 1 # TraceSwitch() +done + +echo LGTM diff --git a/projects/compiler-rt/lib/tsan/check_cmake.sh b/projects/compiler-rt/lib/tsan/check_cmake.sh new file mode 100755 index 00000000..7e858efe --- /dev/null +++ b/projects/compiler-rt/lib/tsan/check_cmake.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -u +set -e + +ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +if [ -d "$ROOT/build" ]; then + cd $ROOT/build +else + mkdir -p $ROOT/build + cd $ROOT/build + CC=clang CXX=clang++ cmake -G Ninja -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../.. +fi +ninja +ninja check-sanitizer +ninja check-tsan +ninja check-asan +ninja check-msan +ninja check-lsan diff --git a/projects/compiler-rt/lib/tsan/go/buildgo.sh b/projects/compiler-rt/lib/tsan/go/buildgo.sh new file mode 100755 index 00000000..bd03fc0b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/go/buildgo.sh @@ -0,0 +1,83 @@ +#!/bin/bash +set -e + +SRCS=" + tsan_go.cc + ../rtl/tsan_clock.cc + ../rtl/tsan_flags.cc + ../rtl/tsan_md5.cc + ../rtl/tsan_mutex.cc + ../rtl/tsan_report.cc + ../rtl/tsan_rtl.cc + ../rtl/tsan_rtl_mutex.cc + ../rtl/tsan_rtl_report.cc + ../rtl/tsan_rtl_thread.cc + ../rtl/tsan_stat.cc + ../rtl/tsan_suppressions.cc + ../rtl/tsan_sync.cc + ../../sanitizer_common/sanitizer_allocator.cc + ../../sanitizer_common/sanitizer_common.cc + ../../sanitizer_common/sanitizer_flags.cc + ../../sanitizer_common/sanitizer_libc.cc + ../../sanitizer_common/sanitizer_printf.cc + ../../sanitizer_common/sanitizer_suppressions.cc + ../../sanitizer_common/sanitizer_thread_registry.cc +" + +if [ "`uname -a | grep Linux`" != "" ]; then + SUFFIX="linux_amd64" + OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Werror" + OSLDFLAGS="-lpthread -fPIC -fpie" + SRCS+=" + ../rtl/tsan_platform_linux.cc + ../../sanitizer_common/sanitizer_posix.cc + ../../sanitizer_common/sanitizer_posix_libcdep.cc + ../../sanitizer_common/sanitizer_linux.cc + ../../sanitizer_common/sanitizer_linux_libcdep.cc + ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc + " +elif [ "`uname -a | grep Darwin`" != "" ]; then + SUFFIX="darwin_amd64" + OSCFLAGS="-fPIC" + OSLDFLAGS="-lpthread -fPIC -fpie" + SRCS+=" + ../rtl/tsan_platform_mac.cc + ../../sanitizer_common/sanitizer_posix.cc + ../../sanitizer_common/sanitizer_mac.cc + ../../sanitizer_common/sanitizer_posix_libcdep.cc + " +elif [ "`uname -a | grep MINGW`" != "" ]; then + SUFFIX="windows_amd64" + OSCFLAGS="-Wno-error=attributes -Wno-attributes" + OSLDFLAGS="" + SRCS+=" + ../rtl/tsan_platform_windows.cc + ../../sanitizer_common/sanitizer_win.cc + " +else + echo Unknown platform + exit 1 +fi + +SRCS+=$ADD_SRCS + +rm -f gotsan.cc +for F in $SRCS; do + cat $F >> gotsan.cc +done + +FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -m64 -Wall -fno-exceptions -fno-rtti -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 $OSCFLAGS" +if [ "$DEBUG" == "" ]; then + FLAGS+=" -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer" +else + FLAGS+=" -DTSAN_DEBUG=1 -g" +fi + +echo gcc gotsan.cc -S -o tmp.s $FLAGS $CFLAGS +gcc gotsan.cc -S -o tmp.s $FLAGS $CFLAGS +cat tmp.s $ASMS > gotsan.s +echo as gotsan.s -o race_$SUFFIX.syso +as gotsan.s -o race_$SUFFIX.syso + +gcc test.c race_$SUFFIX.syso -m64 -o test $OSLDFLAGS +GORACE="exitcode=0 atexit_sleep_ms=0" ./test diff --git a/projects/compiler-rt/lib/tsan/go/test.c b/projects/compiler-rt/lib/tsan/go/test.c new file mode 100644 index 00000000..859b35d3 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/go/test.c @@ -0,0 +1,67 @@ +//===-- test.c ------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Sanity test for Go runtime. +// +//===----------------------------------------------------------------------===// + +#include + +void __tsan_init(void **thr); +void __tsan_fini(); +void __tsan_map_shadow(void *addr, unsigned long size); +void __tsan_go_start(void *thr, void **chthr, void *pc); +void __tsan_go_end(void *thr); +void __tsan_read(void *thr, void *addr, void *pc); +void __tsan_write(void *thr, void *addr, void *pc); +void __tsan_func_enter(void *thr, void *pc); +void __tsan_func_exit(void *thr); +void __tsan_malloc(void *thr, void *p, unsigned long sz, void *pc); +void __tsan_free(void *p); +void __tsan_acquire(void *thr, void *addr); +void __tsan_release(void *thr, void *addr); +void __tsan_release_merge(void *thr, void *addr); + +int __tsan_symbolize(void *pc, char **img, char **rtn, char **file, int *l) { + return 0; +} + +char buf[10]; + +void foobar() {} +void barfoo() {} + +int main(void) { + void *thr0 = 0; + __tsan_init(&thr0); + __tsan_map_shadow(buf, sizeof(buf) + 4096); + __tsan_func_enter(thr0, (char*)&main + 1); + __tsan_malloc(thr0, buf, 10, 0); + __tsan_release(thr0, buf); + __tsan_release_merge(thr0, buf); + void *thr1 = 0; + __tsan_go_start(thr0, &thr1, (char*)&barfoo + 1); + void *thr2 = 0; + __tsan_go_start(thr0, &thr2, (char*)&barfoo + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_func_enter(thr1, (char*)&foobar + 1); + __tsan_write(thr1, buf, (char*)&barfoo + 1); + __tsan_acquire(thr1, buf); + __tsan_func_exit(thr1); + __tsan_func_exit(thr1); + __tsan_go_end(thr1); + __tsan_func_enter(thr2, (char*)&foobar + 1); + __tsan_read(thr2, buf, (char*)&barfoo + 1); + __tsan_func_exit(thr2); + __tsan_go_end(thr2); + __tsan_free(buf); + __tsan_func_exit(thr0); + __tsan_fini(); + return 0; +} diff --git a/projects/compiler-rt/lib/tsan/go/tsan_go.cc b/projects/compiler-rt/lib/tsan/go/tsan_go.cc new file mode 100644 index 00000000..df54bb8e --- /dev/null +++ b/projects/compiler-rt/lib/tsan/go/tsan_go.cc @@ -0,0 +1,198 @@ +//===-- tsan_go.cc --------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ThreadSanitizer runtime for Go language. +// +//===----------------------------------------------------------------------===// + +#include "tsan_rtl.h" +#include "tsan_symbolize.h" +#include "sanitizer_common/sanitizer_common.h" +#include + +namespace __tsan { + +void InitializeInterceptors() { +} + +void InitializeDynamicAnnotations() { +} + +bool IsExpectedReport(uptr addr, uptr size) { + return false; +} + +void internal_start_thread(void(*func)(void*), void *arg) { +} + +ReportLocation *SymbolizeData(uptr addr) { + return 0; +} + +ReportStack *NewReportStackEntry(uptr addr) { + ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, + sizeof(ReportStack)); + internal_memset(ent, 0, sizeof(*ent)); + ent->pc = addr; + return ent; +} + +void *internal_alloc(MBlockType typ, uptr sz) { + return InternalAlloc(sz); +} + +void internal_free(void *p) { + InternalFree(p); +} + +// Callback into Go. +extern "C" int __tsan_symbolize(uptr pc, char **func, char **file, + int *line, int *off); + +ReportStack *SymbolizeCode(uptr addr) { + ReportStack *s = (ReportStack*)internal_alloc(MBlockReportStack, + sizeof(ReportStack)); + internal_memset(s, 0, sizeof(*s)); + s->pc = addr; + char *func = 0, *file = 0; + int line = 0, off = 0; + if (__tsan_symbolize(addr, &func, &file, &line, &off)) { + s->offset = off; + s->func = internal_strdup(func ? func : "??"); + s->file = internal_strdup(file ? file : "-"); + s->line = line; + s->col = 0; + free(func); + free(file); + } + return s; +} + +extern "C" { + +static ThreadState *main_thr; + +static ThreadState *AllocGoroutine() { + ThreadState *thr = (ThreadState*)internal_alloc(MBlockThreadContex, + sizeof(ThreadState)); + internal_memset(thr, 0, sizeof(*thr)); + return thr; +} + +void __tsan_init(ThreadState **thrp) { + ThreadState *thr = AllocGoroutine(); + main_thr = *thrp = thr; + thr->in_rtl++; + Initialize(thr); + thr->in_rtl--; +} + +void __tsan_fini() { + // FIXME: Not necessary thread 0. + ThreadState *thr = main_thr; + thr->in_rtl++; + int res = Finalize(thr); + thr->in_rtl--; + exit(res); +} + +void __tsan_map_shadow(uptr addr, uptr size) { + MapShadow(addr, size); +} + +void __tsan_read(ThreadState *thr, void *addr, void *pc) { + MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1); +} + +void __tsan_write(ThreadState *thr, void *addr, void *pc) { + MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1); +} + +void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr step, + void *pc) { + (void)step; + MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, false); +} + +void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr step, + void *pc) { + (void)step; + MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, true); +} + +void __tsan_func_enter(ThreadState *thr, void *pc) { + FuncEntry(thr, (uptr)pc); +} + +void __tsan_func_exit(ThreadState *thr) { + FuncExit(thr); +} + +void __tsan_malloc(ThreadState *thr, void *p, uptr sz, void *pc) { + if (thr == 0) // probably before __tsan_init() + return; + thr->in_rtl++; + MemoryResetRange(thr, (uptr)pc, (uptr)p, sz); + thr->in_rtl--; +} + +void __tsan_free(void *p) { + (void)p; +} + +void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) { + ThreadState *thr = AllocGoroutine(); + *pthr = thr; + thr->in_rtl++; + parent->in_rtl++; + int goid = ThreadCreate(parent, (uptr)pc, 0, true); + ThreadStart(thr, goid, 0); + parent->in_rtl--; + thr->in_rtl--; +} + +void __tsan_go_end(ThreadState *thr) { + thr->in_rtl++; + ThreadFinish(thr); + thr->in_rtl--; + internal_free(thr); +} + +void __tsan_acquire(ThreadState *thr, void *addr) { + thr->in_rtl++; + Acquire(thr, 0, (uptr)addr); + thr->in_rtl--; +} + +void __tsan_release(ThreadState *thr, void *addr) { + thr->in_rtl++; + ReleaseStore(thr, 0, (uptr)addr); + thr->in_rtl--; +} + +void __tsan_release_merge(ThreadState *thr, void *addr) { + thr->in_rtl++; + Release(thr, 0, (uptr)addr); + thr->in_rtl--; +} + +void __tsan_finalizer_goroutine(ThreadState *thr) { + AcquireGlobal(thr, 0); +} + +} // extern "C" +} // namespace __tsan + +namespace __sanitizer { + +void SymbolizerPrepareForSandboxing() { + // Nothing to do here for Go. +} + +} // namespace __sanitizer diff --git a/projects/compiler-rt/lib/tsan/lit_tests/CMakeLists.txt b/projects/compiler-rt/lib/tsan/lit_tests/CMakeLists.txt new file mode 100644 index 00000000..1f2fbf98 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/CMakeLists.txt @@ -0,0 +1,35 @@ +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) + +if(COMPILER_RT_CAN_EXECUTE_TESTS AND CAN_TARGET_x86_64) + # Run TSan output tests only if we're sure we can produce working binaries. + set(TSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + ${TSAN_RUNTIME_LIBRARIES}) + set(TSAN_TEST_PARAMS + tsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + if(LLVM_INCLUDE_TESTS) + list(APPEND TSAN_TEST_DEPS TsanUnitTests) + endif() + add_lit_testsuite(check-tsan "Running ThreadSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS ${TSAN_TEST_PARAMS} + DEPENDS ${TSAN_TEST_DEPS} + ) + set_target_properties(check-tsan PROPERTIES FOLDER "TSan unittests") +elseif(LLVM_INCLUDE_TESTS AND CAN_TARGET_x86_64) + # Otherwise run only TSan unit tests (they are linked using the + # host compiler). + add_lit_testsuite(check-tsan "Running ThreadSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR}/Unit + DEPENDS TsanUnitTests llvm-symbolizer) + set_target_properties(check-tsan PROPERTIES FOLDER "TSan unittests") +endif() diff --git a/projects/compiler-rt/lib/tsan/lit_tests/Helpers/blacklist.txt b/projects/compiler-rt/lib/tsan/lit_tests/Helpers/blacklist.txt new file mode 100644 index 00000000..22225e54 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/Helpers/blacklist.txt @@ -0,0 +1 @@ +fun:*Blacklisted_Thread2* diff --git a/projects/compiler-rt/lib/tsan/lit_tests/Helpers/lit.local.cfg b/projects/compiler-rt/lib/tsan/lit_tests/Helpers/lit.local.cfg new file mode 100644 index 00000000..9246b103 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/Helpers/lit.local.cfg @@ -0,0 +1,2 @@ +# Files in this directory are helper files for other output tests. +config.suffixes = [] diff --git a/projects/compiler-rt/lib/tsan/lit_tests/SharedLibs/lit.local.cfg b/projects/compiler-rt/lib/tsan/lit_tests/SharedLibs/lit.local.cfg new file mode 100644 index 00000000..b3677c17 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/projects/compiler-rt/lib/tsan/lit_tests/SharedLibs/load_shared_lib-so.cc b/projects/compiler-rt/lib/tsan/lit_tests/SharedLibs/load_shared_lib-so.cc new file mode 100644 index 00000000..d05aa6a4 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/SharedLibs/load_shared_lib-so.cc @@ -0,0 +1,22 @@ +//===----------- load_shared_lib-so.cc --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include + +int GLOB_SHARED = 0; + +extern "C" +void *write_from_so(void *unused) { + GLOB_SHARED++; + return NULL; +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/Unit/lit.cfg b/projects/compiler-rt/lib/tsan/lit_tests/Unit/lit.cfg new file mode 100644 index 00000000..36585df1 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/Unit/lit.cfg @@ -0,0 +1,23 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'ThreadSanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with TSan unit tests. +llvm_obj_root = get_required_attr(config, "llvm_obj_root") +config.test_exec_root = os.path.join(llvm_obj_root, "projects", + "compiler-rt", "lib", + "tsan", "tests") +config.test_source_root = config.test_exec_root diff --git a/projects/compiler-rt/lib/tsan/lit_tests/Unit/lit.site.cfg.in b/projects/compiler-rt/lib/tsan/lit_tests/Unit/lit.site.cfg.in new file mode 100644 index 00000000..3701a2ca --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/Unit/lit.site.cfg.in @@ -0,0 +1,8 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/Unit/lit.cfg") diff --git a/projects/compiler-rt/lib/tsan/lit_tests/aligned_vs_unaligned_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/aligned_vs_unaligned_race.cc new file mode 100644 index 00000000..f4533d08 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/aligned_vs_unaligned_race.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// Race between an aligned access and an unaligned access, which +// touches the same memory region. +// This is a real race which is not detected by tsan. +// https://code.google.com/p/thread-sanitizer/issues/detail?id=17 +#include +#include +#include + +uint64_t Global[2]; + +void *Thread1(void *x) { + Global[1]++; + return NULL; +} + +void *Thread2(void *x) { + char *p1 = reinterpret_cast(&Global[0]); + uint64_t *p4 = reinterpret_cast(p1 + 1); + (*p4)++; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("Pass\n"); + // CHECK-NOT: ThreadSanitizer: data race + // CHECK: Pass + return 0; +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/allocator_returns_null.cc b/projects/compiler-rt/lib/tsan/lit_tests/allocator_returns_null.cc new file mode 100644 index 00000000..4b5eb550 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/allocator_returns_null.cc @@ -0,0 +1,64 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process shoudl crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_tsan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH + +#include +#include +#include +#include +#include +#include +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + fprintf(stderr, "x: %p\n", x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/atomic_free.cc b/projects/compiler-rt/lib/tsan/lit_tests/atomic_free.cc new file mode 100644 index 00000000..87d55936 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/atomic_free.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *a) { + __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST); + return 0; +} + +int main() { + int *a = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, a); + sleep(1); + delete a; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/atomic_free2.cc b/projects/compiler-rt/lib/tsan/lit_tests/atomic_free2.cc new file mode 100644 index 00000000..961ff38c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/atomic_free2.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *a) { + sleep(1); + __atomic_fetch_add((int*)a, 1, __ATOMIC_SEQ_CST); + return 0; +} + +int main() { + int *a = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, a); + delete a; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: heap-use-after-free diff --git a/projects/compiler-rt/lib/tsan/lit_tests/atomic_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/atomic_norace.cc new file mode 100644 index 00000000..265459b0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/atomic_norace.cc @@ -0,0 +1,61 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +const int kTestCount = 4; +typedef long long T; +T atomics[kTestCount * 2]; + +void Test(int test, T *p, bool main_thread) { + volatile T sink; + if (test == 0) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + } else if (test == 1) { + if (main_thread) + __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL); + else + __atomic_exchange_n(p, 1, __ATOMIC_ACQ_REL); + } else if (test == 2) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + __atomic_store_n(p, 1, __ATOMIC_SEQ_CST); + } else if (test == 3) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + sink = *p; + } +} + +void *Thread(void *p) { + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[i], false); + } + sleep(2); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d reverse\n", i); + Test(i, &atomics[kTestCount + i], false); + } + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d\n", i); + Test(i, &atomics[i], true); + } + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[kTestCount + i], true); + } + pthread_join(t, 0); +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/atomic_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/atomic_race.cc new file mode 100644 index 00000000..0dfe4d93 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/atomic_race.cc @@ -0,0 +1,80 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +const int kTestCount = 4; +typedef long long T; +T atomics[kTestCount * 2]; + +void Test(int test, T *p, bool main_thread) { + volatile T sink; + if (test == 0) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + *p = 42; + } else if (test == 1) { + if (main_thread) + __atomic_fetch_add(p, 1, __ATOMIC_RELAXED); + else + sink = *p; + } else if (test == 2) { + if (main_thread) + sink = __atomic_load_n(p, __ATOMIC_SEQ_CST); + else + *p = 42; + } else if (test == 3) { + if (main_thread) + __atomic_store_n(p, 1, __ATOMIC_SEQ_CST); + else + sink = *p; + } +} + +void *Thread(void *p) { + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[i], false); + } + sleep(2); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d reverse\n", i); + Test(i, &atomics[kTestCount + i], false); + } + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + for (int i = 0; i < kTestCount; i++) { + fprintf(stderr, "Test %d\n", i); + Test(i, &atomics[i], true); + } + for (int i = 0; i < kTestCount; i++) { + Test(i, &atomics[kTestCount + i], true); + } + pthread_join(t, 0); +} + +// CHECK: Test 0 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 1 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 2 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 3 +// CHECK: ThreadSanitizer: data race +// CHECK-NOT: SUMMARY{{.*}}tsan_interface_atomic +// CHECK: Test 0 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 1 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 2 reverse +// CHECK: ThreadSanitizer: data race +// CHECK: Test 3 reverse +// CHECK: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/atomic_stack.cc b/projects/compiler-rt/lib/tsan/lit_tests/atomic_stack.cc new file mode 100644 index 00000000..841f74b8 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/atomic_stack.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +int Global; + +void *Thread1(void *x) { + sleep(1); + __atomic_fetch_add(&Global, 1, __ATOMIC_RELAXED); + return NULL; +} + +void *Thread2(void *x) { + Global++; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Atomic write of size 4 +// CHECK: #0 __tsan_atomic32_fetch_add +// CHECK: #1 Thread1 diff --git a/projects/compiler-rt/lib/tsan/lit_tests/benign_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/benign_race.cc new file mode 100644 index 00000000..a4d4d23c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/benign_race.cc @@ -0,0 +1,39 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +int WTFGlobal; + +extern "C" { +void AnnotateBenignRaceSized(const char *f, int l, + void *mem, unsigned int size, const char *desc); +void WTFAnnotateBenignRaceSized(const char *f, int l, + void *mem, unsigned int size, + const char *desc); +} + + +void *Thread(void *x) { + Global = 42; + WTFGlobal = 142; + return 0; +} + +int main() { + AnnotateBenignRaceSized(__FILE__, __LINE__, + &Global, sizeof(Global), "Race on Global"); + WTFAnnotateBenignRaceSized(__FILE__, __LINE__, + &WTFGlobal, sizeof(WTFGlobal), + "Race on WTFGlobal"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + Global = 43; + WTFGlobal = 143; + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/blacklist.cc b/projects/compiler-rt/lib/tsan/lit_tests/blacklist.cc new file mode 100644 index 00000000..5baf926e --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/blacklist.cc @@ -0,0 +1,31 @@ +// Test blacklist functionality for TSan. + +// RUN: %clangxx_tsan -O1 %s \ +// RUN: -fsanitize-blacklist=%p/Helpers/blacklist.txt \ +// RUN: -o %t && %t 2>&1 | FileCheck %s +#include +#include + +int Global; + +void *Thread1(void *x) { + Global++; + return NULL; +} + +void *Blacklisted_Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/cond.c b/projects/compiler-rt/lib/tsan/lit_tests/cond.c new file mode 100644 index 00000000..52c87a41 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/cond.c @@ -0,0 +1,53 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: ThreadSanitizer WARNING: double lock +// CHECK-NOT: ThreadSanitizer WARNING: mutex unlock by another thread +// CHECK: OK + +#include +#include +#include + +pthread_mutex_t m; +pthread_cond_t c; +int x; + +void *thr1(void *p) { + int i; + + for (i = 0; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_cond_signal(&c); + pthread_mutex_unlock(&m); + } + return 0; +} + +void *thr2(void *p) { + int i; + + for (i = 1; i < 10; i += 2) { + pthread_mutex_lock(&m); + while (x != i) + pthread_cond_wait(&c, &m); + x = i + 1; + pthread_mutex_unlock(&m); + pthread_cond_broadcast(&c); + } + return 0; +} + +int main() { + pthread_t th1, th2; + + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + pthread_create(&th1, 0, thr1, 0); + pthread_create(&th2, 0, thr2, 0); + pthread_join(th1, 0); + pthread_join(th2, 0); + fprintf(stderr, "OK\n"); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/cond_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/cond_race.cc new file mode 100644 index 00000000..1e2acb24 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/cond_race.cc @@ -0,0 +1,36 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// CHECK: ThreadSanitizer: data race +// CHECK: pthread_cond_signal + +#include +#include +#include + +struct Ctx { + pthread_mutex_t m; + pthread_cond_t c; + bool done; +}; + +void *thr(void *p) { + Ctx *c = (Ctx*)p; + pthread_mutex_lock(&c->m); + c->done = true; + pthread_mutex_unlock(&c->m); + pthread_cond_signal(&c->c); + return 0; +} + +int main() { + Ctx *c = new Ctx(); + pthread_mutex_init(&c->m, 0); + pthread_cond_init(&c->c, 0); + pthread_t th; + pthread_create(&th, 0, thr, c); + pthread_mutex_lock(&c->m); + while (!c->done) + pthread_cond_wait(&c->c, &c->m); + pthread_mutex_unlock(&c->m); + delete c; + pthread_join(th, 0); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/cond_version.c b/projects/compiler-rt/lib/tsan/lit_tests/cond_version.c new file mode 100644 index 00000000..1f966bfa --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/cond_version.c @@ -0,0 +1,44 @@ +// RUN: %clang_tsan -O1 %s -o %t -lrt && %t 2>&1 | FileCheck %s +// Test that pthread_cond is properly intercepted, +// previously there were issues with versioned symbols. +// CHECK: OK + +#include +#include +#include +#include +#include + +int main() { + typedef unsigned long long u64; + pthread_mutex_t m; + pthread_cond_t c; + pthread_condattr_t at; + struct timespec ts0, ts1, ts2; + int res; + u64 sleep; + + pthread_mutex_init(&m, 0); + pthread_condattr_init(&at); + pthread_condattr_setclock(&at, CLOCK_MONOTONIC); + pthread_cond_init(&c, &at); + + clock_gettime(CLOCK_MONOTONIC, &ts0); + ts1 = ts0; + ts1.tv_sec += 2; + + pthread_mutex_lock(&m); + do { + res = pthread_cond_timedwait(&c, &m, &ts1); + } while (res == 0); + pthread_mutex_unlock(&m); + + clock_gettime(CLOCK_MONOTONIC, &ts2); + sleep = (u64)ts2.tv_sec * 1000000000 + ts2.tv_nsec - + ((u64)ts0.tv_sec * 1000000000 + ts0.tv_nsec); + if (res != ETIMEDOUT) + exit(printf("bad return value %d, want %d\n", res, ETIMEDOUT)); + if (sleep < 1000000000) + exit(printf("bad sleep duration %lluns, want %dns\n", sleep, 1000000000)); + fprintf(stderr, "OK\n"); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/deep_stack1.cc b/projects/compiler-rt/lib/tsan/lit_tests/deep_stack1.cc new file mode 100644 index 00000000..3048aa87 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/deep_stack1.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && not %t 2>&1 | FileCheck %s +#include +#include +#include + +volatile int X; +volatile int N; +void (*volatile F)(); + +static void foo() { + if (--N == 0) + X = 42; + else + F(); +} + +void *Thread(void *p) { +#ifdef ORDER1 + sleep(1); +#endif + F(); + return 0; +} + +int main() { + N = 50000; + F = foo; + pthread_t t; + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setstacksize(&a, N * 256 + (1 << 20)); + pthread_create(&t, &a, Thread, 0); +#ifdef ORDER2 + sleep(1); +#endif + X = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #100 foo +// We must output suffucuently large stack (at least 100 frames) + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/default_options.cc b/projects/compiler-rt/lib/tsan/lit_tests/default_options.cc new file mode 100644 index 00000000..62c6c028 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/default_options.cc @@ -0,0 +1,32 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include + +extern "C" const char *__tsan_default_options() { + return "report_bugs=0"; +} + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_close_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_close_norace.cc new file mode 100644 index 00000000..a8b1a6d7 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_close_norace.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include + +void *Thread1(void *x) { + int f = open("/dev/random", O_RDONLY); + close(f); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + int f = open("/dev/random", O_RDONLY); + close(f); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_close_norace2.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_close_norace2.cc new file mode 100644 index 00000000..b42b334a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_close_norace2.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +int pipes[2]; + +void *Thread(void *x) { + // wait for shutown signal + while (read(pipes[0], &x, 1) != 1) { + } + close(pipes[0]); + close(pipes[1]); + return 0; +} + +int main() { + if (pipe(pipes)) + return 1; + pthread_t t; + pthread_create(&t, 0, Thread, 0); + // send shutdown signal + while (write(pipes[1], &t, 1) != 1) { + } + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_dup_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_dup_norace.cc new file mode 100644 index 00000000..8826f90f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_dup_norace.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include + +int fds[2]; + +void *Thread1(void *x) { + char buf; + read(fds[0], &buf, 1); + close(fds[0]); + return 0; +} + +void *Thread2(void *x) { + close(fds[1]); + return 0; +} + +int main() { + fds[0] = open("/dev/random", O_RDONLY); + fds[1] = dup2(fds[0], 100); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_location.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_location.cc new file mode 100644 index 00000000..2b1e9c56 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_location.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int fds[2]; + +void *Thread1(void *x) { + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + close(fds[0]); + close(fds[1]); + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is file descriptor {{[0-9]+}} created by main thread at: +// CHECK: #0 pipe +// CHECK: #1 main + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_pipe_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_pipe_norace.cc new file mode 100644 index 00000000..2da69ea2 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_pipe_norace.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +int fds[2]; +int X; + +void *Thread1(void *x) { + X = 42; + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + char buf; + while (read(fds[0], &buf, 1) != 1) { + } + X = 43; + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_pipe_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_pipe_race.cc new file mode 100644 index 00000000..4dd2b778 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_pipe_race.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int fds[2]; + +void *Thread1(void *x) { + write(fds[1], "a", 1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + close(fds[0]); + close(fds[1]); + return NULL; +} + +int main() { + pipe(fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 8 +// CHECK: #0 close +// CHECK: #1 Thread2 +// CHECK: Previous read of size 8 +// CHECK: #0 write +// CHECK: #1 Thread1 + + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_socket_connect_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_socket_connect_norace.cc new file mode 100644 index 00000000..065299a9 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_socket_connect_norace.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include +#include +#include + +struct sockaddr_in addr; +int X; + +void *ClientThread(void *x) { + int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + X = 42; + if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) { + perror("connect"); + exit(1); + } + close(c); + return NULL; +} + +int main() { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + addr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + addr.sin_port = INADDR_ANY; + socklen_t len = sizeof(addr); + bind(s, (sockaddr*)&addr, len); + getsockname(s, (sockaddr*)&addr, &len); + listen(s, 10); + pthread_t t; + pthread_create(&t, 0, ClientThread, 0); + int c = accept(s, 0, 0); + X = 42; + pthread_join(t, 0); + close(c); + close(s); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_socket_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_socket_norace.cc new file mode 100644 index 00000000..243fc9de --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_socket_norace.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include +#include +#include + +struct sockaddr_in addr; +int X; + +void *ClientThread(void *x) { + X = 42; + int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) { + perror("connect"); + exit(1); + } + if (send(c, "a", 1, 0) != 1) { + perror("send"); + exit(1); + } + close(c); + return NULL; +} + +int main() { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + addr.sin_family = AF_INET; + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + addr.sin_port = INADDR_ANY; + socklen_t len = sizeof(addr); + bind(s, (sockaddr*)&addr, len); + getsockname(s, (sockaddr*)&addr, &len); + listen(s, 10); + pthread_t t; + pthread_create(&t, 0, ClientThread, 0); + int c = accept(s, 0, 0); + char buf; + while (read(c, &buf, 1) != 1) { + } + X = 43; + close(c); + close(s); + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_socketpair_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_socketpair_norace.cc new file mode 100644 index 00000000..f91e4eca --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_socketpair_norace.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +int fds[2]; +int X; + +void *Thread1(void *x) { + X = 42; + write(fds[1], "a", 1); + close(fds[1]); + return NULL; +} + +void *Thread2(void *x) { + char buf; + while (read(fds[0], &buf, 1) != 1) { + } + X = 43; + close(fds[0]); + return NULL; +} + +int main() { + socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/fd_stdout_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/fd_stdout_race.cc new file mode 100644 index 00000000..4b512bb7 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/fd_stdout_race.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include + +int X; + +void *Thread1(void *x) { + sleep(1); + int f = open("/dev/random", O_RDONLY); + char buf; + read(f, &buf, 1); + close(f); + X = 42; + return NULL; +} + +void *Thread2(void *x) { + X = 43; + write(STDOUT_FILENO, "a", 1); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 +// CHECK: #0 Thread1 +// CHECK: Previous write of size 4 +// CHECK: #0 Thread2 + + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/free_race.c b/projects/compiler-rt/lib/tsan/lit_tests/free_race.c new file mode 100644 index 00000000..d1db9fec --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/free_race.c @@ -0,0 +1,49 @@ +// RUN: %clang_tsan -O1 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOZUPP +// RUN: TSAN_OPTIONS="suppressions=%s.supp print_suppressions=1" %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP + +#include +#include +#include +#include +#include + +int *mem; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx); + free(mem); + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_mutex_lock(&mtx); + mem[0] = 42; + pthread_mutex_unlock(&mtx); + return NULL; +} + +int main() { + mem = (int*)malloc(100); + pthread_mutex_init(&mtx, 0); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_mutex_destroy(&mtx); + return 0; +} + +// CHECK-NOZUPP: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK-NOZUPP: Write of size 4 at {{.*}} by main thread{{.*}}: +// CHECK-NOZUPP: #0 Thread2 +// CHECK-NOZUPP: #1 main +// CHECK-NOZUPP: Previous write of size 8 at {{.*}} by thread T1{{.*}}: +// CHECK-NOZUPP: #0 free +// CHECK-NOZUPP: #{{(1|2)}} Thread1 +// CHECK-NOZUPP: SUMMARY: ThreadSanitizer: heap-use-after-free{{.*}}Thread2 +// CHECK-SUPP: ThreadSanitizer: Matched 1 suppressions +// CHECK-SUPP: 1 race:^Thread2$ diff --git a/projects/compiler-rt/lib/tsan/lit_tests/free_race.c.supp b/projects/compiler-rt/lib/tsan/lit_tests/free_race.c.supp new file mode 100644 index 00000000..f5d6a496 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/free_race.c.supp @@ -0,0 +1,2 @@ +# Suppression for a use-after-free in free_race.c +race:^Thread2$ diff --git a/projects/compiler-rt/lib/tsan/lit_tests/free_race2.c b/projects/compiler-rt/lib/tsan/lit_tests/free_race2.c new file mode 100644 index 00000000..2b9a4192 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/free_race2.c @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include + +void __attribute__((noinline)) foo(int *mem) { + free(mem); +} + +void __attribute__((noinline)) bar(int *mem) { + mem[0] = 42; +} + +int main() { + int *mem = (int*)malloc(100); + foo(mem); + bar(mem); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: heap-use-after-free +// CHECK: Write of size 4 at {{.*}} by main thread: +// CHECK: #0 bar +// CHECK: #1 main +// CHECK: Previous write of size 8 at {{.*}} by main thread: +// CHECK: #0 free +// CHECK: #{{1|2}} foo +// CHECK: #{{2|3}} main diff --git a/projects/compiler-rt/lib/tsan/lit_tests/global_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/global_race.cc new file mode 100644 index 00000000..ac201615 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/global_race.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int GlobalData[10]; +int x; +namespace XXX { + struct YYY { + static int ZZZ[10]; + }; + int YYY::ZZZ[10]; +} + +void *Thread(void *a) { + GlobalData[2] = 42; + x = 1; + XXX::YYY::ZZZ[0] = 1; + return 0; +} + +int main() { + fprintf(stderr, "addr=%p\n", GlobalData); + fprintf(stderr, "addr2=%p\n", &x); + fprintf(stderr, "addr3=%p\n", XXX::YYY::ZZZ); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + GlobalData[2] = 43; + x = 0; + XXX::YYY::ZZZ[0] = 0; + pthread_join(t, 0); +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: addr2=[[ADDR2:0x[0-9,a-f]+]] +// CHECK: addr3=[[ADDR3:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] ({{.*}}+0x{{[0-9,a-f]+}}) +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'x' of size 4 at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}}) +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is global 'XXX::YYY::ZZZ' of size 40 at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}}) diff --git a/projects/compiler-rt/lib/tsan/lit_tests/halt_on_error.cc b/projects/compiler-rt/lib/tsan/lit_tests/halt_on_error.cc new file mode 100644 index 00000000..fddaffff --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/halt_on_error.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" not %t 2>&1 | FileCheck %s +#include +#include + +int X; + +void *Thread(void *x) { + X = 42; + return 0; +} + +int main() { + fprintf(stderr, "BEFORE\n"); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + fprintf(stderr, "AFTER\n"); + return 0; +} + +// CHECK: BEFORE +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: AFTER + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/heap_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/heap_race.cc new file mode 100644 index 00000000..cc2c1fee --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/heap_race.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +void *Thread(void *a) { + ((int*)a)[0]++; + return NULL; +} + +int main() { + int *p = new int(42); + pthread_t t; + pthread_create(&t, NULL, Thread, p); + p[0]++; + pthread_join(t, NULL); + delete p; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_free.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_free.cc new file mode 100644 index 00000000..60369cc1 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_free.cc @@ -0,0 +1,35 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int *p = new int(0); + pthread_t t; + pthread_create(&t, 0, Thread, p); + sleep(1); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + free(p); + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + pthread_join(t, 0); + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib0.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib0.cc new file mode 100644 index 00000000..ea0f061e --- /dev/null +++ b/projects/compiler-rt/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/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib0.cc.supp b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib0.cc.supp new file mode 100644 index 00000000..7728c926 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib0.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib0.so + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib1.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib1.cc new file mode 100644 index 00000000..c4f2e734 --- /dev/null +++ b/projects/compiler-rt/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 +#include +#include +#include +#include +#include + +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/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib1.cc.supp b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib1.cc.supp new file mode 100644 index 00000000..9f4119ec --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib1.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:/libignore_lib1.so$ + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib2.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib2.cc new file mode 100644 index 00000000..97f9419e --- /dev/null +++ b/projects/compiler-rt/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 +#include +#include +#include + +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/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib2.cc.supp b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib2.cc.supp new file mode 100644 index 00000000..1419c71c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib2.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib2 + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib3.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib3.cc new file mode 100644 index 00000000..8f237fcc --- /dev/null +++ b/projects/compiler-rt/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 +#include +#include +#include +#include +#include + +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/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib3.cc.supp b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib3.cc.supp new file mode 100644 index 00000000..975dbcef --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib3.cc.supp @@ -0,0 +1,2 @@ +called_from_lib:ignore_lib3.so + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib_lib.h b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib_lib.h new file mode 100644 index 00000000..2bfe84df --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_lib_lib.h @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include + +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/projects/compiler-rt/lib/tsan/lit_tests/ignore_malloc.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_malloc.cc new file mode 100644 index 00000000..63bd4241 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_malloc.cc @@ -0,0 +1,38 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +extern "C" { +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); +} + +int *g; + +void *Thread(void *a) { + int *p = 0; + while ((p = __atomic_load_n(&g, __ATOMIC_RELAXED)) == 0) + usleep(100); + *p = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + int *p = new int(0); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + __atomic_store_n(&g, p, __ATOMIC_RELAXED); + pthread_join(t, 0); + delete p; + fprintf(stderr, "OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: OK diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_race.cc new file mode 100644 index 00000000..23d74d0e --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; + +extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l); +extern "C" void AnnotateIgnoreWritesEnd(const char *f, int l); +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); +extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l); + +void *Thread(void *x) { + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + AnnotateIgnoreReadsBegin(__FILE__, __LINE__); + Global = 42; + AnnotateIgnoreReadsEnd(__FILE__, __LINE__); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + Global = 43; + pthread_join(t, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/ignore_sync.cc b/projects/compiler-rt/lib/tsan/lit_tests/ignore_sync.cc new file mode 100644 index 00000000..67f2d906 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/ignore_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +extern "C" void AnnotateIgnoreSyncBegin(const char*, int); +extern "C" void AnnotateIgnoreSyncEnd(const char*, int); + +int Global; +pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER; + +void *Thread(void *x) { + AnnotateIgnoreSyncBegin(0, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + AnnotateIgnoreSyncEnd(0, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_mutex_lock(&Mutex); + Global++; + pthread_mutex_unlock(&Mutex); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/inlined_memcpy_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/inlined_memcpy_race.cc new file mode 100644 index 00000000..5dda36e4 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/inlined_memcpy_race.cc @@ -0,0 +1,55 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +int x[4], y[4], z[4]; + +void *MemCpyThread(void *a) { + memcpy((int*)a, z, 16); + return NULL; +} + +void *MemMoveThread(void *a) { + memmove((int*)a, z, 16); + return NULL; +} + +void *MemSetThread(void *a) { + sleep(1); + memset((int*)a, 0, 16); + return NULL; +} + +int main() { + pthread_t t[2]; + // Race on x between memcpy and memset + pthread_create(&t[0], NULL, MemCpyThread, x); + pthread_create(&t[1], NULL, MemSetThread, x); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + // Race on y between memmove and memset + pthread_create(&t[0], NULL, MemMoveThread, y); + pthread_create(&t[1], NULL, MemSetThread, y); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + + printf("PASS\n"); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 memset +// CHECK: #1 MemSetThread +// CHECK: Previous write +// CHECK: #0 memcpy +// CHECK: #1 MemCpyThread + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 memset +// CHECK: #1 MemSetThread +// CHECK: Previous write +// CHECK: #0 memmove +// CHECK: #1 MemMoveThread diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java.h b/projects/compiler-rt/lib/tsan/lit_tests/java.h new file mode 100644 index 00000000..7aa0bca3 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java.h @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +extern "C" { +typedef unsigned long jptr; // NOLINT +void __tsan_java_preinit(const char *libjvm_path); +void __tsan_java_init(jptr heap_begin, jptr heap_size); +int __tsan_java_fini(); +void __tsan_java_alloc(jptr ptr, jptr size); +void __tsan_java_free(jptr ptr, jptr size); +void __tsan_java_move(jptr src, jptr dst, jptr size); +void __tsan_java_mutex_lock(jptr addr); +void __tsan_java_mutex_unlock(jptr addr); +void __tsan_java_mutex_read_lock(jptr addr); +void __tsan_java_mutex_read_unlock(jptr addr); +void __tsan_java_mutex_lock_rec(jptr addr, int rec); +int __tsan_java_mutex_unlock_rec(jptr addr); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_alloc.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_alloc.cc new file mode 100644 index 00000000..4dbce70c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_alloc.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" + +int const kHeapSize = 1024 * 1024; + +void stress(jptr addr) { + for (jptr sz = 8; sz <= 32; sz <<= 1) { + for (jptr i = 0; i < kHeapSize / 4 / sz; i++) { + __tsan_java_alloc(addr + i * sz, sz); + } + __tsan_java_move(addr, addr + kHeapSize / 2, kHeapSize / 4); + __tsan_java_free(addr + kHeapSize / 2, kHeapSize / 4); + } +} + +void *Thread(void *p) { + stress((jptr)p); + return 0; +} + +int main() { + jptr jheap = (jptr)malloc(kHeapSize); + __tsan_java_init(jheap, kHeapSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4)); + stress(jheap); + pthread_join(th, 0); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_lock.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_lock.cc new file mode 100644 index 00000000..d9db1035 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_lock.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" +#include + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 42; + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_lock_move.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_lock_move.cc new file mode 100644 index 00000000..48b5a5a8 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_lock_move.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" + +jptr varaddr; +jptr lockaddr; +jptr varaddr2; +jptr lockaddr2; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_lock(lockaddr2); + *(int*)varaddr2 = 42; + __tsan_java_mutex_unlock(lockaddr2); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 1024; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + lockaddr = (jptr)jheap + 46; + varaddr2 = varaddr + kMove; + lockaddr2 = lockaddr + kMove; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_move(varaddr, varaddr2, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr2, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_lock_rec.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_lock_rec.cc new file mode 100644 index 00000000..5cc80d4a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_lock_rec.cc @@ -0,0 +1,54 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" +#include + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 42; + int rec = __tsan_java_mutex_unlock_rec(lockaddr); + if (rec != 2) { + printf("FAILED 0 rec=%d\n", rec); + exit(1); + } + sleep(2); + __tsan_java_mutex_lock_rec(lockaddr, rec); + if (*(int*)varaddr != 43) { + printf("FAILED 3 var=%d\n", *(int*)varaddr); + exit(1); + } + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + *(int*)varaddr = 0; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + sleep(1); + __tsan_java_mutex_lock(lockaddr); + if (*(int*)varaddr != 42) { + printf("FAILED 1 var=%d\n", *(int*)varaddr); + exit(1); + } + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_lock_rec_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_lock_rec_race.cc new file mode 100644 index 00000000..a868e260 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_lock_rec_race.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include "java.h" +#include + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + __tsan_java_mutex_lock(lockaddr); + int rec = __tsan_java_mutex_unlock_rec(lockaddr); + if (rec != 3) { + printf("FAILED 0 rec=%d\n", rec); + exit(1); + } + *(int*)varaddr = 42; + sleep(2); + __tsan_java_mutex_lock_rec(lockaddr, rec); + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + __tsan_java_mutex_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + *(int*)varaddr = 0; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + sleep(1); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: FAILED diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_race.cc new file mode 100644 index 00000000..4841a7db --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_race.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include "java.h" + +void *Thread(void *p) { + *(int*)p = 42; + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, jheap); + *(int*)jheap = 43; + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_race_move.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_race_move.cc new file mode 100644 index 00000000..6da8a106 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_race_move.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include "java.h" + +jptr varaddr; +jptr varaddr2; + +void *Thread(void *p) { + sleep(1); + *(int*)varaddr2 = 42; + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 64; + int const kMove = 1024; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap + 16; + varaddr2 = varaddr + kMove; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + *(int*)varaddr = 43; + __tsan_java_move(varaddr, varaddr2, kBlockSize); + pthread_join(th, 0); + __tsan_java_free(varaddr2, kBlockSize); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/java_rwlock.cc b/projects/compiler-rt/lib/tsan/lit_tests/java_rwlock.cc new file mode 100644 index 00000000..d1f38733 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/java_rwlock.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include "java.h" +#include + +jptr varaddr; +jptr lockaddr; + +void *Thread(void *p) { + sleep(1); + __tsan_java_mutex_read_lock(lockaddr); + *(int*)varaddr = 42; + __tsan_java_mutex_read_unlock(lockaddr); + return 0; +} + +int main() { + int const kHeapSize = 1024 * 1024; + void *jheap = malloc(kHeapSize); + __tsan_java_init((jptr)jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc((jptr)jheap, kBlockSize); + varaddr = (jptr)jheap; + lockaddr = (jptr)jheap + 8; + pthread_t th; + pthread_create(&th, 0, Thread, 0); + __tsan_java_mutex_lock(lockaddr); + *(int*)varaddr = 43; + __tsan_java_mutex_unlock(lockaddr); + pthread_join(th, 0); + __tsan_java_free((jptr)jheap, kBlockSize); + printf("OK\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/lit.cfg b/projects/compiler-rt/lib/tsan/lit_tests/lit.cfg new file mode 100644 index 00000000..c4193639 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/lit.cfg @@ -0,0 +1,81 @@ +# -*- Python -*- + +import os + +import lit.util + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'ThreadSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +def DisplayNoConfigMessage(): + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-tsan") + +# Figure out LLVM source root. +llvm_src_root = getattr(config, 'llvm_src_root', None) +if llvm_src_root is None: + # We probably haven't loaded the site-specific configuration: the user + # is likely trying to run a test file directly, and the site configuration + # wasn't created by the build system. + tsan_site_cfg = lit_config.params.get('tsan_site_config', None) + if (tsan_site_cfg) and (os.path.exists(tsan_site_cfg)): + lit_config.load_config(config, tsan_site_cfg) + raise SystemExit + + # Try to guess the location of site-specific configuration using llvm-config + # util that can point where the build tree is. + llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) + if not llvm_config: + DisplayNoConfigMessage() + + # Find out the presumed location of generated site config. + llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() + tsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", + "lib", "tsan", "lit_tests", "lit.site.cfg") + if (not tsan_site_cfg) or (not os.path.exists(tsan_site_cfg)): + DisplayNoConfigMessage() + + lit_config.load_config(config, tsan_site_cfg) + raise SystemExit + +# Setup environment variables for running ThreadSanitizer. +tsan_options = "atexit_sleep_ms=0" + +config.environment['TSAN_OPTIONS'] = tsan_options + +# Setup default compiler flags used with -fsanitize=thread option. +# FIXME: Review the set of required flags and check if it can be reduced. +clang_tsan_cflags = ("-fsanitize=thread " + + "-g " + + "-Wall " + + "-lpthread " + + "-ldl " + + "-m64 ") +clang_tsan_cxxflags = "--driver-mode=g++ " + clang_tsan_cflags +config.substitutions.append( ("%clangxx_tsan ", (" " + config.clang + " " + + clang_tsan_cxxflags + " ")) ) +config.substitutions.append( ("%clang_tsan ", (" " + config.clang + " " + + clang_tsan_cflags + " ")) ) + +# Define CHECK-%os to check for OS-dependent output. +config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# ThreadSanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/tsan/lit_tests/lit.site.cfg.in b/projects/compiler-rt/lib/tsan/lit_tests/lit.site.cfg.in new file mode 100644 index 00000000..b0e42744 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/lit.site.cfg.in @@ -0,0 +1,8 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/projects/compiler-rt/lib/tsan/lit_tests/load_shared_lib.cc b/projects/compiler-rt/lib/tsan/lit_tests/load_shared_lib.cc new file mode 100644 index 00000000..d60cd570 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/load_shared_lib.cc @@ -0,0 +1,44 @@ +// Check that if the list of shared libraries changes between the two race +// reports, the second report occurring in a new shared library is still +// symbolized correctly. + +// RUN: %clangxx_tsan -O1 %p/SharedLibs/load_shared_lib-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s + +#include +#include +#include + +#include + +int GLOB = 0; + +void *write_glob(void *unused) { + GLOB++; + return NULL; +} + +void race_two_threads(void *(*access_callback)(void *unused)) { + pthread_t t1, t2; + pthread_create(&t1, NULL, access_callback, NULL); + pthread_create(&t2, NULL, access_callback, NULL); + pthread_join(t1, NULL); + pthread_join(t2, NULL); +} + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + std::string("-so.so"); + race_two_threads(write_glob); + // CHECK: write_glob + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + void *(*write_from_so)(void *unused); + *(void **)&write_from_so = dlsym(lib, "write_from_so"); + race_two_threads(write_from_so); + // CHECK: write_from_so + return 0; +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/longjmp.cc b/projects/compiler-rt/lib/tsan/lit_tests/longjmp.cc new file mode 100644 index 00000000..d9ca4ca5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/longjmp.cc @@ -0,0 +1,22 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +int foo(jmp_buf env) { + longjmp(env, 42); +} + +int main() { + jmp_buf env; + if (setjmp(env) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/projects/compiler-rt/lib/tsan/lit_tests/longjmp2.cc b/projects/compiler-rt/lib/tsan/lit_tests/longjmp2.cc new file mode 100644 index 00000000..0d551fa1 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/longjmp2.cc @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +int foo(sigjmp_buf env) { + printf("env=%p\n", env); + siglongjmp(env, 42); +} + +int main() { + sigjmp_buf env; + printf("env=%p\n", env); + if (sigsetjmp(env, 1) == 42) { + printf("JUMPED\n"); + return 0; + } + foo(env); + printf("FAILED\n"); + return 0; +} + +// CHECK-NOT: FAILED +// CHECK: JUMPED diff --git a/projects/compiler-rt/lib/tsan/lit_tests/longjmp3.cc b/projects/compiler-rt/lib/tsan/lit_tests/longjmp3.cc new file mode 100644 index 00000000..ae2cfd05 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/longjmp3.cc @@ -0,0 +1,48 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +void bar(jmp_buf env) { + volatile int x = 42; + longjmp(env, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/longjmp4.cc b/projects/compiler-rt/lib/tsan/lit_tests/longjmp4.cc new file mode 100644 index 00000000..6b0526ef --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/longjmp4.cc @@ -0,0 +1,51 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +void bar(jmp_buf env) { + volatile int x = 42; + jmp_buf env2; + memcpy(env2, env, sizeof(jmp_buf)); + longjmp(env2, 42); + x++; +} + +void foo(jmp_buf env) { + volatile int x = 42; + bar(env); + x++; +} + +void badguy() { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + pthread_mutex_destroy(&mtx); +} + +void mymain() { + jmp_buf env; + if (setjmp(env) == 42) { + badguy(); + return; + } + foo(env); + printf("FAILED\n"); +} + +int main() { + volatile int x = 42; + mymain(); + return x; +} + +// CHECK-NOT: FAILED +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 badguy +// CHECK: #2 mymain +// CHECK: #3 main + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/malloc_overflow.cc b/projects/compiler-rt/lib/tsan/lit_tests/malloc_overflow.cc new file mode 100644 index 00000000..afbebc8b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/malloc_overflow.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS=allocator_may_return_null=1 %t 2>&1 | FileCheck %s +#include +#include + +int main() { + void *p = malloc((size_t)-1); + if (p != 0) + printf("FAIL malloc(-1) = %p\n", p); + p = malloc((size_t)-1 / 2); + if (p != 0) + printf("FAIL malloc(-1/2) = %p\n", p); + p = calloc((size_t)-1, (size_t)-1); + if (p != 0) + printf("FAIL calloc(-1, -1) = %p\n", p); + p = calloc((size_t)-1 / 2, (size_t)-1 / 2); + if (p != 0) + printf("FAIL calloc(-1/2, -1/2) = %p\n", p); + printf("OK\n"); +} + +// CHECK-NOT: FAIL +// CHECK-NOT: failed to allocate diff --git a/projects/compiler-rt/lib/tsan/lit_tests/malloc_stack.cc b/projects/compiler-rt/lib/tsan/lit_tests/malloc_stack.cc new file mode 100644 index 00000000..3603497e --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/malloc_stack.cc @@ -0,0 +1,25 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +_Atomic(int*) p; + +void *thr(void *a) { + sleep(1); + int *pp = __c11_atomic_load(&p, __ATOMIC_RELAXED); + *pp = 42; + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, thr, p); + __c11_atomic_store(&p, new int, __ATOMIC_RELAXED); + pthread_join(th, 0); +} + +// CHECK: data race +// CHECK: Previous write +// CHECK: #0 operator new +// CHECK: Location is heap block +// CHECK: #0 operator new diff --git a/projects/compiler-rt/lib/tsan/lit_tests/memcpy_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/memcpy_race.cc new file mode 100644 index 00000000..8f391136 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/memcpy_race.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +char *data = new char[10]; +char *data1 = new char[10]; +char *data2 = new char[10]; + +void *Thread1(void *x) { + static volatile int size = 1; + memcpy(data+5, data1, size); + return NULL; +} + +void *Thread2(void *x) { + static volatile int size = 4; + sleep(1); + memcpy(data+3, data2, size); + return NULL; +} + +int main() { + fprintf(stderr, "addr=%p\n", &data[5]); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[ADDR]] by thread T2: +// CHECK: #0 memcpy +// CHECK: #1 Thread2 +// CHECK: Previous write of size 1 at [[ADDR]] by thread T1: +// CHECK: #0 memcpy +// CHECK: #1 Thread1 diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc b/projects/compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc new file mode 100644 index 00000000..2b6a4ff5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mop_with_offset.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +void *Thread1(void *x) { + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int *data = new int(42); + fprintf(stderr, "ptr1=%p\n", data); + fprintf(stderr, "ptr2=%p\n", (char*)data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, data); + pthread_create(&t[1], NULL, Thread2, data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + delete data; +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 at [[PTR2]] by thread T2: +// CHECK: Previous write of size 4 at [[PTR1]] by thread T1: diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc b/projects/compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc new file mode 100644 index 00000000..037c4db5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mop_with_offset2.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +void *Thread1(void *x) { + sleep(1); + int *p = (int*)x; + p[0] = 1; + return NULL; +} + +void *Thread2(void *x) { + char *p = (char*)x; + p[2] = 1; + return NULL; +} + +int main() { + int *data = new int(42); + fprintf(stderr, "ptr1=%p\n", data); + fprintf(stderr, "ptr2=%p\n", (char*)data + 2); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, data); + pthread_create(&t[1], NULL, Thread2, data); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + delete data; +} + +// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]] +// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at [[PTR1]] by thread T1: +// CHECK: Previous write of size 1 at [[PTR2]] by thread T2: diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutex_destroy_locked.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutex_destroy_locked.cc new file mode 100644 index 00000000..9b020d31 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutex_destroy_locked.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +int main() { + pthread_mutex_t m; + pthread_mutex_init(&m, 0); + pthread_mutex_lock(&m); + pthread_mutex_destroy(&m); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex +// CHECK: #0 pthread_mutex_destroy +// CHECK: #1 main +// CHECK: and: +// CHECK: #0 pthread_mutex_lock +// CHECK: #1 main +// CHECK: Mutex {{.*}} created at: +// CHECK: #0 pthread_mutex_init +// CHECK: #1 main +// CHECK: SUMMARY: ThreadSanitizer: destroy of a locked mutex{{.*}}main diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutex_robust.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutex_robust.cc new file mode 100644 index 00000000..b8266160 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutex_robust.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +pthread_mutex_t m; + +void *thr(void *p) { + pthread_mutex_lock(&m); + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_lock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a correct code, and tsan must not bark. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutex_robust2.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutex_robust2.cc new file mode 100644 index 00000000..5bd7ff68 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutex_robust2.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +pthread_mutex_t m; +int x; + +void *thr(void *p) { + pthread_mutex_lock(&m); + x = 42; + return 0; +} + +int main() { + pthread_mutexattr_t a; + pthread_mutexattr_init(&a); + pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&m, &a); + pthread_t th; + pthread_create(&th, 0, thr, 0); + sleep(1); + if (pthread_mutex_trylock(&m) != EOWNERDEAD) { + fprintf(stderr, "not EOWNERDEAD\n"); + exit(1); + } + x = 43; + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// This is a false positive, tsan must not bark at the data race. +// But currently it does. +// CHECK-NOT: WARNING: ThreadSanitizer WARNING: double lock of mutex +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: EOWNERDEAD +// CHECK: DONE +// CHECK-NOT: WARNING: ThreadSanitizer + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset1.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset1.cc new file mode 100644 index 00000000..ca87a7ba --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset1.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx); + Global++; + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset1.cc:[[@LINE+1]] + pthread_mutex_init(&mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset2.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset2.cc new file mode 100644 index 00000000..9ccf952b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset2.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +pthread_mutex_t mtx; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx); + Global++; + pthread_mutex_unlock(&mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T2: + // CHECK: Previous write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset2.cc:[[@LINE+1]] + pthread_mutex_init(&mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset3.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset3.cc new file mode 100644 index 00000000..272ddafb --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset3.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + pthread_mutex_lock(&mtx2); + Global++; + pthread_mutex_unlock(&mtx2); + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+2]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset4.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset4.cc new file mode 100644 index 00000000..be751fa9 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset4.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + pthread_mutex_lock(&mtx1); + pthread_mutex_lock(&mtx2); + Global++; + pthread_mutex_unlock(&mtx2); + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T2: + // CHECK: Previous write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+2]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset5.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset5.cc new file mode 100644 index 00000000..e013edb4 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset5.cc @@ -0,0 +1,46 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +pthread_mutex_t mtx1; +pthread_mutex_t mtx2; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + Global++; + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_lock(&mtx2); + Global--; + pthread_mutex_unlock(&mtx2); + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2 + // CHECK: (mutexes: write [[M2:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+4]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+5]] + pthread_mutex_init(&mtx1, 0); + pthread_mutex_init(&mtx2, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_mutex_destroy(&mtx2); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset6.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset6.cc new file mode 100644 index 00000000..f5e6e66b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset6.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +pthread_mutex_t mtx1; +pthread_spinlock_t mtx2; +pthread_rwlock_t mtx3; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(&mtx1); + Global++; + pthread_mutex_unlock(&mtx1); + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_lock(&mtx1); + pthread_mutex_unlock(&mtx1); + pthread_spin_lock(&mtx2); + pthread_rwlock_rdlock(&mtx3); + Global--; + pthread_spin_unlock(&mtx2); + pthread_rwlock_unlock(&mtx3); + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2 + // CHECK: (mutexes: write [[M2:M[0-9]+]], read [[M3:M[0-9]+]]): + // CHECK: Mutex [[M1]] created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+5]] + // CHECK: Mutex [[M2]] created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+4]] + // CHECK: Mutex [[M3]] created at: + // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+3]] + pthread_mutex_init(&mtx1, 0); + pthread_spin_init(&mtx2, 0); + pthread_rwlock_init(&mtx3, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&mtx1); + pthread_spin_destroy(&mtx2); + pthread_rwlock_destroy(&mtx3); +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset7.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset7.cc new file mode 100644 index 00000000..51451b21 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset7.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +__thread int huge[1024*1024]; + +void *Thread1(void *x) { + sleep(1); + Global++; + return NULL; +} + +void *Thread2(void *x) { + pthread_mutex_t mtx; + pthread_mutex_init(&mtx, 0); + pthread_mutex_lock(&mtx); + Global--; + pthread_mutex_unlock(&mtx); + pthread_mutex_destroy(&mtx); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T1: +// CHECK: Previous write of size 4 at {{.*}} by thread T2 +// CHECK: (mutexes: write [[M1:M[0-9]+]]): +// CHECK: Mutex [[M1]] is already destroyed +// CHECK-NOT: Mutex {{.*}} created at + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/mutexset8.cc b/projects/compiler-rt/lib/tsan/lit_tests/mutexset8.cc new file mode 100644 index 00000000..8822b050 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/mutexset8.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; +pthread_mutex_t *mtx; + +void *Thread1(void *x) { + sleep(1); + pthread_mutex_lock(mtx); + Global++; + pthread_mutex_unlock(mtx); + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + // CHECK: WARNING: ThreadSanitizer: data race + // CHECK: Write of size 4 at {{.*}} by thread T1 + // CHECK: (mutexes: write [[M1:M[0-9]+]]): + // CHECK: Previous write of size 4 at {{.*}} by thread T2: + // CHECK: Mutex [[M1]] created at: + // CHECK: #0 pthread_mutex_init + // CHECK: #1 main {{.*}}/mutexset8.cc + mtx = new pthread_mutex_t; + pthread_mutex_init(mtx, 0); + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(mtx); + delete mtx; +} diff --git a/projects/compiler-rt/lib/tsan/lit_tests/oob_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/oob_race.cc new file mode 100644 index 00000000..9d8e2220 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/oob_race.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +const long kOffset = 64*1024; + +void *Thread(void *p) { + ((char*)p)[-kOffset] = 43; + return 0; +} + +int main() { + char *volatile p0 = new char[16]; + delete[] p0; + char *p = new char[32]; + pthread_t th; + pthread_create(&th, 0, Thread, p); + p[-kOffset] = 42; + pthread_join(th, 0); +} + +// Used to crash with CHECK failed. +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_on_barrier.c b/projects/compiler-rt/lib/tsan/lit_tests/race_on_barrier.c new file mode 100644 index 00000000..3c0199de --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_on_barrier.c @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + pthread_barrier_init(&B, 0, 2); + pthread_barrier_wait(&B); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_barrier_wait(&B); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + pthread_barrier_destroy(&B); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_on_barrier2.c b/projects/compiler-rt/lib/tsan/lit_tests/race_on_barrier2.c new file mode 100644 index 00000000..62773d43 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_on_barrier2.c @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +pthread_barrier_t B; +int Global; + +void *Thread1(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +void *Thread2(void *x) { + if (pthread_barrier_wait(&B) == PTHREAD_BARRIER_SERIAL_THREAD) + pthread_barrier_destroy(&B); + return NULL; +} + +int main() { + pthread_barrier_init(&B, 0, 2); + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + Thread2(0); + pthread_join(t, NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_on_heap.cc b/projects/compiler-rt/lib/tsan/lit_tests/race_on_heap.cc new file mode 100644 index 00000000..a84c0de9 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_on_heap.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +void *Thread1(void *p) { + *(int*)p = 42; + return 0; +} + +void *Thread2(void *p) { + *(int*)p = 44; + return 0; +} + +void *alloc() { + return malloc(99); +} + +void *AllocThread(void* arg) { + return alloc(); +} + +int main() { + void *p = 0; + pthread_t t[2]; + pthread_create(&t[0], 0, AllocThread, 0); + pthread_join(t[0], &p); + fprintf(stderr, "addr=%p\n", p); + pthread_create(&t[0], 0, Thread1, (char*)p + 16); + pthread_create(&t[1], 0, Thread2, (char*)p + 16); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + return 0; +} + +// CHECK: addr=[[ADDR:0x[0-9,a-f]+]] +// CHECK: WARNING: ThreadSanitizer: data race +// ... +// CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread T1: +// CHCEKL #0 malloc +// CHECK: #{{1|2}} alloc +// CHECK: #{{2|3}} AllocThread +// ... +// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at: +// CHECK: #0 pthread_create +// CHECK: #1 main diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_on_mutex.c b/projects/compiler-rt/lib/tsan/lit_tests/race_on_mutex.c new file mode 100644 index 00000000..e6634141 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_on_mutex.c @@ -0,0 +1,42 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +pthread_mutex_t Mtx; +int Global; + +void *Thread1(void *x) { + pthread_mutex_init(&Mtx, 0); + pthread_mutex_lock(&Mtx); + Global = 42; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + pthread_mutex_lock(&Mtx); + Global = 43; + pthread_mutex_unlock(&Mtx); + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + pthread_mutex_destroy(&Mtx); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Atomic read of size 1 at {{.*}} by thread T2: +// CHECK-NEXT: #0 pthread_mutex_lock +// CHECK-NEXT: #1 Thread2{{.*}} {{.*}}race_on_mutex.c:20{{(:3)?}} ({{.*}}) +// CHECK: Previous write of size 1 at {{.*}} by thread T1: +// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}}) +// CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}}) diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_on_mutex2.c b/projects/compiler-rt/lib/tsan/lit_tests/race_on_mutex2.c new file mode 100644 index 00000000..80c395e1 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_on_mutex2.c @@ -0,0 +1,24 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +void *Thread(void *x) { + pthread_mutex_lock((pthread_mutex_t*)x); + pthread_mutex_unlock((pthread_mutex_t*)x); + return 0; +} + +int main() { + pthread_mutex_t Mtx; + pthread_mutex_init(&Mtx, 0); + pthread_t t; + pthread_create(&t, 0, Thread, &Mtx); + sleep(1); + pthread_mutex_destroy(&Mtx); + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_on_read.cc b/projects/compiler-rt/lib/tsan/lit_tests/race_on_read.cc new file mode 100644 index 00000000..4ca4b25b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_on_read.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include + +int fd; +char buf; + +void *Thread(void *x) { + read(fd, &buf, 1); + return NULL; +} + +int main() { + fd = open("/dev/random", O_RDONLY); + if (fd < 0) return 1; + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread, NULL); + pthread_create(&t[1], NULL, Thread, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + close(fd); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 1 +// CHECK: #0 read +// CHECK: Previous write of size 1 +// CHECK: #0 read diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_on_write.cc b/projects/compiler-rt/lib/tsan/lit_tests/race_on_write.cc new file mode 100644 index 00000000..8a56c846 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_on_write.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include + +int fd; +char buf; + +void *Thread1(void *x) { + buf = 1; + sleep(1); + return NULL; +} + +void *Thread2(void *x) { + write(fd, &buf, 1); + return NULL; +} + +int main() { + fd = open("/dev/null", O_WRONLY); + if (fd < 0) return 1; + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + sleep(1); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + close(fd); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Read of size 1 +// CHECK: #0 write +// CHECK: Previous write of size 1 +// CHECK: #0 Thread1 diff --git a/projects/compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc b/projects/compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc new file mode 100644 index 00000000..c713c67a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/race_with_finished_thread.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +// Ensure that we can restore a stack of a finished thread. + +int g_data; + +void __attribute__((noinline)) foobar(int *p) { + *p = 42; +} + +void *Thread1(void *x) { + foobar(&g_data); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + g_data = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T2: +// CHECK: Previous write of size 4 at {{.*}} by thread T1: +// CHECK: #0 foobar +// CHECK: #1 Thread1 +// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at: +// CHECK: #0 pthread_create +// CHECK: #1 main diff --git a/projects/compiler-rt/lib/tsan/lit_tests/signal_errno.cc b/projects/compiler-rt/lib/tsan/lit_tests/signal_errno.cc new file mode 100644 index 00000000..2febca38 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/signal_errno.cc @@ -0,0 +1,43 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include +#include +#include + +pthread_t mainth; +volatile int done; + +static void MyHandler(int, siginfo_t *s, void *c) { + errno = 1; + done = 1; +} + +static void* sendsignal(void *p) { + pthread_kill(mainth, SIGPROF); + return 0; +} + +int main() { + mainth = pthread_self(); + struct sigaction act = {}; + act.sa_sigaction = &MyHandler; + sigaction(SIGPROF, &act, 0); + pthread_t th; + pthread_create(&th, 0, sendsignal, 0); + while (done == 0) { + volatile char *p = (char*)malloc(1); + p[0] = 0; + free((void*)p); + pthread_yield(); + } + pthread_join(th, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: signal handler spoils errno +// CHECK: #0 MyHandler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_errno.cc +// CHECK: SUMMARY: ThreadSanitizer: signal handler spoils errno{{.*}}MyHandler + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/signal_malloc.cc b/projects/compiler-rt/lib/tsan/lit_tests/signal_malloc.cc new file mode 100644 index 00000000..ef180b8a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/signal_malloc.cc @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +static void handler(int, siginfo_t*, void*) { + // CHECK: WARNING: ThreadSanitizer: signal-unsafe call inside of a signal + // CHECK: #0 malloc + // CHECK: #{{(1|2)}} handler(int, siginfo{{(_t)?}}*, void*) {{.*}}signal_malloc.cc:[[@LINE+2]] + // CHECK: SUMMARY: ThreadSanitizer: signal-unsafe call inside of a signal{{.*}}handler + volatile char *p = (char*)malloc(1); + p[0] = 0; + free((void*)p); +} + +int main() { + struct sigaction act = {}; + act.sa_sigaction = &handler; + sigaction(SIGPROF, &act, 0); + kill(getpid(), SIGPROF); + sleep(1); + return 0; +} + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/sigsuspend.cc b/projects/compiler-rt/lib/tsan/lit_tests/sigsuspend.cc new file mode 100644 index 00000000..78d507fa --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/sigsuspend.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +static bool signal_handler_ran = false; + +void do_nothing_signal_handler(int signum) { + write(1, "HANDLER\n", 8); + signal_handler_ran = true; +} + +int main() { + const int kSignalToTest = SIGSYS; + assert(SIG_ERR != signal(kSignalToTest, do_nothing_signal_handler)); + sigset_t empty_set; + assert(0 == sigemptyset(&empty_set)); + sigset_t one_signal = empty_set; + assert(0 == sigaddset(&one_signal, kSignalToTest)); + sigset_t old_set; + assert(0 == sigprocmask(SIG_BLOCK, &one_signal, &old_set)); + raise(kSignalToTest); + assert(!signal_handler_ran); + sigset_t all_but_one; + assert(0 == sigfillset(&all_but_one)); + assert(0 == sigdelset(&all_but_one, kSignalToTest)); + sigsuspend(&all_but_one); + assert(signal_handler_ran); + + // Restore the original set. + assert(0 == sigprocmask(SIG_SETMASK, &old_set, NULL)); + printf("DONE"); +} + +// CHECK: HANDLER +// CHECK: DONE diff --git a/projects/compiler-rt/lib/tsan/lit_tests/simple_race.c b/projects/compiler-rt/lib/tsan/lit_tests/simple_race.c new file mode 100644 index 00000000..80a83e01 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/simple_race.c @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/simple_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/simple_race.cc new file mode 100644 index 00000000..47854cfd --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/simple_race.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +int Global; + +void *Thread1(void *x) { + Global++; + return NULL; +} + +void *Thread2(void *x) { + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: SUMMARY: ThreadSanitizer: data race{{.*}}Thread diff --git a/projects/compiler-rt/lib/tsan/lit_tests/simple_stack.c b/projects/compiler-rt/lib/tsan/lit_tests/simple_stack.c new file mode 100644 index 00000000..a447e288 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/simple_stack.c @@ -0,0 +1,66 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; (void)tmp; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int v = Global; (void)v; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; (void)tmp; + foo2(); +} + +void *Thread1(void *x) { + sleep(1); + bar1(); + return NULL; +} + +void *Thread2(void *x) { + bar2(); + return NULL; +} + +void StartThread(pthread_t *t, void *(*f)(void*)) { + pthread_create(t, NULL, f, NULL); +} + +int main() { + pthread_t t[2]; + StartThread(&t[0], Thread1); + StartThread(&t[1], Thread2); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1: +// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:9{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}}) +// CHECK: Previous read of size 4 at {{.*}} by thread T2: +// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:26)?}} ({{.*}}) +// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}}) +// CHECK: Thread T1 (tid={{.*}}, running) created by main thread at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}}) +// CHECK: Thread T2 ({{.*}}) created by main thread at: +// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}}) +// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}}) diff --git a/projects/compiler-rt/lib/tsan/lit_tests/simple_stack2.cc b/projects/compiler-rt/lib/tsan/lit_tests/simple_stack2.cc new file mode 100644 index 00000000..7a034c4c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/simple_stack2.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; + +void __attribute__((noinline)) foo1() { + Global = 42; +} + +void __attribute__((noinline)) bar1() { + volatile int tmp = 42; + int tmp2 = tmp; + (void)tmp2; + foo1(); +} + +void __attribute__((noinline)) foo2() { + volatile int tmp = Global; + int tmp2 = tmp; + (void)tmp2; +} + +void __attribute__((noinline)) bar2() { + volatile int tmp = 42; + int tmp2 = tmp; + (void)tmp2; + foo2(); +} + +void *Thread1(void *x) { + sleep(1); + bar1(); + return NULL; +} + +int main() { + pthread_t t; + pthread_create(&t, NULL, Thread1, NULL); + bar2(); + pthread_join(t, NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1: +// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}}) +// CHECK: Previous read of size 4 at {{.*}} by main thread: +// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:28)?}} ({{.*}}) +// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} ({{.*}}) +// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} ({{.*}}) diff --git a/projects/compiler-rt/lib/tsan/lit_tests/sleep_sync.cc b/projects/compiler-rt/lib/tsan/lit_tests/sleep_sync.cc new file mode 100644 index 00000000..217a52a0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/sleep_sync.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +int X = 0; + +void MySleep() { + sleep(1); +} + +void *Thread(void *p) { + MySleep(); // Assume the main thread has done the write. + X = 42; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// ... +// CHECK: As if synchronized via sleep: +// CHECK-NEXT: #0 sleep +// CHECK-NEXT: #1 MySleep +// CHECK-NEXT: #2 Thread diff --git a/projects/compiler-rt/lib/tsan/lit_tests/sleep_sync2.cc b/projects/compiler-rt/lib/tsan/lit_tests/sleep_sync2.cc new file mode 100644 index 00000000..e2299927 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/sleep_sync2.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +int X = 0; + +void *Thread(void *p) { + X = 42; + return 0; +} + +int main() { + pthread_t t; + sleep(1); + pthread_create(&t, 0, Thread, 0); + X = 43; + pthread_join(t, 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK-NOT: As if synchronized via sleep diff --git a/projects/compiler-rt/lib/tsan/lit_tests/stack_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/stack_race.cc new file mode 100644 index 00000000..7fabce22 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/stack_race.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *a) { + *(int*)a = 43; + return 0; +} + +int main() { + int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread, &Var); + Var = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is stack of main thread. + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/stack_race2.cc b/projects/compiler-rt/lib/tsan/lit_tests/stack_race2.cc new file mode 100644 index 00000000..c759ec92 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/stack_race2.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +void *Thread2(void *a) { + *(int*)a = 43; + return 0; +} + +void *Thread(void *a) { + int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread2, &Var); + Var = 42; + pthread_join(t, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is stack of thread T1. + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/static_init1.cc b/projects/compiler-rt/lib/tsan/lit_tests/static_init1.cc new file mode 100644 index 00000000..4faf5bc5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/static_init1.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +struct P { + int x; + int y; +}; + +void *Thread(void *x) { + static P p = {rand(), rand()}; + if (p.x > RAND_MAX || p.y > RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/static_init2.cc b/projects/compiler-rt/lib/tsan/lit_tests/static_init2.cc new file mode 100644 index 00000000..96ef821a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/static_init2.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void foo(Cache *my) { + static Cache *c = my ? my : new Cache(rand()); + if (c->x >= RAND_MAX) + exit(1); +} + +void *Thread(void *x) { + foo(new Cache(rand())); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread, 0); + pthread_create(&t[1], 0, Thread, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/static_init3.cc b/projects/compiler-rt/lib/tsan/lit_tests/static_init3.cc new file mode 100644 index 00000000..70a3c168 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/static_init3.cc @@ -0,0 +1,47 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +struct Cache { + int x; +}; + +Cache g_cache; + +Cache *CreateCache() { + g_cache.x = rand(); + return &g_cache; +} + +_Atomic(Cache*) queue; + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + __c11_atomic_store(&queue, c, 0); + return 0; +} + +void *Thread2(void *x) { + Cache *c = 0; + for (;;) { + c = __c11_atomic_load(&queue, 0); + if (c) + break; + sched_yield(); + } + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/static_init4.cc b/projects/compiler-rt/lib/tsan/lit_tests/static_init4.cc new file mode 100644 index 00000000..5ecc3992 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/static_init4.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +int g_other; + +Cache *CreateCache() { + g_other = rand(); + return new Cache(rand()); +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x == g_other) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/static_init5.cc b/projects/compiler-rt/lib/tsan/lit_tests/static_init5.cc new file mode 100644 index 00000000..1d0ed6d5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/static_init5.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void *AsyncInit(void *p) { + return new Cache((int)(long)p); +} + +Cache *CreateCache() { + pthread_t t; + pthread_create(&t, 0, AsyncInit, (void*)(long)rand()); + void *res; + pthread_join(t, &res); + return (Cache*)res; +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/static_init6.cc b/projects/compiler-rt/lib/tsan/lit_tests/static_init6.cc new file mode 100644 index 00000000..c9099f9b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/static_init6.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_tsan -static-libstdc++ -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +struct Cache { + int x; + explicit Cache(int x) + : x(x) { + } +}; + +void *AsyncInit(void *p) { + return new Cache((int)(long)p); +} + +Cache *CreateCache() { + pthread_t t; + pthread_create(&t, 0, AsyncInit, (void*)(long)rand()); + void *res; + pthread_join(t, &res); + return (Cache*)res; +} + +void *Thread1(void *x) { + static Cache *c = CreateCache(); + if (c->x >= RAND_MAX) + exit(1); + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread1, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppress_same_address.cc b/projects/compiler-rt/lib/tsan/lit_tests/suppress_same_address.cc new file mode 100644 index 00000000..c516f895 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppress_same_address.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include + +int X; + +void *Thread1(void *x) { + X = 42; + X = 66; + X = 78; + return 0; +} + +void *Thread2(void *x) { + X = 11; + X = 99; + X = 73; + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread2(0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppress_same_stacks.cc b/projects/compiler-rt/lib/tsan/lit_tests/suppress_same_stacks.cc new file mode 100644 index 00000000..f0ab8b30 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppress_same_stacks.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include + +volatile int N; // Prevent loop unrolling. +int **data; + +void *Thread1(void *x) { + for (int i = 0; i < N; i++) + data[i][0] = 42; + return 0; +} + +int main() { + N = 4; + data = new int*[N]; + for (int i = 0; i < N; i++) + data[i] = new int; + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Thread1(0); + pthread_join(t, 0); + for (int i = 0; i < N; i++) + delete data[i]; + delete[] data; +} + +// CHECK: ThreadSanitizer: reported 1 warnings diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppressions_global.cc b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_global.cc new file mode 100644 index 00000000..181cb56c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_global.cc @@ -0,0 +1,29 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include +#include + +int RacyGlobal; + +void *Thread1(void *x) { + RacyGlobal = 42; + return NULL; +} + +void *Thread2(void *x) { + RacyGlobal = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppressions_global.cc.supp b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_global.cc.supp new file mode 100644 index 00000000..5fa8a2e4 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_global.cc.supp @@ -0,0 +1,2 @@ +race:RacyGlobal + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race.cc new file mode 100644 index 00000000..c88e69be --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race.cc.supp b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race.cc.supp new file mode 100644 index 00000000..cbdba76e --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race.cc.supp @@ -0,0 +1,2 @@ +race:Thread1 + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race2.cc b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race2.cc new file mode 100644 index 00000000..57146f96 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race2.cc @@ -0,0 +1,31 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions=%s.supp" %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; + +void *Thread1(void *x) { + Global = 42; + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + Global = 43; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK-NOT: WARNING: ThreadSanitizer: data race + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race2.cc.supp b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race2.cc.supp new file mode 100644 index 00000000..b3c4dbc5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/suppressions_race2.cc.supp @@ -0,0 +1,2 @@ +race:Thread2 + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/test_output.sh b/projects/compiler-rt/lib/tsan/lit_tests/test_output.sh new file mode 100755 index 00000000..79e773aa --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/test_output.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +ulimit -s 8192 +set -e # fail on any error + +ROOTDIR=$(dirname $0)/.. +BLACKLIST=$ROOTDIR/lit_tests/Helpers/blacklist.txt + +# Assume clang and clang++ are in path. +: ${CC:=clang} +: ${CXX:=clang++} +: ${FILECHECK:=FileCheck} + +# TODO: add testing for all of -O0...-O3 +CFLAGS="-fsanitize=thread -fsanitize-blacklist=$BLACKLIST -fPIE -O1 -g -Wall" +LDFLAGS="-pie -lpthread -ldl -lrt -Wl,--whole-archive $ROOTDIR/rtl/libtsan.a -Wl,--no-whole-archive" + +test_file() { + SRC=$1 + COMPILER=$2 + echo ----- TESTING $(basename $1) + OBJ=$SRC.o + EXE=$SRC.exe + $COMPILER $SRC $CFLAGS -c -o $OBJ + $COMPILER $OBJ $LDFLAGS -o $EXE + RES=$($EXE 2>&1 || true) + printf "%s\n" "$RES" | $FILECHECK $SRC + if [ "$3" == "" ]; then + rm -f $EXE $OBJ + fi +} + +if [ "$1" == "" ]; then + for c in $ROOTDIR/lit_tests/*.{c,cc}; do + if [[ $c == */failing_* ]]; then + echo SKIPPING FAILING TEST $c + continue + fi + if [[ $c == */load_shared_lib.cc ]]; then + echo TEST $c is not supported + continue + fi + if [ "`grep "TSAN_OPTIONS" $c`" ]; then + echo SKIPPING $c -- requires TSAN_OPTIONS + continue + fi + COMPILER=$CXX + case $c in + *.c) COMPILER=$CC + esac + test_file $c $COMPILER & + done + for job in `jobs -p`; do + wait $job || exit 1 + done +else + test_file $ROOTDIR/lit_tests/$1 $CXX "DUMP" +fi diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_end_with_ignore.cc b/projects/compiler-rt/lib/tsan/lit_tests/thread_end_with_ignore.cc new file mode 100644 index 00000000..960a477c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_end_with_ignore.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include + +extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l); + +void *Thread(void *x) { + AnnotateIgnoreReadsBegin("", 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: ThreadSanitizer: thread T1 finished with ignores enabled + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_end_with_ignore2.cc b/projects/compiler-rt/lib/tsan/lit_tests/thread_end_with_ignore2.cc new file mode 100644 index 00000000..8f743ae2 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_end_with_ignore2.cc @@ -0,0 +1,9 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l); + +int main() { + AnnotateIgnoreWritesBegin("", 0); +} + +// CHECK: ThreadSanitizer: thread T0 finished with ignores enabled + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_leak.c b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak.c new file mode 100644 index 00000000..c5e669e5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_leak2.c b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak2.c new file mode 100644 index 00000000..39f6b5e0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak2.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_detach(t); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_leak3.c b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak3.c new file mode 100644 index 00000000..5f447dbd --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak3.c @@ -0,0 +1,17 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *x) { + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + sleep(1); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: thread leak +// CHECK: SUMMARY: ThreadSanitizer: thread leak{{.*}}main diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_leak4.c b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak4.c new file mode 100644 index 00000000..f9fad036 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak4.c @@ -0,0 +1,18 @@ +// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +void *Thread(void *x) { + sleep(10); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + printf("OK\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: thread leak diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_leak5.c b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak5.c new file mode 100644 index 00000000..329f7233 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_leak5.c @@ -0,0 +1,19 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *x) { + return 0; +} + +int main() { + for (int i = 0; i < 5; i++) { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + } + sleep(1); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: thread leak +// CHECK: And 4 more similar thread leaks diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_name.cc b/projects/compiler-rt/lib/tsan/lit_tests/thread_name.cc new file mode 100644 index 00000000..646ab583 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_name.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +extern "C" void AnnotateThreadName(const char *f, int l, const char *name); + +int Global; + +void *Thread1(void *x) { + sleep(1); + AnnotateThreadName(__FILE__, __LINE__, "Thread1"); + Global++; + return NULL; +} + +void *Thread2(void *x) { +#if SANITIZER_LINUX && __GLIBC_PREREQ(2, 12) + pthread_setname_np(pthread_self(), "Thread2"); +#else + AnnotateThreadName(__FILE__, __LINE__, "Thread2"); +#endif + Global--; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'Thread1' +// CHECK: Thread T2 'Thread2' + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/thread_name2.cc b/projects/compiler-rt/lib/tsan/lit_tests/thread_name2.cc new file mode 100644 index 00000000..8c5cb741 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/thread_name2.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global++; + return 0; +} + +void *Thread2(void *x) { + pthread_setname_np(pthread_self(), "foobar2"); + Global--; + return 0; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_setname_np(t[0], "foobar1"); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Thread T1 'foobar1' +// CHECK: Thread T2 'foobar2' + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/tiny_race.c b/projects/compiler-rt/lib/tsan/lit_tests/tiny_race.c new file mode 100644 index 00000000..f77e1606 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/tiny_race.c @@ -0,0 +1,21 @@ +// RUN: %clang_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +int Global; + +void *Thread1(void *x) { + sleep(1); + Global = 42; + return x; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + Global = 43; + pthread_join(t, 0); + return Global; +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/tls_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/tls_race.cc new file mode 100644 index 00000000..3cbcc9db --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/tls_race.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +void *Thread(void *a) { + *(int*)a = 43; + return 0; +} + +int main() { + static __thread int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread, &Var); + Var = 43; + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is TLS of main thread. diff --git a/projects/compiler-rt/lib/tsan/lit_tests/tls_race2.cc b/projects/compiler-rt/lib/tsan/lit_tests/tls_race2.cc new file mode 100644 index 00000000..13608706 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/tls_race2.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include + +void *Thread2(void *a) { + *(int*)a = 43; + return 0; +} + +void *Thread(void *a) { + static __thread int Var = 42; + pthread_t t; + pthread_create(&t, 0, Thread2, &Var); + Var = 42; + pthread_join(t, 0); + return 0; +} + +int main() { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is TLS of thread T1. + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/tsan-vs-gvn.cc b/projects/compiler-rt/lib/tsan/lit_tests/tsan-vs-gvn.cc new file mode 100644 index 00000000..40ae724b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/tsan-vs-gvn.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O3 %s -o %t && %t 2>&1 | FileCheck %s +// +// Check that load widening is not tsan-hostile. +#include +#include +#include + +struct { + int i; + char c1, c2, c3, c4; +} S; + +int G; + +void *Thread1(void *x) { + G = S.c1 + S.c3; + return NULL; +} + +void *Thread2(void *x) { + S.c2 = 1; + return NULL; +} + +int main() { + pthread_t t[2]; + memset(&S, 123, sizeof(S)); + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + printf("PASS\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: PASS diff --git a/projects/compiler-rt/lib/tsan/lit_tests/unaligned_norace.cc b/projects/compiler-rt/lib/tsan/lit_tests/unaligned_norace.cc new file mode 100644 index 00000000..792224b8 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/unaligned_norace.cc @@ -0,0 +1,84 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +uint64_t objs[8*3*3*2][3]; + +extern "C" { +uint16_t __tsan_unaligned_read2(void *addr); +uint32_t __tsan_unaligned_read4(void *addr); +uint64_t __tsan_unaligned_read8(void *addr); +void __tsan_unaligned_write2(void *addr, uint16_t v); +void __tsan_unaligned_write4(void *addr, uint32_t v); +void __tsan_unaligned_write8(void *addr, uint64_t v); +} + +static void access(char *p, int sz, int rw) { + if (rw) { + switch (sz) { + case 0: __tsan_unaligned_write2(p, 0); break; + case 1: __tsan_unaligned_write4(p, 0); break; + case 2: __tsan_unaligned_write8(p, 0); break; + default: exit(1); + } + } else { + switch (sz) { + case 0: __tsan_unaligned_read2(p); break; + case 1: __tsan_unaligned_read4(p); break; + case 2: __tsan_unaligned_read8(p); break; + default: exit(1); + } + } +} + +static int accesssize(int sz) { + switch (sz) { + case 0: return 2; + case 1: return 4; + case 2: return 8; + } + exit(1); +} + +void Test(bool main) { + uint64_t *obj = objs[0]; + for (int off = 0; off < 8; off++) { + for (int sz1 = 0; sz1 < 3; sz1++) { + for (int sz2 = 0; sz2 < 3; sz2++) { + for (int rw = 0; rw < 2; rw++) { + char *p = (char*)obj + off; + if (main) { + // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n", + // main, off, sz1, sz2, rw, p); + access(p, sz1, true); + } else { + p += accesssize(sz1); + // printf("thr=%d off=%d sz1=%d sz2=%d rw=%d p=%p\n", + // main, off, sz1, sz2, rw, p); + access(p, sz2, rw); + } + obj += 3; + } + } + } + } +} + +void *Thread(void *p) { + (void)p; + Test(false); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, Thread, 0); + Test(true); + pthread_join(th, 0); + printf("OK\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: OK diff --git a/projects/compiler-rt/lib/tsan/lit_tests/unaligned_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/unaligned_race.cc new file mode 100644 index 00000000..6ac87b57 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/unaligned_race.cc @@ -0,0 +1,135 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include +#include + +uint64_t objs[8*2*(2 + 4 + 8)][2]; + +extern "C" { +uint16_t __sanitizer_unaligned_load16(void *addr); +uint32_t __sanitizer_unaligned_load32(void *addr); +uint64_t __sanitizer_unaligned_load64(void *addr); +void __sanitizer_unaligned_store16(void *addr, uint16_t v); +void __sanitizer_unaligned_store32(void *addr, uint32_t v); +void __sanitizer_unaligned_store64(void *addr, uint64_t v); +} + +// All this mess is to generate unique stack for each race, +// otherwise tsan will suppress similar stacks. + +static void access(char *p, int sz, int rw) { + if (rw) { + switch (sz) { + case 0: __sanitizer_unaligned_store16(p, 0); break; + case 1: __sanitizer_unaligned_store32(p, 0); break; + case 2: __sanitizer_unaligned_store64(p, 0); break; + default: exit(1); + } + } else { + switch (sz) { + case 0: __sanitizer_unaligned_load16(p); break; + case 1: __sanitizer_unaligned_load32(p); break; + case 2: __sanitizer_unaligned_load64(p); break; + default: exit(1); + } + } +} + +static int accesssize(int sz) { + switch (sz) { + case 0: return 2; + case 1: return 4; + case 2: return 8; + } + exit(1); +} + +template +static void access3(bool main, int sz1, bool rw, char *p) { + p += off; + if (main) { + access(p, sz1, true); + } else { + p += off2; + if (rw) { + *p = 42; + } else { + if (*p == 42) + printf("bingo!\n"); + } + } +} + +template +static void access2(bool main, int sz1, int off2, bool rw, char *obj) { + if (off2 == 0) + access3(main, sz1, rw, obj); + else if (off2 == 1) + access3(main, sz1, rw, obj); + else if (off2 == 2) + access3(main, sz1, rw, obj); + else if (off2 == 3) + access3(main, sz1, rw, obj); + else if (off2 == 4) + access3(main, sz1, rw, obj); + else if (off2 == 5) + access3(main, sz1, rw, obj); + else if (off2 == 6) + access3(main, sz1, rw, obj); + else if (off2 == 7) + access3(main, sz1, rw, obj); +} + +static void access1(bool main, int off, int sz1, int off2, bool rw, char *obj) { + if (off == 0) + access2<0>(main, sz1, off2, rw, obj); + else if (off == 1) + access2<1>(main, sz1, off2, rw, obj); + else if (off == 2) + access2<2>(main, sz1, off2, rw, obj); + else if (off == 3) + access2<3>(main, sz1, off2, rw, obj); + else if (off == 4) + access2<4>(main, sz1, off2, rw, obj); + else if (off == 5) + access2<5>(main, sz1, off2, rw, obj); + else if (off == 6) + access2<6>(main, sz1, off2, rw, obj); + else if (off == 7) + access2<7>(main, sz1, off2, rw, obj); +} + +void Test(bool main) { + uint64_t *obj = objs[0]; + for (int off = 0; off < 8; off++) { + for (int sz1 = 0; sz1 < 3; sz1++) { + for (int off2 = 0; off2 < accesssize(sz1); off2++) { + for (int rw = 0; rw < 2; rw++) { + // printf("thr=%d off=%d sz1=%d off2=%d rw=%d p=%p\n", + // main, off, sz1, off2, rw, obj); + access1(main, off, sz1, off2, rw, (char*)obj); + obj += 2; + } + } + } + } +} + +void *Thread(void *p) { + (void)p; + sleep(1); + Test(false); + return 0; +} + +int main() { + pthread_t th; + pthread_create(&th, 0, Thread, 0); + Test(true); + pthread_join(th, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: ThreadSanitizer: reported 224 warnings diff --git a/projects/compiler-rt/lib/tsan/lit_tests/user_fopen.cc b/projects/compiler-rt/lib/tsan/lit_tests/user_fopen.cc new file mode 100644 index 00000000..794d5987 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/user_fopen.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include + +// defined by tsan. +extern "C" FILE *__interceptor_fopen(const char *file, const char *mode); +extern "C" int __interceptor_fileno(FILE *f); + +extern "C" FILE *fopen(const char *file, const char *mode) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user fopen\n"); + return __interceptor_fopen(file, mode); +} + +extern "C" int fileno(FILE *f) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user fileno\n"); + return 1; +} + +int main() { + FILE *f = fopen("/dev/zero", "r"); + if (f) { + char buf; + fread(&buf, 1, 1, f); + fclose(f); + } +} + +// CHECK: user fopen +// CHECK-NOT: ThreadSanitizer + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/user_malloc.cc b/projects/compiler-rt/lib/tsan/lit_tests/user_malloc.cc new file mode 100644 index 00000000..0be6d54f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/user_malloc.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include + +// defined by tsan. +extern "C" void *__interceptor_malloc(unsigned long size); +extern "C" void __interceptor_free(void *p); + +extern "C" void *malloc(unsigned long size) { + static int first = 0; + if (__sync_lock_test_and_set(&first, 1) == 0) + printf("user malloc\n"); + return __interceptor_malloc(size); +} + +extern "C" void free(void *p) { + __interceptor_free(p); +} + +int main() { + volatile char *p = (char*)malloc(10); + p[0] = 0; + free((void*)p); +} + +// CHECK: user malloc +// CHECK-NOT: ThreadSanitizer + diff --git a/projects/compiler-rt/lib/tsan/lit_tests/virtual_inheritance_compile_bug.cc b/projects/compiler-rt/lib/tsan/lit_tests/virtual_inheritance_compile_bug.cc new file mode 100644 index 00000000..2275b8b8 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/virtual_inheritance_compile_bug.cc @@ -0,0 +1,15 @@ +// Regression test for http://code.google.com/p/thread-sanitizer/issues/detail?id=3. +// The C++ variant is much more compact that the LLVM IR equivalent. + +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +struct AAA { virtual long aaa () { return 0; } }; // NOLINT +struct BBB: virtual AAA { unsigned long bbb; }; // NOLINT +struct CCC: virtual AAA { }; +struct DDD: CCC, BBB { DDD(); }; // NOLINT +DDD::DDD() { } +int main() { + DDD d; + printf("OK\n"); +} +// CHECK: OK diff --git a/projects/compiler-rt/lib/tsan/lit_tests/vptr_benign_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/vptr_benign_race.cc new file mode 100644 index 00000000..8c9fc596 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/vptr_benign_race.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s +#include +#include +#include + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { + sem_wait(&sem_); + sem_destroy(&sem_); + } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); + fprintf(stderr, "PASS\n"); +} +// CHECK: PASS +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/projects/compiler-rt/lib/tsan/lit_tests/vptr_harmful_race.cc b/projects/compiler-rt/lib/tsan/lit_tests/vptr_harmful_race.cc new file mode 100644 index 00000000..0105c4ce --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/vptr_harmful_race.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + sleep(1); + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race on vptr diff --git a/projects/compiler-rt/lib/tsan/lit_tests/vptr_harmful_race2.cc b/projects/compiler-rt/lib/tsan/lit_tests/vptr_harmful_race2.cc new file mode 100644 index 00000000..378790c6 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/vptr_harmful_race2.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include +#include +#include + +struct A { + A() { + sem_init(&sem_, 0, 0); + } + virtual void F() { + } + void Done() { + sem_post(&sem_); + } + virtual ~A() { + sem_wait(&sem_); + sem_destroy(&sem_); + } + sem_t sem_; +}; + +struct B : A { + virtual void F() { + } + virtual ~B() { } +}; + +static A *obj = new B; + +void *Thread1(void *x) { + sleep(1); + obj->F(); + obj->Done(); + return NULL; +} + +void *Thread2(void *x) { + delete obj; + return NULL; +} + +int main() { + pthread_t t[2]; + pthread_create(&t[0], NULL, Thread1, NULL); + pthread_create(&t[1], NULL, Thread2, NULL); + pthread_join(t[0], NULL); + pthread_join(t[1], NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race on vptr diff --git a/projects/compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc b/projects/compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc new file mode 100644 index 00000000..e872fe3f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/lit_tests/write_in_reader_lock.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +#include +#include + +pthread_rwlock_t rwlock; +int GLOB; + +void *Thread1(void *p) { + (void)p; + pthread_rwlock_rdlock(&rwlock); + // Write under reader lock. + sleep(1); + GLOB++; + pthread_rwlock_unlock(&rwlock); + return 0; +} + +int main(int argc, char *argv[]) { + pthread_rwlock_init(&rwlock, NULL); + pthread_rwlock_rdlock(&rwlock); + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + volatile int x = GLOB; + (void)x; + pthread_rwlock_unlock(&rwlock); + pthread_join(t, 0); + pthread_rwlock_destroy(&rwlock); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 4 at {{.*}} by thread T1{{.*}}: +// CHECK: #0 Thread1(void*) {{.*}}write_in_reader_lock.cc:13 +// CHECK: Previous read of size 4 at {{.*}} by main thread{{.*}}: +// CHECK: #0 main {{.*}}write_in_reader_lock.cc:23 diff --git a/projects/compiler-rt/lib/tsan/rtl/Makefile.mk b/projects/compiler-rt/lib/tsan/rtl/Makefile.mk new file mode 100644 index 00000000..2687123f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/Makefile.mk @@ -0,0 +1,25 @@ +#===- lib/tsan/rtl/Makefile.mk -----------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := tsan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) $(AsmSources:%.S=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../../interception/*.h) +Dependencies += $(wildcard $(Dir)/../../sanitizer_common/*.h) + +# Define a convenience variable for all the tsan functions. +TsanFunctions += $(Sources:%.cc=%) $(AsmSources:%.S=%) diff --git a/projects/compiler-rt/lib/tsan/rtl/Makefile.old b/projects/compiler-rt/lib/tsan/rtl/Makefile.old new file mode 100644 index 00000000..33944ffe --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/Makefile.old @@ -0,0 +1,60 @@ +CXXFLAGS = -fPIE -g -Wall -Werror -fno-builtin -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG) +CLANG=clang +ifeq ($(DEBUG), 0) + CXXFLAGS += -O3 +endif + +# For interception. FIXME: move interception one level higher. +INTERCEPTION=../../interception +COMMON=../../sanitizer_common +INCLUDES= -I../.. -I../../../include +EXTRA_CXXFLAGS=-fno-exceptions -fno-rtti +NO_SYSROOT=--sysroot=. +CXXFLAGS+=$(EXTRA_CXXFLAGS) +CXXFLAGS+=$(CFLAGS) +ifeq ($(DEBUG), 0) + CXXFLAGS+=-fomit-frame-pointer +ifeq ($(CXX), g++) + CXXFLAGS+=-Wframe-larger-than=512 +endif # CXX=g++ +endif # DEBUG=0 + +ifeq ($(CXX), $(CLANG)++) + # Global constructors are banned. + CXXFLAGS+=-Wglobal-constructors +endif + + + +all: libtsan.a + +LIBTSAN_HEADERS=$(wildcard *.h) \ + $(wildcard $(INTERCEPTION)/*.h) \ + $(wildcard $(COMMON)/*.h) +LIBTSAN_SRC=$(wildcard *.cc) +LIBTSAN_ASM_SRC=$(wildcard *.S) +INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc) +COMMON_SRC=$(wildcard $(COMMON)/*.cc) + +LIBTSAN_OBJ=$(patsubst %.cc,%.o,$(LIBTSAN_SRC)) \ + $(patsubst %.S,%.o,$(LIBTSAN_ASM_SRC)) \ + $(patsubst $(INTERCEPTION)/%.cc,%.o,$(INTERCEPTION_SRC)) \ + $(patsubst $(COMMON)/%.cc,%.o,$(COMMON_SRC)) + +%_linux.o: %_linux.cc Makefile.old $(LIBTSAN_HEADERS) + $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< +%.o: %.cc Makefile.old $(LIBTSAN_HEADERS) + $(CXX) $(CXXFLAGS) $(INCLUDES) $(NO_SYSROOT) -c $< +%.o: $(INTERCEPTION)/%.cc Makefile.old $(LIBTSAN_HEADERS) + $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ +%.o: $(COMMON)/%.cc Makefile.old $(LIBTSAN_HEADERS) + $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ + +libtsan.a: $(LIBTSAN_OBJ) + ar ru $@ $(LIBTSAN_OBJ) + +libtsan_dummy.a: tsan_dummy_rtl.o + ar ru $@ $< + +clean: + rm -f *.o *.a diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan.syms.extra b/projects/compiler-rt/lib/tsan/rtl/tsan.syms.extra new file mode 100644 index 00000000..49ed6b46 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan.syms.extra @@ -0,0 +1,14 @@ +__tsan_init +__tsan_read* +__tsan_write* +__tsan_vptr* +__tsan_func* +__tsan_atomic* +__tsan_java* +__tsan_unaligned* +__tsan_release +__tsan_acquire +Annotate* +WTFAnnotate* +RunningOnValgrind +ValgrindSlowdown diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_clock.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_clock.cc new file mode 100644 index 00000000..f8745ec3 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_clock.cc @@ -0,0 +1,111 @@ +//===-- tsan_clock.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_clock.h" +#include "tsan_rtl.h" + +// It's possible to optimize clock operations for some important cases +// so that they are O(1). The cases include singletons, once's, local mutexes. +// First, SyncClock must be re-implemented to allow indexing by tid. +// It must not necessarily be a full vector clock, though. For example it may +// be a multi-level table. +// Then, each slot in SyncClock must contain a dirty bit (it's united with +// the clock value, so no space increase). The acquire algorithm looks +// as follows: +// void acquire(thr, tid, thr_clock, sync_clock) { +// if (!sync_clock[tid].dirty) +// return; // No new info to acquire. +// // This handles constant reads of singleton pointers and +// // stop-flags. +// acquire_impl(thr_clock, sync_clock); // As usual, O(N). +// sync_clock[tid].dirty = false; +// sync_clock.dirty_count--; +// } +// The release operation looks as follows: +// void release(thr, tid, thr_clock, sync_clock) { +// // thr->sync_cache is a simple fixed-size hash-based cache that holds +// // several previous sync_clock's. +// if (thr->sync_cache[sync_clock] >= thr->last_acquire_epoch) { +// // The thread did no acquire operations since last release on this clock. +// // So update only the thread's slot (other slots can't possibly change). +// sync_clock[tid].clock = thr->epoch; +// if (sync_clock.dirty_count == sync_clock.cnt +// || (sync_clock.dirty_count == sync_clock.cnt - 1 +// && sync_clock[tid].dirty == false)) +// // All dirty flags are set, bail out. +// return; +// set all dirty bits, but preserve the thread's bit. // O(N) +// update sync_clock.dirty_count; +// return; +// } +// release_impl(thr_clock, sync_clock); // As usual, O(N). +// set all dirty bits, but preserve the thread's bit. +// // The previous step is combined with release_impl(), so that +// // we scan the arrays only once. +// update sync_clock.dirty_count; +// } + +namespace __tsan { + +ThreadClock::ThreadClock() { + nclk_ = 0; + for (uptr i = 0; i < (uptr)kMaxTidInClock; i++) + clk_[i] = 0; +} + +void ThreadClock::acquire(const SyncClock *src) { + DCHECK(nclk_ <= kMaxTid); + DCHECK(src->clk_.Size() <= kMaxTid); + + const uptr nclk = src->clk_.Size(); + if (nclk == 0) + return; + nclk_ = max(nclk_, nclk); + for (uptr i = 0; i < nclk; i++) { + if (clk_[i] < src->clk_[i]) + clk_[i] = src->clk_[i]; + } +} + +void ThreadClock::release(SyncClock *dst) const { + DCHECK(nclk_ <= kMaxTid); + DCHECK(dst->clk_.Size() <= kMaxTid); + + if (dst->clk_.Size() < nclk_) + dst->clk_.Resize(nclk_); + for (uptr i = 0; i < nclk_; i++) { + if (dst->clk_[i] < clk_[i]) + dst->clk_[i] = clk_[i]; + } +} + +void ThreadClock::ReleaseStore(SyncClock *dst) const { + DCHECK(nclk_ <= kMaxTid); + DCHECK(dst->clk_.Size() <= kMaxTid); + + if (dst->clk_.Size() < nclk_) + dst->clk_.Resize(nclk_); + for (uptr i = 0; i < nclk_; i++) + dst->clk_[i] = clk_[i]; + for (uptr i = nclk_; i < dst->clk_.Size(); i++) + dst->clk_[i] = 0; +} + +void ThreadClock::acq_rel(SyncClock *dst) { + acquire(dst); + release(dst); +} + +SyncClock::SyncClock() + : clk_(MBlockClock) { +} +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_clock.h b/projects/compiler-rt/lib/tsan/rtl/tsan_clock.h new file mode 100644 index 00000000..0ee93749 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_clock.h @@ -0,0 +1,80 @@ +//===-- tsan_clock.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_CLOCK_H +#define TSAN_CLOCK_H + +#include "tsan_defs.h" +#include "tsan_vector.h" + +namespace __tsan { + +// The clock that lives in sync variables (mutexes, atomics, etc). +class SyncClock { + public: + SyncClock(); + + uptr size() const { + return clk_.Size(); + } + + void Reset() { + clk_.Reset(); + } + + private: + Vector clk_; + friend struct ThreadClock; +}; + +// The clock that lives in threads. +struct ThreadClock { + public: + ThreadClock(); + + u64 get(unsigned tid) const { + DCHECK_LT(tid, kMaxTidInClock); + return clk_[tid]; + } + + void set(unsigned tid, u64 v) { + DCHECK_LT(tid, kMaxTid); + DCHECK_GE(v, clk_[tid]); + clk_[tid] = v; + if (nclk_ <= tid) + nclk_ = tid + 1; + } + + void tick(unsigned tid) { + DCHECK_LT(tid, kMaxTid); + clk_[tid]++; + if (nclk_ <= tid) + nclk_ = tid + 1; + } + + uptr size() const { + return nclk_; + } + + void acquire(const SyncClock *src); + void release(SyncClock *dst) const; + void acq_rel(SyncClock *dst); + void ReleaseStore(SyncClock *dst) const; + + private: + uptr nclk_; + u64 clk_[kMaxTidInClock]; +}; + +} // namespace __tsan + +#endif // TSAN_CLOCK_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_defs.h b/projects/compiler-rt/lib/tsan/rtl/tsan_defs.h new file mode 100644 index 00000000..1e53a8e0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_defs.h @@ -0,0 +1,166 @@ +//===-- tsan_defs.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAN_DEFS_H +#define TSAN_DEFS_H + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "tsan_stat.h" + +#ifndef TSAN_DEBUG +#define TSAN_DEBUG 0 +#endif // TSAN_DEBUG + +namespace __tsan { + +#ifdef TSAN_GO +const bool kGoMode = true; +const bool kCppMode = false; +const char *const kTsanOptionsEnv = "GORACE"; +// Go linker does not support weak symbols. +#define CPP_WEAK +#else +const bool kGoMode = false; +const bool kCppMode = true; +const char *const kTsanOptionsEnv = "TSAN_OPTIONS"; +#define CPP_WEAK WEAK +#endif + +const int kTidBits = 13; +const unsigned kMaxTid = 1 << kTidBits; +const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. +const int kClkBits = 42; +const uptr kShadowStackSize = 64 * 1024; +const uptr kTraceStackSize = 256; + +#ifdef TSAN_SHADOW_COUNT +# if TSAN_SHADOW_COUNT == 2 \ + || TSAN_SHADOW_COUNT == 4 || TSAN_SHADOW_COUNT == 8 +const uptr kShadowCnt = TSAN_SHADOW_COUNT; +# else +# error "TSAN_SHADOW_COUNT must be one of 2,4,8" +# endif +#else +// Count of shadow values in a shadow cell. +const uptr kShadowCnt = 4; +#endif + +// That many user bytes are mapped onto a single shadow cell. +const uptr kShadowCell = 8; + +// Size of a single shadow value (u64). +const uptr kShadowSize = 8; + +// Shadow memory is kShadowMultiplier times larger than user memory. +const uptr kShadowMultiplier = kShadowSize * kShadowCnt / kShadowCell; + +#if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS +const bool kCollectStats = true; +#else +const bool kCollectStats = false; +#endif + +// The following "build consistency" machinery ensures that all source files +// are built in the same configuration. Inconsistent builds lead to +// hard to debug crashes. +#if TSAN_DEBUG +void build_consistency_debug(); +#else +void build_consistency_release(); +#endif + +#if TSAN_COLLECT_STATS +void build_consistency_stats(); +#else +void build_consistency_nostats(); +#endif + +#if TSAN_SHADOW_COUNT == 1 +void build_consistency_shadow1(); +#elif TSAN_SHADOW_COUNT == 2 +void build_consistency_shadow2(); +#elif TSAN_SHADOW_COUNT == 4 +void build_consistency_shadow4(); +#else +void build_consistency_shadow8(); +#endif + +static inline void USED build_consistency() { +#if TSAN_DEBUG + build_consistency_debug(); +#else + build_consistency_release(); +#endif +#if TSAN_COLLECT_STATS + build_consistency_stats(); +#else + build_consistency_nostats(); +#endif +#if TSAN_SHADOW_COUNT == 1 + build_consistency_shadow1(); +#elif TSAN_SHADOW_COUNT == 2 + build_consistency_shadow2(); +#elif TSAN_SHADOW_COUNT == 4 + build_consistency_shadow4(); +#else + build_consistency_shadow8(); +#endif +} + +template +T min(T a, T b) { + return a < b ? a : b; +} + +template +T max(T a, T b) { + return a > b ? a : b; +} + +template +T RoundUp(T p, u64 align) { + DCHECK_EQ(align & (align - 1), 0); + return (T)(((u64)p + align - 1) & ~(align - 1)); +} + +template +T RoundDown(T p, u64 align) { + DCHECK_EQ(align & (align - 1), 0); + return (T)((u64)p & ~(align - 1)); +} + +// Zeroizes high part, returns 'bits' lsb bits. +template +T GetLsb(T v, int bits) { + return (T)((u64)v & ((1ull << bits) - 1)); +} + +struct MD5Hash { + u64 hash[2]; + bool operator==(const MD5Hash &other) const; +}; + +MD5Hash md5_hash(const void *data, uptr size); + +struct ThreadState; +struct Context; +struct ReportStack; +class ReportDesc; +class RegionAlloc; +class StackTrace; +struct MBlock; + +} // namespace __tsan + +#endif // TSAN_DEFS_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_fd.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_fd.cc new file mode 100644 index 00000000..86db119f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_fd.cc @@ -0,0 +1,300 @@ +//===-- tsan_fd.cc --------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "tsan_fd.h" +#include "tsan_rtl.h" +#include + +namespace __tsan { + +const int kTableSizeL1 = 1024; +const int kTableSizeL2 = 1024; +const int kTableSize = kTableSizeL1 * kTableSizeL2; + +struct FdSync { + atomic_uint64_t rc; +}; + +struct FdDesc { + FdSync *sync; + int creation_tid; + u32 creation_stack; +}; + +struct FdContext { + atomic_uintptr_t tab[kTableSizeL1]; + // Addresses used for synchronization. + FdSync globsync; + FdSync filesync; + FdSync socksync; + u64 connectsync; +}; + +static FdContext fdctx; + +static bool bogusfd(int fd) { + // Apparently a bogus fd value. + return fd < 0 || fd >= (1 << 30); +} + +static FdSync *allocsync() { + FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync)); + atomic_store(&s->rc, 1, memory_order_relaxed); + return s; +} + +static FdSync *ref(FdSync *s) { + if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) + atomic_fetch_add(&s->rc, 1, memory_order_relaxed); + return s; +} + +static void unref(ThreadState *thr, uptr pc, FdSync *s) { + if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) { + if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) { + CHECK_NE(s, &fdctx.globsync); + CHECK_NE(s, &fdctx.filesync); + CHECK_NE(s, &fdctx.socksync); + SyncVar *v = CTX()->synctab.GetAndRemove(thr, pc, (uptr)s); + if (v) + DestroyAndFree(v); + internal_free(s); + } + } +} + +static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { + CHECK_GE(fd, 0); + CHECK_LT(fd, kTableSize); + atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; + uptr l1 = atomic_load(pl1, memory_order_consume); + if (l1 == 0) { + uptr size = kTableSizeL2 * sizeof(FdDesc); + // We need this to reside in user memory to properly catch races on it. + void *p = user_alloc(thr, pc, size); + internal_memset(p, 0, size); + MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size); + if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel)) + l1 = (uptr)p; + else + user_free(thr, pc, p); + } + return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT +} + +// pd must be already ref'ed. +static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) { + FdDesc *d = fddesc(thr, pc, fd); + // As a matter of fact, we don't intercept all close calls. + // See e.g. libc __res_iclose(). + if (d->sync) { + unref(thr, pc, d->sync); + d->sync = 0; + } + if (flags()->io_sync == 0) { + unref(thr, pc, s); + } else if (flags()->io_sync == 1) { + d->sync = s; + } else if (flags()->io_sync == 2) { + unref(thr, pc, s); + d->sync = &fdctx.globsync; + } + d->creation_tid = thr->tid; + d->creation_stack = CurrentStackId(thr, pc); + // To catch races between fd usage and open. + MemoryRangeImitateWrite(thr, pc, (uptr)d, 8); +} + +void FdInit() { + atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed); + atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed); + atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed); +} + +void FdOnFork(ThreadState *thr, uptr pc) { + // On fork() we need to reset all fd's, because the child is going + // close all them, and that will cause races between previous read/write + // and the close. + for (int l1 = 0; l1 < kTableSizeL1; l1++) { + FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); + if (tab == 0) + break; + for (int l2 = 0; l2 < kTableSizeL2; l2++) { + FdDesc *d = &tab[l2]; + MemoryResetRange(thr, pc, (uptr)d, 8); + } + } +} + +bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) { + for (int l1 = 0; l1 < kTableSizeL1; l1++) { + FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); + if (tab == 0) + break; + if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) { + int l2 = (addr - (uptr)tab) / sizeof(FdDesc); + FdDesc *d = &tab[l2]; + *fd = l1 * kTableSizeL1 + l2; + *tid = d->creation_tid; + *stack = d->creation_stack; + return true; + } + } + return false; +} + +void FdAcquire(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; + FdDesc *d = fddesc(thr, pc, fd); + FdSync *s = d->sync; + DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); + if (s) + Acquire(thr, pc, (uptr)s); +} + +void FdRelease(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; + FdDesc *d = fddesc(thr, pc, fd); + FdSync *s = d->sync; + DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); + if (s) + Release(thr, pc, (uptr)s); +} + +void FdAccess(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + FdDesc *d = fddesc(thr, pc, fd); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); +} + +void FdClose(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + FdDesc *d = fddesc(thr, pc, fd); + // To catch races between fd usage and close. + MemoryWrite(thr, pc, (uptr)d, kSizeLog8); + // We need to clear it, because if we do not intercept any call out there + // that creates fd, we will hit false postives. + MemoryResetRange(thr, pc, (uptr)d, 8); + unref(thr, pc, d->sync); + d->sync = 0; + d->creation_tid = 0; + d->creation_stack = 0; +} + +void FdFileCreate(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + init(thr, pc, fd, &fdctx.filesync); +} + +void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { + DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); + if (bogusfd(oldfd) || bogusfd(newfd)) + return; + // Ignore the case when user dups not yet connected socket. + FdDesc *od = fddesc(thr, pc, oldfd); + MemoryRead(thr, pc, (uptr)od, kSizeLog8); + FdClose(thr, pc, newfd); + init(thr, pc, newfd, ref(od->sync)); +} + +void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { + DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd); + FdSync *s = allocsync(); + init(thr, pc, rfd, ref(s)); + init(thr, pc, wfd, ref(s)); + unref(thr, pc, s); +} + +void FdEventCreate(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + init(thr, pc, fd, allocsync()); +} + +void FdSignalCreate(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + init(thr, pc, fd, 0); +} + +void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + init(thr, pc, fd, 0); +} + +void FdPollCreate(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + init(thr, pc, fd, allocsync()); +} + +void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + // It can be a UDP socket. + init(thr, pc, fd, &fdctx.socksync); +} + +void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { + DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); + if (bogusfd(fd)) + return; + // Synchronize connect->accept. + Acquire(thr, pc, (uptr)&fdctx.connectsync); + init(thr, pc, newfd, &fdctx.socksync); +} + +void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + // Synchronize connect->accept. + Release(thr, pc, (uptr)&fdctx.connectsync); +} + +void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { + DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; + init(thr, pc, fd, &fdctx.socksync); +} + +uptr File2addr(char *path) { + (void)path; + static u64 addr; + return (uptr)&addr; +} + +uptr Dir2addr(char *path) { + (void)path; + static u64 addr; + return (uptr)&addr; +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_fd.h b/projects/compiler-rt/lib/tsan/rtl/tsan_fd.h new file mode 100644 index 00000000..979198e2 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_fd.h @@ -0,0 +1,65 @@ +//===-- tsan_fd.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// This file handles synchronization via IO. +// People use IO for synchronization along the lines of: +// +// int X; +// int client_socket; // initialized elsewhere +// int server_socket; // initialized elsewhere +// +// Thread 1: +// X = 42; +// send(client_socket, ...); +// +// Thread 2: +// if (recv(server_socket, ...) > 0) +// assert(X == 42); +// +// This file determines the scope of the file descriptor (pipe, socket, +// all local files, etc) and executes acquire and release operations on +// the scope as necessary. Some scopes are very fine grained (e.g. pipe +// operations synchronize only with operations on the same pipe), while +// others are corse-grained (e.g. all operations on local files synchronize +// with each other). +//===----------------------------------------------------------------------===// +#ifndef TSAN_FD_H +#define TSAN_FD_H + +#include "tsan_rtl.h" + +namespace __tsan { + +void FdInit(); +void FdAcquire(ThreadState *thr, uptr pc, int fd); +void FdRelease(ThreadState *thr, uptr pc, int fd); +void FdAccess(ThreadState *thr, uptr pc, int fd); +void FdClose(ThreadState *thr, uptr pc, int fd); +void FdFileCreate(ThreadState *thr, uptr pc, int fd); +void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd); +void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd); +void FdEventCreate(ThreadState *thr, uptr pc, int fd); +void FdSignalCreate(ThreadState *thr, uptr pc, int fd); +void FdInotifyCreate(ThreadState *thr, uptr pc, int fd); +void FdPollCreate(ThreadState *thr, uptr pc, int fd); +void FdSocketCreate(ThreadState *thr, uptr pc, int fd); +void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd); +void FdSocketConnecting(ThreadState *thr, uptr pc, int fd); +void FdSocketConnect(ThreadState *thr, uptr pc, int fd); +bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack); +void FdOnFork(ThreadState *thr, uptr pc); + +uptr File2addr(char *path); +uptr Dir2addr(char *path); + +} // namespace __tsan + +#endif // TSAN_INTERFACE_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_flags.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_flags.cc new file mode 100644 index 00000000..c6f24bf4 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_flags.cc @@ -0,0 +1,127 @@ +//===-- tsan_flags.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "tsan_flags.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" + +namespace __tsan { + +Flags *flags() { + return &CTX()->flags; +} + +// Can be overriden in frontend. +#ifdef TSAN_EXTERNAL_HOOKS +void OverrideFlags(Flags *f); +extern "C" const char* __tsan_default_options(); +#else +void WEAK OverrideFlags(Flags *f) { + (void)f; +} +extern "C" const char *WEAK __tsan_default_options() { + return ""; +} +#endif + +static void ParseFlags(Flags *f, const char *env) { + ParseFlag(env, &f->enable_annotations, "enable_annotations"); + ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); + ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); + ParseFlag(env, &f->suppress_java, "suppress_java"); + ParseFlag(env, &f->report_bugs, "report_bugs"); + ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); + ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); + ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); + ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); + ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); + ParseFlag(env, &f->suppressions, "suppressions"); + ParseFlag(env, &f->print_suppressions, "print_suppressions"); + ParseFlag(env, &f->print_benign, "print_benign"); + ParseFlag(env, &f->exitcode, "exitcode"); + ParseFlag(env, &f->halt_on_error, "halt_on_error"); + ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); + ParseFlag(env, &f->profile_memory, "profile_memory"); + ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); + ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); + ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb"); + ParseFlag(env, &f->stop_on_start, "stop_on_start"); + ParseFlag(env, &f->history_size, "history_size"); + ParseFlag(env, &f->io_sync, "io_sync"); +} + +void InitializeFlags(Flags *f, const char *env) { + internal_memset(f, 0, sizeof(*f)); + + // Default values. + f->enable_annotations = true; + f->suppress_equal_stacks = true; + f->suppress_equal_addresses = true; + f->suppress_java = false; + f->report_bugs = true; + f->report_thread_leaks = true; + f->report_destroy_locked = true; + f->report_signal_unsafe = true; + f->report_atomic_races = true; + f->force_seq_cst_atomics = false; + f->suppressions = ""; + f->print_suppressions = false; + f->print_benign = false; + f->exitcode = 66; + f->halt_on_error = false; + f->atexit_sleep_ms = 1000; + f->profile_memory = ""; + f->flush_memory_ms = 0; + f->flush_symbolizer_ms = 5000; + f->memory_limit_mb = 0; + f->stop_on_start = false; + f->running_on_valgrind = false; + f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. + f->io_sync = 1; + + CommonFlags *cf = common_flags(); + SetCommonFlagDefaults(); + *static_cast(f) = *cf; + + // Let a frontend override. + OverrideFlags(f); + ParseFlags(f, __tsan_default_options()); + ParseCommonFlagsFromString(__tsan_default_options()); + // Override from command line. + ParseFlags(f, env); + ParseCommonFlagsFromString(env); + *static_cast(f) = *cf; + + // Sanity check. + if (!f->report_bugs) { + f->report_thread_leaks = false; + f->report_destroy_locked = false; + f->report_signal_unsafe = false; + } + + if (f->history_size < 0 || f->history_size > 7) { + Printf("ThreadSanitizer: incorrect value for history_size" + " (must be [0..7])\n"); + Die(); + } + + if (f->io_sync < 0 || f->io_sync > 2) { + Printf("ThreadSanitizer: incorrect value for io_sync" + " (must be [0..2])\n"); + Die(); + } +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_flags.h b/projects/compiler-rt/lib/tsan/rtl/tsan_flags.h new file mode 100644 index 00000000..3916df3c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_flags.h @@ -0,0 +1,96 @@ +//===-- tsan_flags.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// NOTE: This file may be included into user code. +//===----------------------------------------------------------------------===// + +#ifndef TSAN_FLAGS_H +#define TSAN_FLAGS_H + +// ----------- ATTENTION ------------- +// ThreadSanitizer user may provide its implementation of weak +// symbol __tsan::OverrideFlags(__tsan::Flags). Therefore, this +// header may be included in the user code, and shouldn't include +// other headers from TSan or common sanitizer runtime. + +#include "sanitizer_common/sanitizer_flags.h" + +namespace __tsan { + +struct Flags : CommonFlags { + // Enable dynamic annotations, otherwise they are no-ops. + bool enable_annotations; + // Supress a race report if we've already output another race report + // with the same stack. + bool suppress_equal_stacks; + // Supress a race report if we've already output another race report + // on the same address. + bool suppress_equal_addresses; + // Suppress weird race reports that can be seen if JVM is embed + // into the process. + bool suppress_java; + // Turns off bug reporting entirely (useful for benchmarking). + bool report_bugs; + // Report thread leaks at exit? + bool report_thread_leaks; + // Report destruction of a locked mutex? + bool report_destroy_locked; + // Report violations of async signal-safety + // (e.g. malloc() call from a signal handler). + bool report_signal_unsafe; + // Report races between atomic and plain memory accesses. + bool report_atomic_races; + // If set, all atomics are effectively sequentially consistent (seq_cst), + // regardless of what user actually specified. + bool force_seq_cst_atomics; + // Suppressions filename. + const char *suppressions; + // Print matched suppressions at exit. + bool print_suppressions; + // Print matched "benign" races at exit. + bool print_benign; + // Override exit status if something was reported. + int exitcode; + // Exit after first reported error. + bool halt_on_error; + // Sleep in main thread before exiting for that many ms + // (useful to catch "at exit" races). + int atexit_sleep_ms; + // If set, periodically write memory profile to that file. + const char *profile_memory; + // Flush shadow memory every X ms. + int flush_memory_ms; + // Flush symbolizer caches every X ms. + int flush_symbolizer_ms; + // Resident memory limit in MB to aim at. + // If the process consumes more memory, then TSan will flush shadow memory. + int memory_limit_mb; + // Stops on start until __tsan_resume() is called (for debugging). + bool stop_on_start; + // Controls whether RunningOnValgrind() returns true or false. + bool running_on_valgrind; + // Per-thread history size, controls how many previous memory accesses + // are remembered per thread. Possible values are [0..7]. + // history_size=0 amounts to 32K memory accesses. Each next value doubles + // the amount of memory accesses, up to history_size=7 that amounts to + // 4M memory accesses. The default value is 2 (128K memory accesses). + int history_size; + // Controls level of synchronization implied by IO operations. + // 0 - no synchronization + // 1 - reasonable level of synchronization (write->read) + // 2 - global synchronization of all IO operations + int io_sync; +}; + +Flags *flags(); +void InitializeFlags(Flags *flags, const char *env); +} // namespace __tsan + +#endif // TSAN_FLAGS_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc new file mode 100644 index 00000000..ef38f798 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc @@ -0,0 +1,2239 @@ +//===-- tsan_interceptors.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// FIXME: move as many interceptors as possible into +// sanitizer_common/sanitizer_common_interceptors.inc +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#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" + +using namespace __tsan; // NOLINT + +const int kSigCount = 64; + +struct my_siginfo_t { + // The size is determined by looking at sizeof of real siginfo_t on linux. + u64 opaque[128 / sizeof(u64)]; +}; + +struct ucontext_t { + // The size is determined by looking at sizeof of real ucontext_t on linux. + u64 opaque[936 / sizeof(u64) + 1]; +}; + +extern "C" int pthread_attr_init(void *attr); +extern "C" int pthread_attr_destroy(void *attr); +DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) +extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); +extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); +extern "C" int pthread_setspecific(unsigned key, const void *v); +extern "C" int pthread_mutexattr_gettype(void *a, int *type); +extern "C" int pthread_yield(); +extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset); +// REAL(sigfillset) defined in common interceptors. +DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) +extern "C" void *pthread_self(); +extern "C" void _exit(int status); +extern "C" int *__errno_location(); +extern "C" int fileno_unlocked(void *stream); +extern "C" void *__libc_malloc(uptr size); +extern "C" void *__libc_calloc(uptr size, uptr n); +extern "C" void *__libc_realloc(void *ptr, uptr size); +extern "C" void __libc_free(void *ptr); +extern "C" int mallopt(int param, int value); +const int PTHREAD_MUTEX_RECURSIVE = 1; +const int PTHREAD_MUTEX_RECURSIVE_NP = 1; +const int EINVAL = 22; +const int EBUSY = 16; +const int EOWNERDEAD = 130; +const int EPOLL_CTL_ADD = 1; +const int SIGILL = 4; +const int SIGABRT = 6; +const int SIGFPE = 8; +const int SIGSEGV = 11; +const int SIGPIPE = 13; +const int SIGBUS = 7; +const int SIGSYS = 31; +void *const MAP_FAILED = (void*)-1; +const int PTHREAD_BARRIER_SERIAL_THREAD = -1; +const int MAP_FIXED = 0x10; +typedef long long_t; // NOLINT + +// From /usr/include/unistd.h +# define F_ULOCK 0 /* Unlock a previously locked region. */ +# define F_LOCK 1 /* Lock a region for exclusive use. */ +# define F_TLOCK 2 /* Test and lock a region for exclusive use. */ +# define F_TEST 3 /* Test a region for other processes locks. */ + +typedef void (*sighandler_t)(int sig); + +#define errno (*__errno_location()) + +struct sigaction_t { + union { + sighandler_t sa_handler; + void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx); + }; + __sanitizer_sigset_t sa_mask; + int sa_flags; + void (*sa_restorer)(); +}; + +const sighandler_t SIG_DFL = (sighandler_t)0; +const sighandler_t SIG_IGN = (sighandler_t)1; +const sighandler_t SIG_ERR = (sighandler_t)-1; +const int SA_SIGINFO = 4; +const int SIG_SETMASK = 2; + +namespace std { +struct nothrow_t {}; +} // namespace std + +static sigaction_t sigactions[kSigCount]; + +namespace __tsan { +struct SignalDesc { + bool armed; + bool sigaction; + my_siginfo_t siginfo; + ucontext_t ctx; +}; + +struct SignalContext { + int in_blocking_func; + int int_signal_send; + int pending_signal_count; + SignalDesc pending_signals[kSigCount]; +}; + +// 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_placeholder[0]); +} + +void InitializeLibIgnore() { + libignore()->Init(*GetSuppressionContext()); + libignore()->OnLibraryLoaded(0); +} + +} // namespace __tsan + +static SignalContext *SigCtx(ThreadState *thr) { + SignalContext *ctx = (SignalContext*)thr->signal_ctx; + if (ctx == 0 && thr->is_alive) { + ScopedInRtl in_rtl; + ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext"); + MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx)); + thr->signal_ctx = ctx; + } + return ctx; +} + +static unsigned g_thread_finalize_key; + +class ScopedInterceptor { + public: + ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc); + ~ScopedInterceptor(); + 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_ignored_lib_(false) { + if (thr_->in_rtl == 0) { + Initialize(thr); + FuncEntry(thr, pc); + thr_->in_rtl++; + DPrintf("#%d: intercept %s()\n", thr_->tid, fname); + } 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_); + ProcessPendingSignals(thr_); + } + CHECK_EQ(in_rtl_, thr_->in_rtl); +} + +#define SCOPED_INTERCEPTOR_RAW(func, ...) \ + ThreadState *thr = cur_thread(); \ + StatInc(thr, StatInterceptor); \ + StatInc(thr, StatInt_##func); \ + const uptr caller_pc = GET_CALLER_PC(); \ + ScopedInterceptor si(thr, #func, caller_pc); \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ + (void)pc; \ +/**/ + +#define SCOPED_TSAN_INTERCEPTOR(func, ...) \ + SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ + if (REAL(func) == 0) { \ + Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ + Die(); \ + } \ + if (thr->in_rtl > 1 || thr->in_ignored_lib) \ + return REAL(func)(__VA_ARGS__); \ +/**/ + +#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__) +#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func) + +#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name)) + +struct BlockingCall { + explicit BlockingCall(ThreadState *thr) + : ctx(SigCtx(thr)) { + ctx->in_blocking_func++; + } + + ~BlockingCall() { + ctx->in_blocking_func--; + } + + SignalContext *ctx; +}; + +TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) { + SCOPED_TSAN_INTERCEPTOR(sleep, sec); + unsigned res = BLOCK_REAL(sleep)(sec); + AfterSleep(thr, pc); + return res; +} + +TSAN_INTERCEPTOR(int, usleep, long_t usec) { + SCOPED_TSAN_INTERCEPTOR(usleep, usec); + int res = BLOCK_REAL(usleep)(usec); + AfterSleep(thr, pc); + return res; +} + +TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) { + SCOPED_TSAN_INTERCEPTOR(nanosleep, req, rem); + int res = BLOCK_REAL(nanosleep)(req, rem); + AfterSleep(thr, pc); + 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(filename); + 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() + : mtx_(MutexTypeAtExit, StatMtxAtExit) + , pos_() { + } + + typedef void(*atexit_t)(); + + int atexit(ThreadState *thr, uptr pc, bool is_on_exit, + atexit_t f, void *arg) { + Lock l(&mtx_); + if (pos_ == kMaxAtExit) + return 1; + Release(thr, pc, (uptr)this); + stack_[pos_] = f; + args_[pos_] = arg; + is_on_exits_[pos_] = is_on_exit; + pos_++; + return 0; + } + + void exit(ThreadState *thr, uptr pc) { + CHECK_EQ(thr->in_rtl, 0); + for (;;) { + atexit_t f = 0; + void *arg = 0; + bool is_on_exit = false; + { + Lock l(&mtx_); + if (pos_) { + pos_--; + f = stack_[pos_]; + arg = args_[pos_]; + is_on_exit = is_on_exits_[pos_]; + ScopedInRtl in_rtl; + Acquire(thr, pc, (uptr)this); + } + } + if (f == 0) + break; + DPrintf("#%d: executing atexit func %p\n", thr->tid, f); + CHECK_EQ(thr->in_rtl, 0); + if (is_on_exit) + ((void(*)(int status, void *arg))f)(0, arg); + else + ((void(*)(void *arg, void *dso))f)(arg, 0); + } + } + + private: + static const int kMaxAtExit = 128; + Mutex mtx_; + atexit_t stack_[kMaxAtExit]; + void *args_[kMaxAtExit]; + bool is_on_exits_[kMaxAtExit]; + int pos_; +}; + +static AtExitContext *atexit_ctx; + +TSAN_INTERCEPTOR(int, atexit, void (*f)()) { + if (cur_thread()->in_symbolizer) + return 0; + SCOPED_TSAN_INTERCEPTOR(atexit, f); + return atexit_ctx->atexit(thr, pc, false, (void(*)())f, 0); +} + +TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) { + if (cur_thread()->in_symbolizer) + return 0; + SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg); + return atexit_ctx->atexit(thr, pc, true, (void(*)())f, arg); +} + +TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { + if (cur_thread()->in_symbolizer) + return 0; + SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); + if (dso) { + // Memory allocation in __cxa_atexit will race with free during exit, + // because we do not see synchronization around atexit callback list. + ThreadIgnoreBegin(thr); + int res = REAL(__cxa_atexit)(f, arg, dso); + ThreadIgnoreEnd(thr); + return res; + } + return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); +} + +// Cleanup old bufs. +static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { + for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { + JmpBuf *buf = &thr->jmp_bufs[i]; + if (buf->sp <= sp) { + uptr sz = thr->jmp_bufs.Size(); + thr->jmp_bufs[i] = thr->jmp_bufs[sz - 1]; + thr->jmp_bufs.PopBack(); + i--; + } + } +} + +static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) { + if (thr->shadow_stack_pos == 0) // called from libc guts during bootstrap + return; + // Cleanup old bufs. + JmpBufGarbageCollect(thr, sp); + // Remember the buf. + JmpBuf *buf = thr->jmp_bufs.PushBack(); + buf->sp = sp; + buf->mangled_sp = mangled_sp; + buf->shadow_stack_pos = thr->shadow_stack_pos; +} + +static void LongJmp(ThreadState *thr, uptr *env) { + uptr mangled_sp = env[6]; + // Find the saved buf by mangled_sp. + for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { + JmpBuf *buf = &thr->jmp_bufs[i]; + if (buf->mangled_sp == mangled_sp) { + CHECK_GE(thr->shadow_stack_pos, buf->shadow_stack_pos); + // Unwind the stack. + while (thr->shadow_stack_pos > buf->shadow_stack_pos) + FuncExit(thr); + JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp + return; + } + } + Printf("ThreadSanitizer: can't find longjmp buf\n"); + CHECK(0); +} + +// FIXME: put everything below into a common extern "C" block? +extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { + ScopedInRtl in_rtl; + SetJmp(cur_thread(), sp, mangled_sp); +} + +// Not called. Merely to satisfy TSAN_INTERCEPT(). +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_setjmp(void *env); +extern "C" int __interceptor_setjmp(void *env) { + CHECK(0); + return 0; +} + +// FIXME: any reason to have a separate declaration? +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor__setjmp(void *env); +extern "C" int __interceptor__setjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_sigsetjmp(void *env); +extern "C" int __interceptor_sigsetjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor___sigsetjmp(void *env); +extern "C" int __interceptor___sigsetjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" int setjmp(void *env); +extern "C" int _setjmp(void *env); +extern "C" int sigsetjmp(void *env); +extern "C" int __sigsetjmp(void *env); +DEFINE_REAL(int, setjmp, void *env) +DEFINE_REAL(int, _setjmp, void *env) +DEFINE_REAL(int, sigsetjmp, void *env) +DEFINE_REAL(int, __sigsetjmp, void *env) + +TSAN_INTERCEPTOR(void, longjmp, uptr *env, int val) { + { + SCOPED_TSAN_INTERCEPTOR(longjmp, env, val); + } + LongJmp(cur_thread(), env); + REAL(longjmp)(env, val); +} + +TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) { + { + SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val); + } + LongJmp(cur_thread(), env); + REAL(siglongjmp)(env, val); +} + +TSAN_INTERCEPTOR(void*, malloc, uptr size) { + if (cur_thread()->in_symbolizer) + return __libc_malloc(size); + void *p = 0; + { + SCOPED_INTERCEPTOR_RAW(malloc, size); + p = user_alloc(thr, pc, size); + } + invoke_malloc_hook(p, size); + return p; +} + +TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { + SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz); + return user_alloc(thr, pc, sz, align); +} + +TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { + if (cur_thread()->in_symbolizer) + return __libc_calloc(size, n); + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) + return AllocatorReturnNull(); + void *p = 0; + { + SCOPED_INTERCEPTOR_RAW(calloc, size, n); + p = user_alloc(thr, pc, n * size); + if (p) + internal_memset(p, 0, n * size); + } + invoke_malloc_hook(p, n * size); + return p; +} + +TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { + if (cur_thread()->in_symbolizer) + return __libc_realloc(p, size); + if (p) + invoke_free_hook(p); + { + SCOPED_INTERCEPTOR_RAW(realloc, p, size); + p = user_realloc(thr, pc, p, size); + } + invoke_malloc_hook(p, size); + return p; +} + +TSAN_INTERCEPTOR(void, free, void *p) { + if (p == 0) + return; + if (cur_thread()->in_symbolizer) + return __libc_free(p); + invoke_free_hook(p); + SCOPED_INTERCEPTOR_RAW(free, p); + user_free(thr, pc, p); +} + +TSAN_INTERCEPTOR(void, cfree, void *p) { + if (p == 0) + return; + if (cur_thread()->in_symbolizer) + return __libc_free(p); + invoke_free_hook(p); + SCOPED_INTERCEPTOR_RAW(cfree, p); + user_free(thr, pc, p); +} + +TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { + SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); + return user_alloc_usable_size(thr, pc, p); +} + +#define OPERATOR_NEW_BODY(mangled_name) \ + if (cur_thread()->in_symbolizer) \ + return __libc_malloc(size); \ + void *p = 0; \ + { \ + SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ + p = user_alloc(thr, pc, size); \ + } \ + invoke_malloc_hook(p, size); \ + return p; + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size); +void *operator new(__sanitizer::uptr size) { + OPERATOR_NEW_BODY(_Znwm); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size); +void *operator new[](__sanitizer::uptr size) { + OPERATOR_NEW_BODY(_Znam); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size, std::nothrow_t const&); +void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); +void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); +} + +#define OPERATOR_DELETE_BODY(mangled_name) \ + if (ptr == 0) return; \ + if (cur_thread()->in_symbolizer) \ + return __libc_free(ptr); \ + invoke_free_hook(ptr); \ + SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ + user_free(thr, pc, ptr); + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr); +void operator delete(void *ptr) { + OPERATOR_DELETE_BODY(_ZdlPv); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr); +void operator delete[](void *ptr) { + OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&); +void operator delete(void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(_ZdaPv); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&); +void operator delete[](void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); +} + +TSAN_INTERCEPTOR(uptr, strlen, const char *s) { + SCOPED_TSAN_INTERCEPTOR(strlen, s); + uptr len = internal_strlen(s); + MemoryAccessRange(thr, pc, (uptr)s, len + 1, false); + return len; +} + +TSAN_INTERCEPTOR(void*, memset, void *dst, int v, uptr size) { + SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size); + MemoryAccessRange(thr, pc, (uptr)dst, size, true); + return internal_memset(dst, v, size); +} + +TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) { + SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size); + MemoryAccessRange(thr, pc, (uptr)dst, size, true); + MemoryAccessRange(thr, pc, (uptr)src, size, false); + return internal_memcpy(dst, src, size); +} + +TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) { + SCOPED_TSAN_INTERCEPTOR(memcmp, s1, s2, n); + int res = 0; + uptr len = 0; + for (; len < n; len++) { + if ((res = ((unsigned char*)s1)[len] - ((unsigned char*)s2)[len])) + break; + } + MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false); + MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false); + return res; +} + +TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) { + SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n); + void *res = REAL(memchr)(s, c, n); + uptr len = res ? (char*)res - (char*)s + 1 : n; + MemoryAccessRange(thr, pc, (uptr)s, len, false); + return res; +} + +TSAN_INTERCEPTOR(void*, memrchr, char *s, int c, uptr n) { + SCOPED_TSAN_INTERCEPTOR(memrchr, s, c, n); + MemoryAccessRange(thr, pc, (uptr)s, n, false); + return REAL(memrchr)(s, c, n); +} + +TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) { + SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n); + MemoryAccessRange(thr, pc, (uptr)dst, n, true); + MemoryAccessRange(thr, pc, (uptr)src, n, false); + return REAL(memmove)(dst, src, n); +} + +TSAN_INTERCEPTOR(char*, strchr, char *s, int c) { + SCOPED_TSAN_INTERCEPTOR(strchr, s, c); + char *res = REAL(strchr)(s, c); + uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1; + MemoryAccessRange(thr, pc, (uptr)s, len, false); + return res; +} + +TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) { + SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c); + char *res = REAL(strchrnul)(s, c); + uptr len = (char*)res - (char*)s + 1; + MemoryAccessRange(thr, pc, (uptr)s, len, false); + return res; +} + +TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) { + SCOPED_TSAN_INTERCEPTOR(strrchr, s, c); + MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s) + 1, false); + return REAL(strrchr)(s, c); +} + +TSAN_INTERCEPTOR(char*, strcpy, char *dst, const char *src) { // NOLINT + SCOPED_TSAN_INTERCEPTOR(strcpy, dst, src); // NOLINT + uptr srclen = internal_strlen(src); + MemoryAccessRange(thr, pc, (uptr)dst, srclen + 1, true); + MemoryAccessRange(thr, pc, (uptr)src, srclen + 1, false); + return REAL(strcpy)(dst, src); // NOLINT +} + +TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) { + SCOPED_TSAN_INTERCEPTOR(strncpy, dst, src, n); + uptr srclen = internal_strnlen(src, n); + MemoryAccessRange(thr, pc, (uptr)dst, n, true); + MemoryAccessRange(thr, pc, (uptr)src, min(srclen + 1, n), false); + return REAL(strncpy)(dst, src, n); +} + +TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { + SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2); + const char *res = REAL(strstr)(s1, s2); + uptr len1 = internal_strlen(s1); + uptr len2 = internal_strlen(s2); + MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false); + MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false); + return res; +} + +TSAN_INTERCEPTOR(char*, strdup, const char *str) { + SCOPED_TSAN_INTERCEPTOR(strdup, str); + // strdup will call malloc, so no instrumentation is required here. + return REAL(strdup)(str); +} + +static bool fix_mmap_addr(void **addr, long_t sz, int flags) { + if (*addr) { + if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) { + if (flags & MAP_FIXED) { + errno = EINVAL; + return false; + } else { + *addr = 0; + } + } + } + return true; +} + +TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, + int flags, int fd, unsigned off) { + SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off); + if (!fix_mmap_addr(&addr, sz, flags)) + return MAP_FAILED; + void *res = REAL(mmap)(addr, sz, prot, flags, fd, off); + if (res != MAP_FAILED) { + if (fd > 0) + FdAccess(thr, pc, fd); + MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); + } + return res; +} + +TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, + int flags, int fd, u64 off) { + SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off); + if (!fix_mmap_addr(&addr, sz, flags)) + return MAP_FAILED; + void *res = REAL(mmap64)(addr, sz, prot, flags, fd, off); + if (res != MAP_FAILED) { + if (fd > 0) + FdAccess(thr, pc, fd); + MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); + } + return res; +} + +TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { + SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz); + DontNeedShadowFor((uptr)addr, sz); + int res = REAL(munmap)(addr, sz); + return res; +} + +TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { + SCOPED_INTERCEPTOR_RAW(memalign, align, sz); + return user_alloc(thr, pc, sz, align); +} + +TSAN_INTERCEPTOR(void*, valloc, uptr sz) { + SCOPED_INTERCEPTOR_RAW(valloc, sz); + return user_alloc(thr, pc, sz, GetPageSizeCached()); +} + +TSAN_INTERCEPTOR(void*, pvalloc, uptr 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_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); + *memptr = user_alloc(thr, pc, sz, align); + return 0; +} + +// Used in thread-safe function static initialization. +extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) { + SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g); + for (;;) { + u32 cmp = atomic_load(g, memory_order_acquire); + if (cmp == 0) { + if (atomic_compare_exchange_strong(g, &cmp, 1<<16, memory_order_relaxed)) + return 1; + } else if (cmp == 1) { + Acquire(thr, pc, (uptr)g); + return 0; + } else { + internal_sched_yield(); + } + } +} + +extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_release(atomic_uint32_t *g) { + SCOPED_INTERCEPTOR_RAW(__cxa_guard_release, g); + Release(thr, pc, (uptr)g); + atomic_store(g, 1, memory_order_release); +} + +extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) { + SCOPED_INTERCEPTOR_RAW(__cxa_guard_abort, g); + atomic_store(g, 0, memory_order_relaxed); +} + +static void thread_finalize(void *v) { + uptr iter = (uptr)v; + if (iter > 1) { + if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { + Printf("ThreadSanitizer: failed to set thread key\n"); + Die(); + } + return; + } + { + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + ThreadFinish(thr); + SignalContext *sctx = thr->signal_ctx; + if (sctx) { + thr->signal_ctx = 0; + UnmapOrDie(sctx, sizeof(*sctx)); + } + } +} + + +struct ThreadParam { + void* (*callback)(void *arg); + void *param; + atomic_uintptr_t tid; +}; + +extern "C" void *__tsan_thread_start_func(void *arg) { + ThreadParam *p = (ThreadParam*)arg; + void* (*callback)(void *arg) = p->callback; + void *param = p->param; + int tid = 0; + { + ThreadState *thr = cur_thread(); + ScopedInRtl in_rtl; + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { + Printf("ThreadSanitizer: failed to set thread key\n"); + Die(); + } + while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) + pthread_yield(); + atomic_store(&p->tid, 0, memory_order_release); + ThreadStart(thr, tid, GetTid()); + CHECK_EQ(thr->in_rtl, 1); + } + void *res = callback(param); + // Prevent the callback from being tail called, + // it mixes up stack traces. + volatile int foo = 42; + foo++; + return res; +} + +TSAN_INTERCEPTOR(int, pthread_create, + void *th, void *attr, void *(*callback)(void*), void * param) { + SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param); + __sanitizer_pthread_attr_t myattr; + if (attr == 0) { + pthread_attr_init(&myattr); + attr = &myattr; + } + int detached = 0; + REAL(pthread_attr_getdetachstate)(attr, &detached); + AdjustStackSizeLinux(attr); + + ThreadParam p; + p.callback = callback; + p.param = param; + atomic_store(&p.tid, 0, memory_order_relaxed); + int res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p); + if (res == 0) { + int tid = ThreadCreate(thr, pc, *(uptr*)th, detached); + CHECK_NE(tid, 0); + atomic_store(&p.tid, tid, memory_order_release); + while (atomic_load(&p.tid, memory_order_acquire) != 0) + pthread_yield(); + } + if (attr == &myattr) + pthread_attr_destroy(&myattr); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_join, void *th, void **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) { + ThreadJoin(thr, pc, tid); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_detach, void *th) { + SCOPED_TSAN_INTERCEPTOR(pthread_detach, th); + int tid = ThreadTid(thr, pc, (uptr)th); + int res = REAL(pthread_detach)(th); + if (res == 0) { + ThreadDetach(thr, pc, tid); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) { + SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a); + int res = REAL(pthread_mutex_init)(m, a); + if (res == 0) { + bool recursive = false; + if (a) { + int type = 0; + if (pthread_mutexattr_gettype(a, &type) == 0) + recursive = (type == PTHREAD_MUTEX_RECURSIVE + || type == PTHREAD_MUTEX_RECURSIVE_NP); + } + MutexCreate(thr, pc, (uptr)m, false, recursive, false); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_mutex_destroy, m); + int res = REAL(pthread_mutex_destroy)(m); + if (res == 0 || res == EBUSY) { + MutexDestroy(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m); + int res = REAL(pthread_mutex_trylock)(m); + if (res == EOWNERDEAD) + MutexRepair(thr, pc, (uptr)m); + if (res == 0 || res == EOWNERDEAD) + MutexLock(thr, pc, (uptr)m); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { + SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime); + int res = REAL(pthread_mutex_timedlock)(m, abstime); + if (res == 0) { + MutexLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { + SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); + int res = REAL(pthread_spin_init)(m, pshared); + if (res == 0) { + MutexCreate(thr, pc, (uptr)m, false, false, false); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_spin_destroy, m); + int res = REAL(pthread_spin_destroy)(m); + if (res == 0) { + MutexDestroy(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m); + int res = REAL(pthread_spin_lock)(m); + if (res == 0) { + MutexLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m); + int res = REAL(pthread_spin_trylock)(m); + if (res == 0) { + MutexLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m); + MutexUnlock(thr, pc, (uptr)m); + int res = REAL(pthread_spin_unlock)(m); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a); + int res = REAL(pthread_rwlock_init)(m, a); + if (res == 0) { + MutexCreate(thr, pc, (uptr)m, true, false, false); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_destroy, m); + int res = REAL(pthread_rwlock_destroy)(m); + if (res == 0) { + MutexDestroy(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m); + int res = REAL(pthread_rwlock_rdlock)(m); + if (res == 0) { + MutexReadLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m); + int res = REAL(pthread_rwlock_tryrdlock)(m); + if (res == 0) { + MutexReadLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime); + int res = REAL(pthread_rwlock_timedrdlock)(m, abstime); + if (res == 0) { + MutexReadLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m); + int res = REAL(pthread_rwlock_wrlock)(m); + if (res == 0) { + MutexLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m); + int res = REAL(pthread_rwlock_trywrlock)(m); + if (res == 0) { + MutexLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime); + int res = REAL(pthread_rwlock_timedwrlock)(m, abstime); + if (res == 0) { + MutexLock(thr, pc, (uptr)m); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { + SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m); + MutexReadOrWriteUnlock(thr, pc, (uptr)m); + int res = REAL(pthread_rwlock_unlock)(m); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) { + SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c); + MemoryWrite(thr, pc, (uptr)c, kSizeLog1); + int res = REAL(pthread_cond_destroy)(c); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, + void *abstime) { + SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime); + MutexUnlock(thr, pc, (uptr)m); + MemoryRead(thr, pc, (uptr)c, kSizeLog1); + int res = REAL(pthread_cond_timedwait)(c, m, abstime); + MutexLock(thr, pc, (uptr)m); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) { + SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count); + MemoryWrite(thr, pc, (uptr)b, kSizeLog1); + int res = REAL(pthread_barrier_init)(b, a, count); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) { + SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b); + MemoryWrite(thr, pc, (uptr)b, kSizeLog1); + int res = REAL(pthread_barrier_destroy)(b); + return res; +} + +TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { + SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b); + Release(thr, pc, (uptr)b); + MemoryRead(thr, pc, (uptr)b, kSizeLog1); + int res = REAL(pthread_barrier_wait)(b); + MemoryRead(thr, pc, (uptr)b, kSizeLog1); + if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) { + Acquire(thr, pc, (uptr)b); + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { + SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); + // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib, + // the user callback must be executed with thr->in_rtl == 0. + if (o == 0 || f == 0) + return EINVAL; + atomic_uint32_t *a = static_cast(o); + u32 v = atomic_load(a, memory_order_acquire); + if (v == 0 && atomic_compare_exchange_strong(a, &v, 1, + memory_order_relaxed)) { + const int old_in_rtl = thr->in_rtl; + thr->in_rtl = 0; + (*f)(); + CHECK_EQ(thr->in_rtl, 0); + thr->in_rtl = old_in_rtl; + if (!thr->in_ignored_lib) + Release(thr, pc, (uptr)o); + atomic_store(a, 2, memory_order_release); + } else { + while (v != 2) { + pthread_yield(); + v = atomic_load(a, memory_order_acquire); + } + if (!thr->in_ignored_lib) + Acquire(thr, pc, (uptr)o); + } + return 0; +} + +TSAN_INTERCEPTOR(int, sem_init, void *s, int pshared, unsigned value) { + SCOPED_TSAN_INTERCEPTOR(sem_init, s, pshared, value); + int res = REAL(sem_init)(s, pshared, value); + return res; +} + +TSAN_INTERCEPTOR(int, sem_destroy, void *s) { + SCOPED_TSAN_INTERCEPTOR(sem_destroy, s); + int res = REAL(sem_destroy)(s); + return res; +} + +TSAN_INTERCEPTOR(int, sem_wait, void *s) { + SCOPED_TSAN_INTERCEPTOR(sem_wait, s); + int res = BLOCK_REAL(sem_wait)(s); + if (res == 0) { + Acquire(thr, pc, (uptr)s); + } + return res; +} + +TSAN_INTERCEPTOR(int, sem_trywait, void *s) { + SCOPED_TSAN_INTERCEPTOR(sem_trywait, s); + int res = BLOCK_REAL(sem_trywait)(s); + if (res == 0) { + Acquire(thr, pc, (uptr)s); + } + return res; +} + +TSAN_INTERCEPTOR(int, sem_timedwait, void *s, void *abstime) { + SCOPED_TSAN_INTERCEPTOR(sem_timedwait, s, abstime); + int res = BLOCK_REAL(sem_timedwait)(s, abstime); + if (res == 0) { + Acquire(thr, pc, (uptr)s); + } + return res; +} + +TSAN_INTERCEPTOR(int, sem_post, void *s) { + SCOPED_TSAN_INTERCEPTOR(sem_post, s); + Release(thr, pc, (uptr)s); + int res = REAL(sem_post)(s); + return res; +} + +TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) { + SCOPED_TSAN_INTERCEPTOR(sem_getvalue, s, sval); + int res = REAL(sem_getvalue)(s, sval); + if (res == 0) { + Acquire(thr, pc, (uptr)s); + } + return res; +} + +TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); + return REAL(__xstat)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf); + return REAL(__xstat)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); + return REAL(__xstat64)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); + return REAL(__xstat64)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); + return REAL(__lxstat)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf); + return REAL(__lxstat)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); + return REAL(__lxstat64)(version, path, buf); +} + +TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); + return REAL(__lxstat64)(0, path, buf); +} + +TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat)(version, fd, buf); +} + +TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat)(0, fd, buf); +} + +TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat64)(version, fd, buf); +} + +TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { + SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf); + if (fd > 0) + FdAccess(thr, pc, fd); + return REAL(__fxstat64)(0, fd, buf); +} + +TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { + SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode); + int fd = REAL(open)(name, flags, mode); + if (fd >= 0) + FdFileCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) { + SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode); + int fd = REAL(open64)(name, flags, mode); + if (fd >= 0) + FdFileCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, creat, const char *name, int mode) { + SCOPED_TSAN_INTERCEPTOR(creat, name, mode); + int fd = REAL(creat)(name, mode); + if (fd >= 0) + FdFileCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) { + SCOPED_TSAN_INTERCEPTOR(creat64, name, mode); + int fd = REAL(creat64)(name, mode); + if (fd >= 0) + FdFileCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, dup, int oldfd) { + SCOPED_TSAN_INTERCEPTOR(dup, oldfd); + int newfd = REAL(dup)(oldfd); + if (oldfd >= 0 && newfd >= 0 && newfd != oldfd) + FdDup(thr, pc, oldfd, newfd); + return newfd; +} + +TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) { + SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd); + int newfd2 = REAL(dup2)(oldfd, newfd); + if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd) + FdDup(thr, pc, oldfd, newfd2); + return newfd2; +} + +TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) { + SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags); + int newfd2 = REAL(dup3)(oldfd, newfd, flags); + if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd) + FdDup(thr, pc, oldfd, newfd2); + return newfd2; +} + +TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) { + SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags); + int fd = REAL(eventfd)(initval, flags); + if (fd >= 0) + FdEventCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) { + SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags); + if (fd >= 0) + FdClose(thr, pc, fd); + fd = REAL(signalfd)(fd, mask, flags); + if (fd >= 0) + FdSignalCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, inotify_init, int fake) { + SCOPED_TSAN_INTERCEPTOR(inotify_init, fake); + int fd = REAL(inotify_init)(fake); + if (fd >= 0) + FdInotifyCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, inotify_init1, int flags) { + SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags); + int fd = REAL(inotify_init1)(flags); + if (fd >= 0) + FdInotifyCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) { + SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol); + int fd = REAL(socket)(domain, type, protocol); + if (fd >= 0) + FdSocketCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int *fd) { + SCOPED_TSAN_INTERCEPTOR(socketpair, domain, type, protocol, fd); + int res = REAL(socketpair)(domain, type, protocol, fd); + if (res == 0 && fd[0] >= 0 && fd[1] >= 0) + FdPipeCreate(thr, pc, fd[0], fd[1]); + return res; +} + +TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) { + SCOPED_TSAN_INTERCEPTOR(connect, fd, addr, addrlen); + FdSocketConnecting(thr, pc, fd); + int res = REAL(connect)(fd, addr, addrlen); + if (res == 0 && fd >= 0) + FdSocketConnect(thr, pc, fd); + return res; +} + +TSAN_INTERCEPTOR(int, bind, int fd, void *addr, unsigned addrlen) { + SCOPED_TSAN_INTERCEPTOR(bind, fd, addr, addrlen); + int res = REAL(bind)(fd, addr, addrlen); + if (fd > 0 && res == 0) + FdAccess(thr, pc, fd); + return res; +} + +TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { + SCOPED_TSAN_INTERCEPTOR(listen, fd, backlog); + int res = REAL(listen)(fd, backlog); + if (fd > 0 && res == 0) + FdAccess(thr, pc, fd); + return res; +} + +TSAN_INTERCEPTOR(int, epoll_create, int size) { + SCOPED_TSAN_INTERCEPTOR(epoll_create, size); + int fd = REAL(epoll_create)(size); + if (fd >= 0) + FdPollCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, epoll_create1, int flags) { + SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags); + int fd = REAL(epoll_create1)(flags); + if (fd >= 0) + FdPollCreate(thr, pc, fd); + return fd; +} + +TSAN_INTERCEPTOR(int, close, int fd) { + SCOPED_TSAN_INTERCEPTOR(close, fd); + if (fd >= 0) + FdClose(thr, pc, fd); + return REAL(close)(fd); +} + +TSAN_INTERCEPTOR(int, __close, int fd) { + SCOPED_TSAN_INTERCEPTOR(__close, fd); + if (fd >= 0) + FdClose(thr, pc, fd); + return REAL(__close)(fd); +} + +// glibc guts +TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) { + SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr); + int fds[64]; + int cnt = ExtractResolvFDs(state, fds, ARRAY_SIZE(fds)); + for (int i = 0; i < cnt; i++) { + if (fds[i] > 0) + FdClose(thr, pc, fds[i]); + } + REAL(__res_iclose)(state, free_addr); +} + +TSAN_INTERCEPTOR(int, pipe, int *pipefd) { + SCOPED_TSAN_INTERCEPTOR(pipe, pipefd); + int res = REAL(pipe)(pipefd); + if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0) + FdPipeCreate(thr, pc, pipefd[0], pipefd[1]); + return res; +} + +TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { + SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags); + int res = REAL(pipe2)(pipefd, flags); + if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0) + FdPipeCreate(thr, pc, pipefd[0], pipefd[1]); + return res; +} + +TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { + SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); + if (fd >= 0) { + FdAccess(thr, pc, fd); + FdRelease(thr, pc, fd); + } + int res = REAL(send)(fd, buf, len, flags); + return res; +} + +TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) { + SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags); + if (fd >= 0) { + FdAccess(thr, pc, fd); + FdRelease(thr, pc, fd); + } + int res = REAL(sendmsg)(fd, msg, flags); + return res; +} + +TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { + SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags); + if (fd >= 0) + FdAccess(thr, pc, fd); + int res = REAL(recv)(fd, buf, len, flags); + if (res >= 0 && fd >= 0) { + FdAcquire(thr, pc, fd); + } + return res; +} + +TSAN_INTERCEPTOR(int, unlink, char *path) { + SCOPED_TSAN_INTERCEPTOR(unlink, path); + Release(thr, pc, File2addr(path)); + int res = REAL(unlink)(path); + return res; +} + +TSAN_INTERCEPTOR(void*, fopen, char *path, char *mode) { + SCOPED_TSAN_INTERCEPTOR(fopen, path, mode); + void *res = REAL(fopen)(path, mode); + Acquire(thr, pc, File2addr(path)); + if (res) { + int fd = fileno_unlocked(res); + if (fd >= 0) + FdFileCreate(thr, pc, fd); + } + return res; +} + +TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) { + SCOPED_TSAN_INTERCEPTOR(freopen, path, mode, stream); + if (stream) { + int fd = fileno_unlocked(stream); + if (fd >= 0) + FdClose(thr, pc, fd); + } + void *res = REAL(freopen)(path, mode, stream); + Acquire(thr, pc, File2addr(path)); + if (res) { + int fd = fileno_unlocked(res); + if (fd >= 0) + FdFileCreate(thr, pc, fd); + } + return res; +} + +TSAN_INTERCEPTOR(int, fclose, void *stream) { + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fclose, stream); + if (stream) { + int fd = fileno_unlocked(stream); + if (fd >= 0) + FdClose(thr, pc, fd); + } + } + return REAL(fclose)(stream); +} + +TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f); + MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true); + } + return REAL(fread)(ptr, size, nmemb, f); +} + +TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f); + MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false); + } + return REAL(fwrite)(p, size, nmemb, f); +} + +TSAN_INTERCEPTOR(int, fflush, void *stream) { + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fflush, stream); + } + return REAL(fflush)(stream); +} + +TSAN_INTERCEPTOR(void, abort, int fake) { + SCOPED_TSAN_INTERCEPTOR(abort, fake); + REAL(fflush)(0); + REAL(abort)(fake); +} + +TSAN_INTERCEPTOR(int, puts, const char *s) { + SCOPED_TSAN_INTERCEPTOR(puts, s); + MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s), false); + return REAL(puts)(s); +} + +TSAN_INTERCEPTOR(int, rmdir, char *path) { + SCOPED_TSAN_INTERCEPTOR(rmdir, path); + Release(thr, pc, Dir2addr(path)); + int res = REAL(rmdir)(path); + return res; +} + +TSAN_INTERCEPTOR(void*, opendir, char *path) { + SCOPED_TSAN_INTERCEPTOR(opendir, path); + void *res = REAL(opendir)(path); + if (res != 0) + Acquire(thr, pc, Dir2addr(path)); + return res; +} + +TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { + SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); + if (epfd >= 0) + FdAccess(thr, pc, epfd); + if (epfd >= 0 && fd >= 0) + FdAccess(thr, pc, fd); + if (op == EPOLL_CTL_ADD && epfd >= 0) + FdRelease(thr, pc, epfd); + int res = REAL(epoll_ctl)(epfd, op, fd, ev); + return res; +} + +TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { + SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout); + if (epfd >= 0) + FdAccess(thr, pc, epfd); + int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout); + if (res > 0 && epfd >= 0) + FdAcquire(thr, pc, epfd); + return res; +} + +void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, + my_siginfo_t *info, void *ctx) { + ThreadState *thr = cur_thread(); + SignalContext *sctx = SigCtx(thr); + // Don't mess with synchronous signals. + if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || + sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS || + // If we are sending signal to ourselves, we must process it now. + (sctx && sig == sctx->int_signal_send) || + // If we are in blocking function, we can safely process it now + // (but check if we are in a recursive interceptor, + // i.e. pthread_join()->munmap()). + (sctx && sctx->in_blocking_func == 1 && thr->in_rtl == 1)) { + int in_rtl = thr->in_rtl; + thr->in_rtl = 0; + CHECK_EQ(thr->in_signal_handler, false); + thr->in_signal_handler = true; + if (sigact) + sigactions[sig].sa_sigaction(sig, info, ctx); + else + sigactions[sig].sa_handler(sig); + CHECK_EQ(thr->in_signal_handler, true); + thr->in_signal_handler = false; + thr->in_rtl = in_rtl; + return; + } + + if (sctx == 0) + return; + SignalDesc *signal = &sctx->pending_signals[sig]; + if (signal->armed == false) { + signal->armed = true; + signal->sigaction = sigact; + if (info) + internal_memcpy(&signal->siginfo, info, sizeof(*info)); + if (ctx) + internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx)); + sctx->pending_signal_count++; + } +} + +static void rtl_sighandler(int sig) { + rtl_generic_sighandler(false, sig, 0, 0); +} + +static void rtl_sigaction(int sig, my_siginfo_t *info, void *ctx) { + rtl_generic_sighandler(true, sig, info, ctx); +} + +TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { + SCOPED_TSAN_INTERCEPTOR(sigaction, sig, act, old); + if (old) + internal_memcpy(old, &sigactions[sig], sizeof(*old)); + if (act == 0) + return 0; + internal_memcpy(&sigactions[sig], act, sizeof(*act)); + sigaction_t newact; + internal_memcpy(&newact, act, sizeof(newact)); + REAL(sigfillset)(&newact.sa_mask); + if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) { + if (newact.sa_flags & SA_SIGINFO) + newact.sa_sigaction = rtl_sigaction; + else + newact.sa_handler = rtl_sighandler; + } + int res = REAL(sigaction)(sig, &newact, 0); + return res; +} + +TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) { + sigaction_t act; + act.sa_handler = h; + REAL(memset)(&act.sa_mask, -1, sizeof(act.sa_mask)); + act.sa_flags = 0; + sigaction_t old; + int res = sigaction(sig, &act, &old); + if (res) + return SIG_ERR; + return old.sa_handler; +} + +TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { + SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); + return REAL(sigsuspend)(mask); +} + +TSAN_INTERCEPTOR(int, raise, int sig) { + SCOPED_TSAN_INTERCEPTOR(raise, sig); + SignalContext *sctx = SigCtx(thr); + CHECK_NE(sctx, 0); + int prev = sctx->int_signal_send; + sctx->int_signal_send = sig; + int res = REAL(raise)(sig); + CHECK_EQ(sctx->int_signal_send, sig); + sctx->int_signal_send = prev; + return res; +} + +TSAN_INTERCEPTOR(int, kill, int pid, int sig) { + SCOPED_TSAN_INTERCEPTOR(kill, pid, sig); + SignalContext *sctx = SigCtx(thr); + CHECK_NE(sctx, 0); + int prev = sctx->int_signal_send; + if (pid == (int)internal_getpid()) { + sctx->int_signal_send = sig; + } + int res = REAL(kill)(pid, sig); + if (pid == (int)internal_getpid()) { + CHECK_EQ(sctx->int_signal_send, sig); + sctx->int_signal_send = prev; + } + return res; +} + +TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) { + SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig); + SignalContext *sctx = SigCtx(thr); + CHECK_NE(sctx, 0); + int prev = sctx->int_signal_send; + if (tid == pthread_self()) { + sctx->int_signal_send = sig; + } + int res = REAL(pthread_kill)(tid, sig); + if (tid == pthread_self()) { + CHECK_EQ(sctx->int_signal_send, sig); + sctx->int_signal_send = prev; + } + return res; +} + +TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { + SCOPED_TSAN_INTERCEPTOR(gettimeofday, tv, tz); + // It's intercepted merely to process pending signals. + return REAL(gettimeofday)(tv, tz); +} + +TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service, + void *hints, void *rv) { + SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv); + // We miss atomic synchronization in getaddrinfo, + // and can report false race between malloc and free + // inside of getaddrinfo. So ignore memory accesses. + ThreadIgnoreBegin(thr); + // getaddrinfo calls fopen, which can be intercepted by user. + thr->in_rtl--; + CHECK_EQ(thr->in_rtl, 0); + int res = REAL(getaddrinfo)(node, service, hints, rv); + thr->in_rtl++; + ThreadIgnoreEnd(thr); + return res; +} + +// Linux kernel has a bug that leads to kernel deadlock if a process +// maps TBs of memory and then calls mlock(). +static void MlockIsUnsupported() { + static atomic_uint8_t printed; + if (atomic_exchange(&printed, 1, memory_order_relaxed)) + return; + if (flags()->verbosity > 0) + Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); +} + +TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +TSAN_INTERCEPTOR(int, munlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +TSAN_INTERCEPTOR(int, mlockall, int flags) { + MlockIsUnsupported(); + return 0; +} + +TSAN_INTERCEPTOR(int, munlockall, void) { + MlockIsUnsupported(); + return 0; +} + +TSAN_INTERCEPTOR(int, fork, int fake) { + SCOPED_INTERCEPTOR_RAW(fork, fake); + int pid = REAL(fork)(fake); + if (pid == 0) { + // child + FdOnFork(thr, pc); + } else if (pid > 0) { + // parent + } + return pid; +} + +static int OnExit(ThreadState *thr) { + int status = Finalize(thr); + REAL(fflush)(0); + return status; +} + +struct TsanInterceptorContext { + ThreadState *thr; + const uptr caller_pc; + const uptr pc; +}; + +#include "sanitizer_common/sanitizer_platform_interceptors.h" +// Causes interceptor recursion (getpwuid_r() calls fopen()) +#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS +#undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS +// Causes interceptor recursion (getaddrinfo() and fopen()) +#undef SANITIZER_INTERCEPT_GETADDRINFO +#undef SANITIZER_INTERCEPT_GETNAMEINFO +// Causes interceptor recursion (glob64() calls lstat64()) +#undef SANITIZER_INTERCEPT_GLOB + +#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) + +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \ + true) + +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \ + ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \ + false) + +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \ + TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ + ctx = (void *)&_ctx; \ + (void) ctx; + +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \ + FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd) + +#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ + ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name) + +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + CTX()->thread_registry->SetThreadNameByUserId(thread, name) + +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name) + +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \ + OnExit(((TsanInterceptorContext *) ctx)->thr) + +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ + MutexLock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ + MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \ + MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#include "sanitizer_common/sanitizer_common_interceptors.inc" + +#define TSAN_SYSCALL() \ + ThreadState *thr = cur_thread(); \ + ScopedSyscall scoped_syscall(thr) \ +/**/ + +struct ScopedSyscall { + ThreadState *thr; + + explicit ScopedSyscall(ThreadState *thr) + : thr(thr) { + if (thr->in_rtl == 0) + Initialize(thr); + thr->in_rtl++; + } + + ~ScopedSyscall() { + thr->in_rtl--; + if (thr->in_rtl == 0) + ProcessPendingSignals(thr); + } +}; + +static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { + TSAN_SYSCALL(); + MemoryAccessRange(thr, pc, p, s, write); +} + +static void syscall_fd_close(uptr pc, int fd) { + TSAN_SYSCALL(); + if (fd >= 0) + FdClose(thr, pc, fd); +} + +static void syscall_pre_fork(uptr pc) { + TSAN_SYSCALL(); +} + +static void syscall_post_fork(uptr pc, int res) { + TSAN_SYSCALL(); + if (res == 0) { + // child + FdOnFork(thr, pc); + } else if (res > 0) { + // parent + } +} + +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd) +#define COMMON_SYSCALL_PRE_FORK() \ + syscall_pre_fork(GET_CALLER_PC()) +#define COMMON_SYSCALL_POST_FORK(res) \ + syscall_post_fork(GET_CALLER_PC(), res) +#include "sanitizer_common/sanitizer_common_syscalls.inc" + +namespace __tsan { + +static void finalize(void *arg) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + atexit_ctx->exit(thr, pc); + int status = Finalize(thr); + REAL(fflush)(0); + if (status) + REAL(_exit)(status); +} + +void ProcessPendingSignals(ThreadState *thr) { + CHECK_EQ(thr->in_rtl, 0); + SignalContext *sctx = SigCtx(thr); + if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler) + return; + Context *ctx = CTX(); + thr->in_signal_handler = true; + sctx->pending_signal_count = 0; + // These are too big for stack. + static THREADLOCAL __sanitizer_sigset_t emptyset, oldset; + REAL(sigfillset)(&emptyset); + pthread_sigmask(SIG_SETMASK, &emptyset, &oldset); + for (int sig = 0; sig < kSigCount; sig++) { + SignalDesc *signal = &sctx->pending_signals[sig]; + if (signal->armed) { + signal->armed = false; + if (sigactions[sig].sa_handler != SIG_DFL + && sigactions[sig].sa_handler != SIG_IGN) { + // Insure that the handler does not spoil errno. + const int saved_errno = errno; + errno = 0; + if (signal->sigaction) + sigactions[sig].sa_sigaction(sig, &signal->siginfo, &signal->ctx); + else + sigactions[sig].sa_handler(sig); + if (flags()->report_bugs && errno != 0) { + ScopedInRtl in_rtl; + __tsan::StackTrace stack; + uptr pc = signal->sigaction ? + (uptr)sigactions[sig].sa_sigaction : + (uptr)sigactions[sig].sa_handler; + pc += 1; // return address is expected, OutputReport() will undo this + stack.Init(&pc, 1); + ThreadRegistryLock l(ctx->thread_registry); + ScopedReport rep(ReportTypeErrnoInSignal); + if (!IsFiredSuppression(ctx, rep, stack)) { + rep.AddStack(&stack); + OutputReport(ctx, rep, rep.GetReport()->stacks[0]); + } + } + errno = saved_errno; + } + } + } + pthread_sigmask(SIG_SETMASK, &oldset, 0); + CHECK_EQ(thr->in_signal_handler, true); + thr->in_signal_handler = false; +} + +static void unreachable() { + Printf("FATAL: ThreadSanitizer: unreachable called\n"); + Die(); +} + +void InitializeInterceptors() { + CHECK_GT(cur_thread()->in_rtl, 0); + + // We need to setup it early, because functions like dlsym() can call it. + REAL(memset) = internal_memset; + REAL(memcpy) = internal_memcpy; + REAL(memcmp) = internal_memcmp; + + // Instruct libc malloc to consume less memory. + mallopt(1, 0); // M_MXFAST + mallopt(-3, 32*1024); // M_MMAP_THRESHOLD + + SANITIZER_COMMON_INTERCEPTORS_INIT; + + TSAN_INTERCEPT(setjmp); + TSAN_INTERCEPT(_setjmp); + TSAN_INTERCEPT(sigsetjmp); + TSAN_INTERCEPT(__sigsetjmp); + TSAN_INTERCEPT(longjmp); + TSAN_INTERCEPT(siglongjmp); + + TSAN_INTERCEPT(malloc); + TSAN_INTERCEPT(__libc_memalign); + TSAN_INTERCEPT(calloc); + TSAN_INTERCEPT(realloc); + TSAN_INTERCEPT(free); + TSAN_INTERCEPT(cfree); + TSAN_INTERCEPT(mmap); + TSAN_INTERCEPT(mmap64); + TSAN_INTERCEPT(munmap); + TSAN_INTERCEPT(memalign); + TSAN_INTERCEPT(valloc); + TSAN_INTERCEPT(pvalloc); + TSAN_INTERCEPT(posix_memalign); + + TSAN_INTERCEPT(strlen); + TSAN_INTERCEPT(memset); + TSAN_INTERCEPT(memcpy); + TSAN_INTERCEPT(memchr); + TSAN_INTERCEPT(memrchr); + TSAN_INTERCEPT(memmove); + TSAN_INTERCEPT(memcmp); + TSAN_INTERCEPT(strchr); + TSAN_INTERCEPT(strchrnul); + TSAN_INTERCEPT(strrchr); + TSAN_INTERCEPT(strcpy); // NOLINT + TSAN_INTERCEPT(strncpy); + TSAN_INTERCEPT(strstr); + TSAN_INTERCEPT(strdup); + + TSAN_INTERCEPT(pthread_create); + TSAN_INTERCEPT(pthread_join); + TSAN_INTERCEPT(pthread_detach); + + TSAN_INTERCEPT(pthread_mutex_init); + TSAN_INTERCEPT(pthread_mutex_destroy); + TSAN_INTERCEPT(pthread_mutex_trylock); + TSAN_INTERCEPT(pthread_mutex_timedlock); + + TSAN_INTERCEPT(pthread_spin_init); + TSAN_INTERCEPT(pthread_spin_destroy); + TSAN_INTERCEPT(pthread_spin_lock); + TSAN_INTERCEPT(pthread_spin_trylock); + TSAN_INTERCEPT(pthread_spin_unlock); + + TSAN_INTERCEPT(pthread_rwlock_init); + TSAN_INTERCEPT(pthread_rwlock_destroy); + TSAN_INTERCEPT(pthread_rwlock_rdlock); + TSAN_INTERCEPT(pthread_rwlock_tryrdlock); + TSAN_INTERCEPT(pthread_rwlock_timedrdlock); + TSAN_INTERCEPT(pthread_rwlock_wrlock); + TSAN_INTERCEPT(pthread_rwlock_trywrlock); + TSAN_INTERCEPT(pthread_rwlock_timedwrlock); + TSAN_INTERCEPT(pthread_rwlock_unlock); + + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); + + TSAN_INTERCEPT(pthread_barrier_init); + TSAN_INTERCEPT(pthread_barrier_destroy); + TSAN_INTERCEPT(pthread_barrier_wait); + + TSAN_INTERCEPT(pthread_once); + + TSAN_INTERCEPT(sem_init); + TSAN_INTERCEPT(sem_destroy); + TSAN_INTERCEPT(sem_wait); + TSAN_INTERCEPT(sem_trywait); + TSAN_INTERCEPT(sem_timedwait); + TSAN_INTERCEPT(sem_post); + TSAN_INTERCEPT(sem_getvalue); + + TSAN_INTERCEPT(stat); + TSAN_INTERCEPT(__xstat); + TSAN_INTERCEPT(stat64); + TSAN_INTERCEPT(__xstat64); + TSAN_INTERCEPT(lstat); + TSAN_INTERCEPT(__lxstat); + TSAN_INTERCEPT(lstat64); + TSAN_INTERCEPT(__lxstat64); + TSAN_INTERCEPT(fstat); + TSAN_INTERCEPT(__fxstat); + TSAN_INTERCEPT(fstat64); + TSAN_INTERCEPT(__fxstat64); + TSAN_INTERCEPT(open); + TSAN_INTERCEPT(open64); + TSAN_INTERCEPT(creat); + TSAN_INTERCEPT(creat64); + TSAN_INTERCEPT(dup); + TSAN_INTERCEPT(dup2); + TSAN_INTERCEPT(dup3); + TSAN_INTERCEPT(eventfd); + TSAN_INTERCEPT(signalfd); + TSAN_INTERCEPT(inotify_init); + TSAN_INTERCEPT(inotify_init1); + TSAN_INTERCEPT(socket); + TSAN_INTERCEPT(socketpair); + TSAN_INTERCEPT(connect); + TSAN_INTERCEPT(bind); + TSAN_INTERCEPT(listen); + TSAN_INTERCEPT(epoll_create); + TSAN_INTERCEPT(epoll_create1); + TSAN_INTERCEPT(close); + TSAN_INTERCEPT(__close); + TSAN_INTERCEPT(__res_iclose); + TSAN_INTERCEPT(pipe); + TSAN_INTERCEPT(pipe2); + + TSAN_INTERCEPT(send); + TSAN_INTERCEPT(sendmsg); + TSAN_INTERCEPT(recv); + + TSAN_INTERCEPT(unlink); + TSAN_INTERCEPT(fopen); + TSAN_INTERCEPT(freopen); + TSAN_INTERCEPT(fclose); + TSAN_INTERCEPT(fread); + TSAN_INTERCEPT(fwrite); + TSAN_INTERCEPT(fflush); + TSAN_INTERCEPT(abort); + TSAN_INTERCEPT(puts); + TSAN_INTERCEPT(rmdir); + TSAN_INTERCEPT(opendir); + + TSAN_INTERCEPT(epoll_ctl); + TSAN_INTERCEPT(epoll_wait); + + TSAN_INTERCEPT(sigaction); + TSAN_INTERCEPT(signal); + TSAN_INTERCEPT(sigsuspend); + TSAN_INTERCEPT(raise); + TSAN_INTERCEPT(kill); + TSAN_INTERCEPT(pthread_kill); + TSAN_INTERCEPT(sleep); + TSAN_INTERCEPT(usleep); + TSAN_INTERCEPT(nanosleep); + TSAN_INTERCEPT(gettimeofday); + TSAN_INTERCEPT(getaddrinfo); + + TSAN_INTERCEPT(mlock); + TSAN_INTERCEPT(munlock); + TSAN_INTERCEPT(mlockall); + TSAN_INTERCEPT(munlockall); + + TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(dlopen); + TSAN_INTERCEPT(dlclose); + TSAN_INTERCEPT(on_exit); + TSAN_INTERCEPT(__cxa_atexit); + TSAN_INTERCEPT(_exit); + + // Need to setup it, because interceptors check that the function is resolved. + // But atexit is emitted directly into the module, so can't be resolved. + REAL(atexit) = (int(*)(void(*)()))unreachable; + atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext))) + AtExitContext(); + + if (REAL(__cxa_atexit)(&finalize, 0, 0)) { + Printf("ThreadSanitizer: failed to setup atexit callback\n"); + Die(); + } + + if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { + Printf("ThreadSanitizer: failed to create thread key\n"); + Die(); + } + + FdInit(); +} + +void internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal users + // signals. + __sanitizer_kernel_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); + void *th; + REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); + REAL(pthread_detach)(th); + internal_sigprocmask(SIG_SETMASK, &old, 0); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_interface.cc new file mode 100644 index 00000000..9de3808e --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface.cc @@ -0,0 +1,98 @@ +//===-- tsan_interface.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "tsan_interface.h" +#include "tsan_interface_ann.h" +#include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +#define CALLERPC ((uptr)__builtin_return_address(0)) + +using namespace __tsan; // NOLINT + +typedef u16 uint16_t; +typedef u32 uint32_t; +typedef u64 uint64_t; + +void __tsan_init() { + Initialize(cur_thread()); +} + +void __tsan_read16(void *addr) { + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); +} + +void __tsan_write16(void *addr) { + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); +} + +u16 __tsan_unaligned_read2(const uu16 *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false); + return *addr; +} + +u32 __tsan_unaligned_read4(const uu32 *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false); + return *addr; +} + +u64 __tsan_unaligned_read8(const uu64 *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false); + return *addr; +} + +void __tsan_unaligned_write2(uu16 *addr, u16 v) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false); + *addr = v; +} + +void __tsan_unaligned_write4(uu32 *addr, u32 v) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false); + *addr = v; +} + +void __tsan_unaligned_write8(uu64 *addr, u64 v) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false); + *addr = v; +} + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +uint16_t __sanitizer_unaligned_load16(void *addr) + ALIAS("__tsan_unaligned_read2"); +SANITIZER_INTERFACE_ATTRIBUTE +uint32_t __sanitizer_unaligned_load32(void *addr) + ALIAS("__tsan_unaligned_read4"); +SANITIZER_INTERFACE_ATTRIBUTE +uint64_t __sanitizer_unaligned_load64(void *addr) + ALIAS("__tsan_unaligned_read8"); +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(void *addr, uint16_t v) + ALIAS("__tsan_unaligned_write2"); +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(void *addr, uint32_t v) + ALIAS("__tsan_unaligned_write4"); +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(void *addr, uint64_t v) + ALIAS("__tsan_unaligned_write8"); +} + +void __tsan_acquire(void *addr) { + Acquire(cur_thread(), CALLERPC, (uptr)addr); +} + +void __tsan_release(void *addr) { + Release(cur_thread(), CALLERPC, (uptr)addr); +} diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface.h b/projects/compiler-rt/lib/tsan/rtl/tsan_interface.h new file mode 100644 index 00000000..70450697 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface.h @@ -0,0 +1,67 @@ +//===-- tsan_interface.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// The functions declared in this header will be inserted by the instrumentation +// module. +// This header can be included by the instrumented program or by TSan tests. +//===----------------------------------------------------------------------===// +#ifndef TSAN_INTERFACE_H +#define TSAN_INTERFACE_H + +#include + +// This header should NOT include any other headers. +// All functions in this header are extern "C" and start with __tsan_. + +#ifdef __cplusplus +extern "C" { +#endif + +// This function should be called at the very beginning of the process, +// before any instrumented code is executed and before any call to malloc. +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_init(); + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16(void *addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr); + +SANITIZER_INTERFACE_ATTRIBUTE u16 __tsan_unaligned_read2(const uu16 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u32 __tsan_unaligned_read4(const uu32 *addr); +SANITIZER_INTERFACE_ATTRIBUTE u64 __tsan_unaligned_read8(const uu64 *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(uu16 *addr, u16 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(uu32 *addr, u32 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(uu64 *addr, u64 v); + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_vptr_update(void **vptr_p, void *new_val); + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_entry(void *call_pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_exit(); + +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_read_range(void *addr, unsigned long size); // NOLINT +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_write_range(void *addr, unsigned long size); // NOLINT + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TSAN_INTERFACE_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cc new file mode 100644 index 00000000..cacbc028 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cc @@ -0,0 +1,464 @@ +//===-- tsan_interface_ann.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "tsan_interface_ann.h" +#include "tsan_mutex.h" +#include "tsan_report.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" +#include "tsan_flags.h" +#include "tsan_platform.h" +#include "tsan_vector.h" + +#define CALLERPC ((uptr)__builtin_return_address(0)) + +using namespace __tsan; // NOLINT + +namespace __tsan { + +class ScopedAnnotation { + public: + ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l, + uptr pc) + : thr_(thr) + , in_rtl_(thr->in_rtl) { + CHECK_EQ(thr_->in_rtl, 0); + FuncEntry(thr_, pc); + thr_->in_rtl++; + DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l); + } + + ~ScopedAnnotation() { + thr_->in_rtl--; + CHECK_EQ(in_rtl_, thr_->in_rtl); + FuncExit(thr_); + } + private: + ThreadState *const thr_; + const int in_rtl_; +}; + +#define SCOPED_ANNOTATION(typ) \ + if (!flags()->enable_annotations) \ + return; \ + ThreadState *thr = cur_thread(); \ + const uptr pc = (uptr)__builtin_return_address(0); \ + StatInc(thr, StatAnnotation); \ + StatInc(thr, Stat##typ); \ + ScopedAnnotation sa(thr, __FUNCTION__, f, l, \ + (uptr)__builtin_return_address(0)); \ + (void)pc; \ +/**/ + +static const int kMaxDescLen = 128; + +struct ExpectRace { + ExpectRace *next; + ExpectRace *prev; + int hitcount; + int addcount; + uptr addr; + uptr size; + char *file; + int line; + char desc[kMaxDescLen]; +}; + +struct DynamicAnnContext { + Mutex mtx; + ExpectRace expect; + ExpectRace benign; + + DynamicAnnContext() + : mtx(MutexTypeAnnotations, StatMtxAnnotations) { + } +}; + +static DynamicAnnContext *dyn_ann_ctx; +static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)] ALIGNED(64); + +static void AddExpectRace(ExpectRace *list, + char *f, int l, uptr addr, uptr size, char *desc) { + ExpectRace *race = list->next; + for (; race != list; race = race->next) { + if (race->addr == addr && race->size == size) { + race->addcount++; + return; + } + } + race = (ExpectRace*)internal_alloc(MBlockExpectRace, sizeof(ExpectRace)); + race->addr = addr; + race->size = size; + race->file = f; + race->line = l; + race->desc[0] = 0; + race->hitcount = 0; + race->addcount = 1; + if (desc) { + int i = 0; + for (; i < kMaxDescLen - 1 && desc[i]; i++) + race->desc[i] = desc[i]; + race->desc[i] = 0; + } + race->prev = list; + race->next = list->next; + race->next->prev = race; + list->next = race; +} + +static ExpectRace *FindRace(ExpectRace *list, uptr addr, uptr size) { + for (ExpectRace *race = list->next; race != list; race = race->next) { + uptr maxbegin = max(race->addr, addr); + uptr minend = min(race->addr + race->size, addr + size); + if (maxbegin < minend) + return race; + } + return 0; +} + +static bool CheckContains(ExpectRace *list, uptr addr, uptr size) { + ExpectRace *race = FindRace(list, addr, size); + if (race == 0 && AlternativeAddress(addr)) + race = FindRace(list, AlternativeAddress(addr), size); + if (race == 0) + return false; + DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n", + race->desc, race->addr, (int)race->size, race->file, race->line); + race->hitcount++; + return true; +} + +static void InitList(ExpectRace *list) { + list->next = list; + list->prev = list; +} + +void InitializeDynamicAnnotations() { + dyn_ann_ctx = new(dyn_ann_ctx_placeholder) DynamicAnnContext; + InitList(&dyn_ann_ctx->expect); + InitList(&dyn_ann_ctx->benign); +} + +bool IsExpectedReport(uptr addr, uptr size) { + Lock lock(&dyn_ann_ctx->mtx); + if (CheckContains(&dyn_ann_ctx->expect, addr, size)) + return true; + if (CheckContains(&dyn_ann_ctx->benign, addr, size)) + return true; + return false; +} + +static void CollectMatchedBenignRaces(Vector *matched, + int *unique_count, int *hit_count, int ExpectRace::*counter) { + ExpectRace *list = &dyn_ann_ctx->benign; + for (ExpectRace *race = list->next; race != list; race = race->next) { + (*unique_count)++; + if (race->*counter == 0) + continue; + (*hit_count) += race->*counter; + uptr i = 0; + for (; i < matched->Size(); i++) { + ExpectRace *race0 = &(*matched)[i]; + if (race->line == race0->line + && internal_strcmp(race->file, race0->file) == 0 + && internal_strcmp(race->desc, race0->desc) == 0) { + race0->*counter += race->*counter; + break; + } + } + if (i == matched->Size()) + matched->PushBack(*race); + } +} + +void PrintMatchedBenignRaces() { + Lock lock(&dyn_ann_ctx->mtx); + int unique_count = 0; + int hit_count = 0; + int add_count = 0; + Vector hit_matched(MBlockScopedBuf); + CollectMatchedBenignRaces(&hit_matched, &unique_count, &hit_count, + &ExpectRace::hitcount); + Vector add_matched(MBlockScopedBuf); + CollectMatchedBenignRaces(&add_matched, &unique_count, &add_count, + &ExpectRace::addcount); + if (hit_matched.Size()) { + Printf("ThreadSanitizer: Matched %d \"benign\" races (pid=%d):\n", + hit_count, (int)internal_getpid()); + for (uptr i = 0; i < hit_matched.Size(); i++) { + Printf("%d %s:%d %s\n", + hit_matched[i].hitcount, hit_matched[i].file, + hit_matched[i].line, hit_matched[i].desc); + } + } + if (hit_matched.Size()) { + Printf("ThreadSanitizer: Annotated %d \"benign\" races, %d unique" + " (pid=%d):\n", + add_count, unique_count, (int)internal_getpid()); + for (uptr i = 0; i < add_matched.Size(); i++) { + Printf("%d %s:%d %s\n", + add_matched[i].addcount, add_matched[i].file, + add_matched[i].line, add_matched[i].desc); + } + } +} + +static void ReportMissedExpectedRace(ExpectRace *race) { + Printf("==================\n"); + Printf("WARNING: ThreadSanitizer: missed expected data race\n"); + Printf(" %s addr=%zx %s:%d\n", + race->desc, race->addr, race->file, race->line); + Printf("==================\n"); +} +} // namespace __tsan + +using namespace __tsan; // NOLINT + +extern "C" { +void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) { + SCOPED_ANNOTATION(AnnotateHappensBefore); + Release(thr, pc, addr); +} + +void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) { + SCOPED_ANNOTATION(AnnotateHappensAfter); + Acquire(thr, pc, addr); +} + +void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) { + SCOPED_ANNOTATION(AnnotateCondVarSignal); +} + +void INTERFACE_ATTRIBUTE AnnotateCondVarSignalAll(char *f, int l, uptr cv) { + SCOPED_ANNOTATION(AnnotateCondVarSignalAll); +} + +void INTERFACE_ATTRIBUTE AnnotateMutexIsNotPHB(char *f, int l, uptr mu) { + SCOPED_ANNOTATION(AnnotateMutexIsNotPHB); +} + +void INTERFACE_ATTRIBUTE AnnotateCondVarWait(char *f, int l, uptr cv, + uptr lock) { + SCOPED_ANNOTATION(AnnotateCondVarWait); +} + +void INTERFACE_ATTRIBUTE AnnotateRWLockCreate(char *f, int l, uptr m) { + SCOPED_ANNOTATION(AnnotateRWLockCreate); + MutexCreate(thr, pc, m, true, true, false); +} + +void INTERFACE_ATTRIBUTE AnnotateRWLockCreateStatic(char *f, int l, uptr m) { + SCOPED_ANNOTATION(AnnotateRWLockCreateStatic); + MutexCreate(thr, pc, m, true, true, true); +} + +void INTERFACE_ATTRIBUTE AnnotateRWLockDestroy(char *f, int l, uptr m) { + SCOPED_ANNOTATION(AnnotateRWLockDestroy); + MutexDestroy(thr, pc, m); +} + +void INTERFACE_ATTRIBUTE AnnotateRWLockAcquired(char *f, int l, uptr m, + uptr is_w) { + SCOPED_ANNOTATION(AnnotateRWLockAcquired); + if (is_w) + MutexLock(thr, pc, m); + else + MutexReadLock(thr, pc, m); +} + +void INTERFACE_ATTRIBUTE AnnotateRWLockReleased(char *f, int l, uptr m, + uptr is_w) { + SCOPED_ANNOTATION(AnnotateRWLockReleased); + if (is_w) + MutexUnlock(thr, pc, m); + else + MutexReadUnlock(thr, pc, m); +} + +void INTERFACE_ATTRIBUTE AnnotateTraceMemory(char *f, int l, uptr mem) { + SCOPED_ANNOTATION(AnnotateTraceMemory); +} + +void INTERFACE_ATTRIBUTE AnnotateFlushState(char *f, int l) { + SCOPED_ANNOTATION(AnnotateFlushState); +} + +void INTERFACE_ATTRIBUTE AnnotateNewMemory(char *f, int l, uptr mem, + uptr size) { + SCOPED_ANNOTATION(AnnotateNewMemory); +} + +void INTERFACE_ATTRIBUTE AnnotateNoOp(char *f, int l, uptr mem) { + SCOPED_ANNOTATION(AnnotateNoOp); +} + +void INTERFACE_ATTRIBUTE AnnotateFlushExpectedRaces(char *f, int l) { + SCOPED_ANNOTATION(AnnotateFlushExpectedRaces); + Lock lock(&dyn_ann_ctx->mtx); + while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) { + ExpectRace *race = dyn_ann_ctx->expect.next; + if (race->hitcount == 0) { + CTX()->nmissed_expected++; + ReportMissedExpectedRace(race); + } + race->prev->next = race->next; + race->next->prev = race->prev; + internal_free(race); + } +} + +void INTERFACE_ATTRIBUTE AnnotateEnableRaceDetection( + char *f, int l, int enable) { + SCOPED_ANNOTATION(AnnotateEnableRaceDetection); + // FIXME: Reconsider this functionality later. It may be irrelevant. +} + +void INTERFACE_ATTRIBUTE AnnotateMutexIsUsedAsCondVar( + char *f, int l, uptr mu) { + SCOPED_ANNOTATION(AnnotateMutexIsUsedAsCondVar); +} + +void INTERFACE_ATTRIBUTE AnnotatePCQGet( + char *f, int l, uptr pcq) { + SCOPED_ANNOTATION(AnnotatePCQGet); +} + +void INTERFACE_ATTRIBUTE AnnotatePCQPut( + char *f, int l, uptr pcq) { + SCOPED_ANNOTATION(AnnotatePCQPut); +} + +void INTERFACE_ATTRIBUTE AnnotatePCQDestroy( + char *f, int l, uptr pcq) { + SCOPED_ANNOTATION(AnnotatePCQDestroy); +} + +void INTERFACE_ATTRIBUTE AnnotatePCQCreate( + char *f, int l, uptr pcq) { + SCOPED_ANNOTATION(AnnotatePCQCreate); +} + +void INTERFACE_ATTRIBUTE AnnotateExpectRace( + char *f, int l, uptr mem, char *desc) { + SCOPED_ANNOTATION(AnnotateExpectRace); + Lock lock(&dyn_ann_ctx->mtx); + AddExpectRace(&dyn_ann_ctx->expect, + f, l, mem, 1, desc); + DPrintf("Add expected race: %s addr=%zx %s:%d\n", desc, mem, f, l); +} + +static void BenignRaceImpl( + char *f, int l, uptr mem, uptr size, char *desc) { + Lock lock(&dyn_ann_ctx->mtx); + AddExpectRace(&dyn_ann_ctx->benign, + f, l, mem, size, desc); + DPrintf("Add benign race: %s addr=%zx %s:%d\n", desc, mem, f, l); +} + +// FIXME: Turn it off later. WTF is benign race?1?? Go talk to Hans Boehm. +void INTERFACE_ATTRIBUTE AnnotateBenignRaceSized( + char *f, int l, uptr mem, uptr size, char *desc) { + SCOPED_ANNOTATION(AnnotateBenignRaceSized); + BenignRaceImpl(f, l, mem, size, desc); +} + +void INTERFACE_ATTRIBUTE AnnotateBenignRace( + char *f, int l, uptr mem, char *desc) { + SCOPED_ANNOTATION(AnnotateBenignRace); + BenignRaceImpl(f, l, mem, 1, desc); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin); + ThreadIgnoreBegin(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd); + ThreadIgnoreEnd(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin); + ThreadIgnoreBegin(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd); + ThreadIgnoreEnd(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin); + ThreadIgnoreSyncBegin(thr); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd); + ThreadIgnoreSyncEnd(thr); +} + +void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange( + char *f, int l, uptr addr, uptr size) { + SCOPED_ANNOTATION(AnnotatePublishMemoryRange); +} + +void INTERFACE_ATTRIBUTE AnnotateUnpublishMemoryRange( + char *f, int l, uptr addr, uptr size) { + SCOPED_ANNOTATION(AnnotateUnpublishMemoryRange); +} + +void INTERFACE_ATTRIBUTE AnnotateThreadName( + char *f, int l, char *name) { + SCOPED_ANNOTATION(AnnotateThreadName); + ThreadSetName(thr, name); +} + +// We deliberately omit the implementation of WTFAnnotateHappensBefore() and +// WTFAnnotateHappensAfter(). Those are being used by Webkit to annotate +// atomic operations, which should be handled by ThreadSanitizer correctly. +void INTERFACE_ATTRIBUTE WTFAnnotateHappensBefore(char *f, int l, uptr addr) { + SCOPED_ANNOTATION(AnnotateHappensBefore); +} + +void INTERFACE_ATTRIBUTE WTFAnnotateHappensAfter(char *f, int l, uptr addr) { + SCOPED_ANNOTATION(AnnotateHappensAfter); +} + +void INTERFACE_ATTRIBUTE WTFAnnotateBenignRaceSized( + char *f, int l, uptr mem, uptr sz, char *desc) { + SCOPED_ANNOTATION(AnnotateBenignRaceSized); + BenignRaceImpl(f, l, mem, 1, desc); +} + +int INTERFACE_ATTRIBUTE RunningOnValgrind() { + return flags()->running_on_valgrind; +} + +double __attribute__((weak)) INTERFACE_ATTRIBUTE ValgrindSlowdown(void) { + return 10.0; +} + +const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) { + if (internal_strcmp(query, "pure_happens_before") == 0) + return "1"; + else + return "0"; +} + +void INTERFACE_ATTRIBUTE +AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {} +} // extern "C" diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface_ann.h b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_ann.h new file mode 100644 index 00000000..963bcc55 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_ann.h @@ -0,0 +1,33 @@ +//===-- tsan_interface_ann.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Interface for dynamic annotations. +//===----------------------------------------------------------------------===// +#ifndef TSAN_INTERFACE_ANN_H +#define TSAN_INTERFACE_ANN_H + +#include + +// This header should NOT include any other headers. +// All functions in this header are extern "C" and start with __tsan_. + +#ifdef __cplusplus +extern "C" { +#endif + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_acquire(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_release(void *addr); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // TSAN_INTERFACE_ANN_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc new file mode 100644 index 00000000..d9f8cdf5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cc @@ -0,0 +1,679 @@ +//===-- tsan_interface_atomic.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +// ThreadSanitizer atomic operations are based on C++11/C1x standards. +// For background see C++11 standard. A slightly older, publically +// available draft of the standard (not entirely up-to-date, but close enough +// for casual browsing) is available here: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf +// The following page contains more background information: +// http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/ + +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "tsan_interface_atomic.h" +#include "tsan_flags.h" +#include "tsan_rtl.h" + +using namespace __tsan; // NOLINT + +#define SCOPED_ATOMIC(func, ...) \ + const uptr callpc = (uptr)__builtin_return_address(0); \ + uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ + mo = ConvertOrder(mo); \ + mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \ + ThreadState *const thr = cur_thread(); \ + AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \ + ScopedAtomic sa(thr, callpc, a, mo, __FUNCTION__); \ + return Atomic##func(thr, pc, __VA_ARGS__); \ +/**/ + +// Some shortcuts. +typedef __tsan_memory_order morder; +typedef __tsan_atomic8 a8; +typedef __tsan_atomic16 a16; +typedef __tsan_atomic32 a32; +typedef __tsan_atomic64 a64; +typedef __tsan_atomic128 a128; +const morder mo_relaxed = __tsan_memory_order_relaxed; +const morder mo_consume = __tsan_memory_order_consume; +const morder mo_acquire = __tsan_memory_order_acquire; +const morder mo_release = __tsan_memory_order_release; +const morder mo_acq_rel = __tsan_memory_order_acq_rel; +const morder mo_seq_cst = __tsan_memory_order_seq_cst; + +class ScopedAtomic { + public: + ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a, + morder mo, const char *func) + : thr_(thr) { + CHECK_EQ(thr_->in_rtl, 0); + ProcessPendingSignals(thr); + FuncEntry(thr_, pc); + DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo); + thr_->in_rtl++; + } + ~ScopedAtomic() { + thr_->in_rtl--; + CHECK_EQ(thr_->in_rtl, 0); + FuncExit(thr_); + } + private: + ThreadState *thr_; +}; + +static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) { + StatInc(thr, StatAtomic); + StatInc(thr, t); + StatInc(thr, size == 1 ? StatAtomic1 + : size == 2 ? StatAtomic2 + : size == 4 ? StatAtomic4 + : size == 8 ? StatAtomic8 + : StatAtomic16); + StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed + : mo == mo_consume ? StatAtomicConsume + : mo == mo_acquire ? StatAtomicAcquire + : mo == mo_release ? StatAtomicRelease + : mo == mo_acq_rel ? StatAtomicAcq_Rel + : StatAtomicSeq_Cst); +} + +static bool IsLoadOrder(morder mo) { + return mo == mo_relaxed || mo == mo_consume + || mo == mo_acquire || mo == mo_seq_cst; +} + +static bool IsStoreOrder(morder mo) { + return mo == mo_relaxed || mo == mo_release || mo == mo_seq_cst; +} + +static bool IsReleaseOrder(morder mo) { + return mo == mo_release || mo == mo_acq_rel || mo == mo_seq_cst; +} + +static bool IsAcquireOrder(morder mo) { + return mo == mo_consume || mo == mo_acquire + || mo == mo_acq_rel || mo == mo_seq_cst; +} + +static bool IsAcqRelOrder(morder mo) { + return mo == mo_acq_rel || mo == mo_seq_cst; +} + +static morder ConvertOrder(morder mo) { + if (mo > (morder)100500) { + mo = morder(mo - 100500); + if (mo == morder(1 << 0)) + mo = mo_relaxed; + else if (mo == morder(1 << 1)) + mo = mo_consume; + else if (mo == morder(1 << 2)) + mo = mo_acquire; + else if (mo == morder(1 << 3)) + mo = mo_release; + else if (mo == morder(1 << 4)) + mo = mo_acq_rel; + else if (mo == morder(1 << 5)) + mo = mo_seq_cst; + } + CHECK_GE(mo, mo_relaxed); + CHECK_LE(mo, mo_seq_cst); + return mo; +} + +template T func_xchg(volatile T *v, T op) { + T res = __sync_lock_test_and_set(v, op); + // __sync_lock_test_and_set does not contain full barrier. + __sync_synchronize(); + return res; +} + +template T func_add(volatile T *v, T op) { + return __sync_fetch_and_add(v, op); +} + +template T func_sub(volatile T *v, T op) { + return __sync_fetch_and_sub(v, op); +} + +template T func_and(volatile T *v, T op) { + return __sync_fetch_and_and(v, op); +} + +template T func_or(volatile T *v, T op) { + return __sync_fetch_and_or(v, op); +} + +template T func_xor(volatile T *v, T op) { + return __sync_fetch_and_xor(v, op); +} + +template T func_nand(volatile T *v, T op) { + // clang does not support __sync_fetch_and_nand. + T cmp = *v; + for (;;) { + T newv = ~(cmp & op); + T cur = __sync_val_compare_and_swap(v, cmp, newv); + if (cmp == cur) + return cmp; + cmp = cur; + } +} + +template T func_cas(volatile T *v, T cmp, T xch) { + return __sync_val_compare_and_swap(v, cmp, xch); +} + +// clang does not support 128-bit atomic ops. +// Atomic ops are executed under tsan internal mutex, +// here we assume that the atomic variables are not accessed +// from non-instrumented code. +#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16 +a128 func_xchg(volatile a128 *v, a128 op) { + a128 cmp = *v; + *v = op; + return cmp; +} + +a128 func_add(volatile a128 *v, a128 op) { + a128 cmp = *v; + *v = cmp + op; + return cmp; +} + +a128 func_sub(volatile a128 *v, a128 op) { + a128 cmp = *v; + *v = cmp - op; + return cmp; +} + +a128 func_and(volatile a128 *v, a128 op) { + a128 cmp = *v; + *v = cmp & op; + return cmp; +} + +a128 func_or(volatile a128 *v, a128 op) { + a128 cmp = *v; + *v = cmp | op; + return cmp; +} + +a128 func_xor(volatile a128 *v, a128 op) { + a128 cmp = *v; + *v = cmp ^ op; + return cmp; +} + +a128 func_nand(volatile a128 *v, a128 op) { + a128 cmp = *v; + *v = ~(cmp & op); + return cmp; +} + +a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) { + a128 cur = *v; + if (cur == cmp) + *v = xch; + return cur; +} +#endif + +template +static int SizeLog() { + if (sizeof(T) <= 1) + return kSizeLog1; + else if (sizeof(T) <= 2) + return kSizeLog2; + else if (sizeof(T) <= 4) + return kSizeLog4; + else + return kSizeLog8; + // For 16-byte atomics we also use 8-byte memory access, + // this leads to false negatives only in very obscure cases. +} + +template +static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, + morder mo) { + CHECK(IsLoadOrder(mo)); + // This fast-path is critical for performance. + // Assume the access is atomic. + if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) { + MemoryReadAtomic(thr, pc, (uptr)a, SizeLog()); + return *a; // as if atomic + } + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false); + AcquireImpl(thr, pc, &s->clock); + T v = *a; + s->mtx.ReadUnlock(); + __sync_synchronize(); + MemoryReadAtomic(thr, pc, (uptr)a, SizeLog()); + return v; +} + +template +static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + CHECK(IsStoreOrder(mo)); + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog()); + // This fast-path is critical for performance. + // Assume the access is atomic. + // Strictly saying even relaxed store cuts off release sequence, + // so must reset the clock. + if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) { + *a = v; // as if atomic + return; + } + __sync_synchronize(); + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); + *a = v; + s->mtx.Unlock(); + // Trainling memory barrier to provide sequential consistency + // for Dekker-like store-load synchronization. + __sync_synchronize(); +} + +template +static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog()); + SyncVar *s = 0; + if (mo != mo_relaxed) { + s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + if (IsAcqRelOrder(mo)) + AcquireReleaseImpl(thr, pc, &s->clock); + else if (IsReleaseOrder(mo)) + ReleaseImpl(thr, pc, &s->clock); + else if (IsAcquireOrder(mo)) + AcquireImpl(thr, pc, &s->clock); + } + v = F(a, v); + if (s) + s->mtx.Unlock(); + return v; +} + +template +static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + return AtomicRMW(thr, pc, a, v, mo); +} + +template +static T AtomicFetchAdd(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + return AtomicRMW(thr, pc, a, v, mo); +} + +template +static T AtomicFetchSub(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + return AtomicRMW(thr, pc, a, v, mo); +} + +template +static T AtomicFetchAnd(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + return AtomicRMW(thr, pc, a, v, mo); +} + +template +static T AtomicFetchOr(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + return AtomicRMW(thr, pc, a, v, mo); +} + +template +static T AtomicFetchXor(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + return AtomicRMW(thr, pc, a, v, mo); +} + +template +static T AtomicFetchNand(ThreadState *thr, uptr pc, volatile T *a, T v, + morder mo) { + return AtomicRMW(thr, pc, a, v, mo); +} + +template +static bool AtomicCAS(ThreadState *thr, uptr pc, + volatile T *a, T *c, T v, morder mo, morder fmo) { + (void)fmo; // Unused because llvm does not pass it yet. + MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog()); + SyncVar *s = 0; + if (mo != mo_relaxed) { + s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + if (IsAcqRelOrder(mo)) + AcquireReleaseImpl(thr, pc, &s->clock); + else if (IsReleaseOrder(mo)) + ReleaseImpl(thr, pc, &s->clock); + else if (IsAcquireOrder(mo)) + AcquireImpl(thr, pc, &s->clock); + } + T cc = *c; + T pr = func_cas(a, cc, v); + if (s) + s->mtx.Unlock(); + if (pr == cc) + return true; + *c = pr; + return false; +} + +template +static T AtomicCAS(ThreadState *thr, uptr pc, + volatile T *a, T c, T v, morder mo, morder fmo) { + AtomicCAS(thr, pc, a, &c, v, mo, fmo); + return c; +} + +static void AtomicFence(ThreadState *thr, uptr pc, morder mo) { + // FIXME(dvyukov): not implemented. + __sync_synchronize(); +} + +a8 __tsan_atomic8_load(const volatile a8 *a, morder mo) { + SCOPED_ATOMIC(Load, a, mo); +} + +a16 __tsan_atomic16_load(const volatile a16 *a, morder mo) { + SCOPED_ATOMIC(Load, a, mo); +} + +a32 __tsan_atomic32_load(const volatile a32 *a, morder mo) { + SCOPED_ATOMIC(Load, a, mo); +} + +a64 __tsan_atomic64_load(const volatile a64 *a, morder mo) { + SCOPED_ATOMIC(Load, a, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_load(const volatile a128 *a, morder mo) { + SCOPED_ATOMIC(Load, a, mo); +} +#endif + +void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(Store, a, v, mo); +} + +void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(Store, a, v, mo); +} + +void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(Store, a, v, mo); +} + +void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(Store, a, v, mo); +} + +#if __TSAN_HAS_INT128 +void __tsan_atomic128_store(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(Store, a, v, mo); +} +#endif + +a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(Exchange, a, v, mo); +} + +a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(Exchange, a, v, mo); +} + +a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(Exchange, a, v, mo); +} + +a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(Exchange, a, v, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(Exchange, a, v, mo); +} +#endif + +a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(FetchAdd, a, v, mo); +} + +a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(FetchAdd, a, v, mo); +} + +a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(FetchAdd, a, v, mo); +} + +a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(FetchAdd, a, v, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(FetchAdd, a, v, mo); +} +#endif + +a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(FetchSub, a, v, mo); +} + +a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(FetchSub, a, v, mo); +} + +a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(FetchSub, a, v, mo); +} + +a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(FetchSub, a, v, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(FetchSub, a, v, mo); +} +#endif + +a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(FetchAnd, a, v, mo); +} + +a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(FetchAnd, a, v, mo); +} + +a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(FetchAnd, a, v, mo); +} + +a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(FetchAnd, a, v, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(FetchAnd, a, v, mo); +} +#endif + +a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(FetchOr, a, v, mo); +} + +a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(FetchOr, a, v, mo); +} + +a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(FetchOr, a, v, mo); +} + +a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(FetchOr, a, v, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(FetchOr, a, v, mo); +} +#endif + +a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(FetchXor, a, v, mo); +} + +a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(FetchXor, a, v, mo); +} + +a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(FetchXor, a, v, mo); +} + +a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(FetchXor, a, v, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(FetchXor, a, v, mo); +} +#endif + +a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, morder mo) { + SCOPED_ATOMIC(FetchNand, a, v, mo); +} + +a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, morder mo) { + SCOPED_ATOMIC(FetchNand, a, v, mo); +} + +a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, morder mo) { + SCOPED_ATOMIC(FetchNand, a, v, mo); +} + +a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, morder mo) { + SCOPED_ATOMIC(FetchNand, a, v, mo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, morder mo) { + SCOPED_ATOMIC(FetchNand, a, v, mo); +} +#endif + +int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +#if __TSAN_HAS_INT128 +int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} +#endif + +int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +#if __TSAN_HAS_INT128 +int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} +#endif + +a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} +a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} + +#if __TSAN_HAS_INT128 +a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v, + morder mo, morder fmo) { + SCOPED_ATOMIC(CAS, a, c, v, mo, fmo); +} +#endif + +void __tsan_atomic_thread_fence(morder mo) { + char* a = 0; + SCOPED_ATOMIC(Fence, mo); +} + +void __tsan_atomic_signal_fence(morder mo) { +} diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.h b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.h new file mode 100644 index 00000000..5352d566 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.h @@ -0,0 +1,205 @@ +//===-- tsan_interface_atomic.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_INTERFACE_ATOMIC_H +#define TSAN_INTERFACE_ATOMIC_H + +#ifndef INTERFACE_ATTRIBUTE +# define INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char __tsan_atomic8; +typedef short __tsan_atomic16; // NOLINT +typedef int __tsan_atomic32; +typedef long __tsan_atomic64; // NOLINT + +#if defined(__SIZEOF_INT128__) \ + || (__clang_major__ * 100 + __clang_minor__ >= 302) +__extension__ typedef __int128 __tsan_atomic128; +#define __TSAN_HAS_INT128 1 +#else +typedef char __tsan_atomic128; +#define __TSAN_HAS_INT128 0 +#endif + +// Part of ABI, do not change. +// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup +typedef enum { + __tsan_memory_order_relaxed, + __tsan_memory_order_consume, + __tsan_memory_order_acquire, + __tsan_memory_order_release, + __tsan_memory_order_acq_rel, + __tsan_memory_order_seq_cst +} __tsan_memory_order; + +__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_load(const volatile __tsan_atomic128 *a, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +void __tsan_atomic128_store(volatile __tsan_atomic128 *a, __tsan_atomic128 v, + __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a, + __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a, + __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a, + __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a, + __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_exchange(volatile __tsan_atomic128 *a, + __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a, + __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a, + __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a, + __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a, + __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_fetch_add(volatile __tsan_atomic128 *a, + __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_fetch_sub(volatile __tsan_atomic8 *a, + __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_fetch_sub(volatile __tsan_atomic16 *a, + __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_fetch_sub(volatile __tsan_atomic32 *a, + __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_fetch_sub(volatile __tsan_atomic64 *a, + __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_fetch_sub(volatile __tsan_atomic128 *a, + __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a, + __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a, + __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a, + __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a, + __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_fetch_and(volatile __tsan_atomic128 *a, + __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a, + __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a, + __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a, + __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a, + __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_fetch_or(volatile __tsan_atomic128 *a, + __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a, + __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a, + __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a, + __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a, + __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_fetch_xor(volatile __tsan_atomic128 *a, + __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_fetch_nand(volatile __tsan_atomic8 *a, + __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_fetch_nand(volatile __tsan_atomic16 *a, + __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_fetch_nand(volatile __tsan_atomic32 *a, + __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_fetch_nand(volatile __tsan_atomic64 *a, + __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_fetch_nand(volatile __tsan_atomic128 *a, + __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a, + __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a, + __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a, + __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a, + __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic128_compare_exchange_weak(volatile __tsan_atomic128 *a, + __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; + +int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a, + __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a, + __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a, + __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a, + __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +int __tsan_atomic128_compare_exchange_strong(volatile __tsan_atomic128 *a, + __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo, + __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; + +__tsan_atomic8 __tsan_atomic8_compare_exchange_val( + volatile __tsan_atomic8 *a, __tsan_atomic8 c, __tsan_atomic8 v, + __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +__tsan_atomic16 __tsan_atomic16_compare_exchange_val( + volatile __tsan_atomic16 *a, __tsan_atomic16 c, __tsan_atomic16 v, + __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +__tsan_atomic32 __tsan_atomic32_compare_exchange_val( + volatile __tsan_atomic32 *a, __tsan_atomic32 c, __tsan_atomic32 v, + __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +__tsan_atomic64 __tsan_atomic64_compare_exchange_val( + volatile __tsan_atomic64 *a, __tsan_atomic64 c, __tsan_atomic64 v, + __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; +__tsan_atomic128 __tsan_atomic128_compare_exchange_val( + volatile __tsan_atomic128 *a, __tsan_atomic128 c, __tsan_atomic128 v, + __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE; + +void __tsan_atomic_thread_fence(__tsan_memory_order mo) INTERFACE_ATTRIBUTE; +void __tsan_atomic_signal_fence(__tsan_memory_order mo) INTERFACE_ATTRIBUTE; + +#ifdef __cplusplus +} // extern "C" +#endif + +#undef INTERFACE_ATTRIBUTE + +#endif // #ifndef TSAN_INTERFACE_ATOMIC_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h new file mode 100644 index 00000000..0187e49d --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_inl.h @@ -0,0 +1,85 @@ +//===-- tsan_interface_inl.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "tsan_interface.h" +#include "tsan_rtl.h" + +#define CALLERPC ((uptr)__builtin_return_address(0)) + +using namespace __tsan; // NOLINT + +void __tsan_read1(void *addr) { + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); +} + +void __tsan_read2(void *addr) { + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); +} + +void __tsan_read4(void *addr) { + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); +} + +void __tsan_read8(void *addr) { + MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); +} + +void __tsan_write1(void *addr) { + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog1); +} + +void __tsan_write2(void *addr) { + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog2); +} + +void __tsan_write4(void *addr) { + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog4); +} + +void __tsan_write8(void *addr) { + MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); +} + +void __tsan_vptr_update(void **vptr_p, void *new_val) { + CHECK_EQ(sizeof(vptr_p), 8); + if (*vptr_p != new_val) { + ThreadState *thr = cur_thread(); + thr->is_vptr_access = true; + MemoryWrite(thr, CALLERPC, (uptr)vptr_p, kSizeLog8); + thr->is_vptr_access = false; + } +} + +void __tsan_vptr_read(void **vptr_p) { + CHECK_EQ(sizeof(vptr_p), 8); + ThreadState *thr = cur_thread(); + thr->is_vptr_access = true; + MemoryRead(thr, CALLERPC, (uptr)vptr_p, kSizeLog8); + thr->is_vptr_access = false; +} + +void __tsan_func_entry(void *pc) { + FuncEntry(cur_thread(), (uptr)pc); +} + +void __tsan_func_exit() { + FuncExit(cur_thread()); +} + +void __tsan_read_range(void *addr, uptr size) { + MemoryAccessRange(cur_thread(), CALLERPC, (uptr)addr, size, false); +} + +void __tsan_write_range(void *addr, uptr size) { + MemoryAccessRange(cur_thread(), CALLERPC, (uptr)addr, size, true); +} diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc new file mode 100644 index 00000000..53f14cf0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc @@ -0,0 +1,331 @@ +//===-- tsan_interface_java.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "tsan_interface_java.h" +#include "tsan_rtl.h" +#include "tsan_mutex.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +using namespace __tsan; // NOLINT + +namespace __tsan { + +const uptr kHeapShadow = 0x300000000000ull; +const uptr kHeapAlignment = 8; + +struct BlockDesc { + bool begin; + Mutex mtx; + SyncVar *head; + + BlockDesc() + : mtx(MutexTypeJavaMBlock, StatMtxJavaMBlock) + , head() { + CHECK_EQ(begin, false); + begin = true; + } + + ~BlockDesc() { + CHECK_EQ(begin, true); + begin = false; + ThreadState *thr = cur_thread(); + SyncVar *s = head; + while (s) { + SyncVar *s1 = s->next; + StatInc(thr, StatSyncDestroyed); + s->mtx.Lock(); + s->mtx.Unlock(); + thr->mset.Remove(s->GetId()); + DestroyAndFree(s); + s = s1; + } + } +}; + +struct JavaContext { + const uptr heap_begin; + const uptr heap_size; + BlockDesc *heap_shadow; + + JavaContext(jptr heap_begin, jptr heap_size) + : heap_begin(heap_begin) + , heap_size(heap_size) { + uptr size = heap_size / kHeapAlignment * sizeof(BlockDesc); + heap_shadow = (BlockDesc*)MmapFixedNoReserve(kHeapShadow, size); + if ((uptr)heap_shadow != kHeapShadow) { + Printf("ThreadSanitizer: failed to mmap Java heap shadow\n"); + Die(); + } + } +}; + +class ScopedJavaFunc { + public: + ScopedJavaFunc(ThreadState *thr, uptr pc) + : thr_(thr) { + Initialize(thr_); + FuncEntry(thr, pc); + CHECK_EQ(thr_->in_rtl, 0); + thr_->in_rtl++; + } + + ~ScopedJavaFunc() { + thr_->in_rtl--; + CHECK_EQ(thr_->in_rtl, 0); + FuncExit(thr_); + // FIXME(dvyukov): process pending signals. + } + + private: + ThreadState *thr_; +}; + +static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1]; +static JavaContext *jctx; + +static BlockDesc *getblock(uptr addr) { + uptr i = (addr - jctx->heap_begin) / kHeapAlignment; + return &jctx->heap_shadow[i]; +} + +static uptr USED getmem(BlockDesc *b) { + uptr i = b - jctx->heap_shadow; + uptr p = jctx->heap_begin + i * kHeapAlignment; + CHECK_GE(p, jctx->heap_begin); + CHECK_LT(p, jctx->heap_begin + jctx->heap_size); + return p; +} + +static BlockDesc *getblockbegin(uptr addr) { + for (BlockDesc *b = getblock(addr);; b--) { + CHECK_GE(b, jctx->heap_shadow); + if (b->begin) + return b; + } + return 0; +} + +SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr, + bool write_lock, bool create) { + if (jctx == 0 || addr < jctx->heap_begin + || addr >= jctx->heap_begin + jctx->heap_size) + return 0; + BlockDesc *b = getblockbegin(addr); + DPrintf("#%d: GetJavaSync %p->%p\n", thr->tid, addr, b); + Lock l(&b->mtx); + SyncVar *s = b->head; + for (; s; s = s->next) { + if (s->addr == addr) { + DPrintf("#%d: found existing sync for %p\n", thr->tid, addr); + break; + } + } + if (s == 0 && create) { + DPrintf("#%d: creating new sync for %p\n", thr->tid, addr); + s = CTX()->synctab.Create(thr, pc, addr); + s->next = b->head; + b->head = s; + } + if (s) { + if (write_lock) + s->mtx.Lock(); + else + s->mtx.ReadLock(); + } + return s; +} + +SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { + // We do not destroy Java mutexes other than in __tsan_java_free(). + return 0; +} + +} // namespace __tsan + +#define SCOPED_JAVA_FUNC(func) \ + ThreadState *thr = cur_thread(); \ + const uptr caller_pc = GET_CALLER_PC(); \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ + (void)pc; \ + ScopedJavaFunc scoped(thr, caller_pc); \ +/**/ + +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); + CHECK_EQ(jctx, 0); + CHECK_GT(heap_begin, 0); + CHECK_GT(heap_size, 0); + CHECK_EQ(heap_begin % kHeapAlignment, 0); + CHECK_EQ(heap_size % kHeapAlignment, 0); + CHECK_LT(heap_begin, heap_begin + heap_size); + jctx = new(jctx_buf) JavaContext(heap_begin, heap_size); +} + +int __tsan_java_fini() { + SCOPED_JAVA_FUNC(__tsan_java_fini); + DPrintf("#%d: java_fini()\n", thr->tid); + CHECK_NE(jctx, 0); + // FIXME(dvyukov): this does not call atexit() callbacks. + int status = Finalize(thr); + DPrintf("#%d: java_fini() = %d\n", thr->tid, status); + return status; +} + +void __tsan_java_alloc(jptr ptr, jptr size) { + SCOPED_JAVA_FUNC(__tsan_java_alloc); + DPrintf("#%d: java_alloc(%p, %p)\n", thr->tid, ptr, size); + CHECK_NE(jctx, 0); + CHECK_NE(size, 0); + CHECK_EQ(ptr % kHeapAlignment, 0); + CHECK_EQ(size % kHeapAlignment, 0); + CHECK_GE(ptr, jctx->heap_begin); + CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size); + + BlockDesc *b = getblock(ptr); + new(b) BlockDesc(); +} + +void __tsan_java_free(jptr ptr, jptr size) { + SCOPED_JAVA_FUNC(__tsan_java_free); + DPrintf("#%d: java_free(%p, %p)\n", thr->tid, ptr, size); + CHECK_NE(jctx, 0); + CHECK_NE(size, 0); + CHECK_EQ(ptr % kHeapAlignment, 0); + CHECK_EQ(size % kHeapAlignment, 0); + CHECK_GE(ptr, jctx->heap_begin); + CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size); + + BlockDesc *beg = getblock(ptr); + BlockDesc *end = getblock(ptr + size); + for (BlockDesc *b = beg; b != end; b++) { + if (b->begin) + b->~BlockDesc(); + } +} + +void __tsan_java_move(jptr src, jptr dst, jptr size) { + SCOPED_JAVA_FUNC(__tsan_java_move); + DPrintf("#%d: java_move(%p, %p, %p)\n", thr->tid, src, dst, size); + CHECK_NE(jctx, 0); + CHECK_NE(size, 0); + CHECK_EQ(src % kHeapAlignment, 0); + CHECK_EQ(dst % kHeapAlignment, 0); + CHECK_EQ(size % kHeapAlignment, 0); + CHECK_GE(src, jctx->heap_begin); + CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size); + CHECK_GE(dst, jctx->heap_begin); + CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size); + CHECK(dst >= src + size || src >= dst + size); + + // Assuming it's not running concurrently with threads that do + // memory accesses and mutex operations (stop-the-world phase). + { // NOLINT + BlockDesc *s = getblock(src); + BlockDesc *d = getblock(dst); + BlockDesc *send = getblock(src + size); + for (; s != send; s++, d++) { + CHECK_EQ(d->begin, false); + if (s->begin) { + DPrintf("#%d: moving block %p->%p\n", thr->tid, getmem(s), getmem(d)); + new(d) BlockDesc; + d->head = s->head; + for (SyncVar *sync = d->head; sync; sync = sync->next) { + uptr newaddr = sync->addr - src + dst; + DPrintf("#%d: moving sync %p->%p\n", thr->tid, sync->addr, newaddr); + sync->addr = newaddr; + } + s->head = 0; + s->~BlockDesc(); + } + } + } + + { // NOLINT + u64 *s = (u64*)MemToShadow(src); + u64 *d = (u64*)MemToShadow(dst); + u64 *send = (u64*)MemToShadow(src + size); + for (; s != send; s++, d++) { + *d = *s; + *s = 0; + } + } +} + +void __tsan_java_mutex_lock(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_lock); + DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + MutexCreate(thr, pc, addr, true, true, true); + MutexLock(thr, pc, addr); +} + +void __tsan_java_mutex_unlock(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock); + DPrintf("#%d: java_mutex_unlock(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + MutexUnlock(thr, pc, addr); +} + +void __tsan_java_mutex_read_lock(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_read_lock); + DPrintf("#%d: java_mutex_read_lock(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + MutexCreate(thr, pc, addr, true, true, true); + MutexReadLock(thr, pc, addr); +} + +void __tsan_java_mutex_read_unlock(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_read_unlock); + DPrintf("#%d: java_mutex_read_unlock(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + MutexReadUnlock(thr, pc, addr); +} + +void __tsan_java_mutex_lock_rec(jptr addr, int rec) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_lock_rec); + DPrintf("#%d: java_mutex_lock_rec(%p, %d)\n", thr->tid, addr, rec); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + CHECK_GT(rec, 0); + + MutexCreate(thr, pc, addr, true, true, true); + MutexLock(thr, pc, addr, rec); +} + +int __tsan_java_mutex_unlock_rec(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock_rec); + DPrintf("#%d: java_mutex_unlock_rec(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + return MutexUnlock(thr, pc, addr, true); +} diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_interface_java.h b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_java.h new file mode 100644 index 00000000..9ac78e07 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_interface_java.h @@ -0,0 +1,83 @@ +//===-- tsan_interface_java.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Interface for verification of Java or mixed Java/C++ programs. +// The interface is intended to be used from within a JVM and notify TSan +// about such events like Java locks and GC memory compaction. +// +// For plain memory accesses and function entry/exit a JVM is intended to use +// C++ interfaces: __tsan_readN/writeN and __tsan_func_enter/exit. +// +// For volatile memory accesses and atomic operations JVM is intended to use +// standard atomics API: __tsan_atomicN_load/store/etc. +// +// For usage examples see lit_tests/java_*.cc +//===----------------------------------------------------------------------===// +#ifndef TSAN_INTERFACE_JAVA_H +#define TSAN_INTERFACE_JAVA_H + +#ifndef INTERFACE_ATTRIBUTE +# define INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long jptr; // NOLINT + +// 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). +// Returns exit status or 0 if tsan does not want to override it. +int __tsan_java_fini() INTERFACE_ATTRIBUTE; + +// Callback for memory allocations. +// May be omitted for allocations that are not subject to data races +// nor contain synchronization objects (e.g. String). +void __tsan_java_alloc(jptr ptr, jptr size) INTERFACE_ATTRIBUTE; +// Callback for memory free. +// Can be aggregated for several objects (preferably). +void __tsan_java_free(jptr ptr, jptr size) INTERFACE_ATTRIBUTE; +// Callback for memory move by GC. +// Can be aggregated for several objects (preferably). +// The ranges must not overlap. +void __tsan_java_move(jptr src, jptr dst, jptr size) INTERFACE_ATTRIBUTE; + +// Mutex lock. +// Addr is any unique address associated with the mutex. +// Can be called on recursive reentry. +void __tsan_java_mutex_lock(jptr addr) INTERFACE_ATTRIBUTE; +// Mutex unlock. +void __tsan_java_mutex_unlock(jptr addr) INTERFACE_ATTRIBUTE; +// Mutex read lock. +void __tsan_java_mutex_read_lock(jptr addr) INTERFACE_ATTRIBUTE; +// Mutex read unlock. +void __tsan_java_mutex_read_unlock(jptr addr) INTERFACE_ATTRIBUTE; +// Recursive mutex lock, intended for handling of Object.wait(). +// The 'rec' value must be obtained from the previous +// __tsan_java_mutex_unlock_rec(). +void __tsan_java_mutex_lock_rec(jptr addr, int rec) INTERFACE_ATTRIBUTE; +// Recursive mutex unlock, intended for handling of Object.wait(). +// The return value says how many times this thread called lock() +// w/o a pairing unlock() (i.e. how many recursive levels it unlocked). +// It must be passed back to __tsan_java_mutex_lock_rec() to restore +// the same recursion level. +int __tsan_java_mutex_unlock_rec(jptr addr) INTERFACE_ATTRIBUTE; + +#ifdef __cplusplus +} // extern "C" +#endif + +#undef INTERFACE_ATTRIBUTE + +#endif // #ifndef TSAN_INTERFACE_JAVA_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_md5.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_md5.cc new file mode 100644 index 00000000..66e82404 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_md5.cc @@ -0,0 +1,245 @@ +//===-- tsan_md5.cc -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_defs.h" + +namespace __tsan { + +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) + +typedef unsigned int MD5_u32plus; +typedef unsigned long ulong_t; // NOLINT + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +static void *body(MD5_CTX *ctx, void *data, ulong_t size) { + unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (unsigned char*)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) + + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) { + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, void *data, ulong_t size) { + MD5_u32plus saved_lo; + ulong_t used, free; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + free = 64 - used; + + if (size < free) { + internal_memcpy(&ctx->buffer[used], data, size); + return; + } + + internal_memcpy(&ctx->buffer[used], data, free); + data = (unsigned char *)data + free; + size -= free; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(ulong_t)0x3f); + size &= 0x3f; + } + + internal_memcpy(ctx->buffer, data, size); +} + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) { + ulong_t used, free; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + internal_memset(&ctx->buffer[used], 0, free); + body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + internal_memset(&ctx->buffer[used], 0, free - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + internal_memset(ctx, 0, sizeof(*ctx)); +} + +MD5Hash md5_hash(const void *data, uptr size) { + MD5Hash res; + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, (void*)data, size); + MD5_Final((unsigned char*)&res.hash[0], &ctx); + return res; +} +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_mman.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_mman.cc new file mode 100644 index 00000000..8547f714 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_mman.cc @@ -0,0 +1,275 @@ +//===-- tsan_mman.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "tsan_mman.h" +#include "tsan_rtl.h" +#include "tsan_report.h" +#include "tsan_flags.h" + +// May be overriden by front-end. +extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) { + (void)ptr; + (void)size; +} + +extern "C" void WEAK __tsan_free_hook(void *ptr) { + (void)ptr; +} + +namespace __tsan { + +COMPILER_CHECK(sizeof(MBlock) == 16); + +void MBlock::Lock() { + atomic_uintptr_t *a = reinterpret_cast(this); + uptr v = atomic_load(a, memory_order_relaxed); + for (int iter = 0;; iter++) { + if (v & 1) { + if (iter < 10) + proc_yield(20); + else + internal_sched_yield(); + v = atomic_load(a, memory_order_relaxed); + continue; + } + if (atomic_compare_exchange_weak(a, &v, v | 1, memory_order_acquire)) + break; + } +} + +void MBlock::Unlock() { + atomic_uintptr_t *a = reinterpret_cast(this); + uptr v = atomic_load(a, memory_order_relaxed); + DCHECK(v & 1); + atomic_store(a, v & ~1, memory_order_relaxed); +} + +struct MapUnmapCallback { + void OnMap(uptr p, uptr size) const { } + void OnUnmap(uptr p, uptr size) const { + // We are about to unmap a chunk of user memory. + // Mark the corresponding shadow memory as not needed. + DontNeedShadowFor(p, size); + } +}; + +static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64); +Allocator *allocator() { + return reinterpret_cast(&allocator_placeholder); +} + +void InitializeAllocator() { + allocator()->Init(); +} + +void AllocatorThreadStart(ThreadState *thr) { + allocator()->InitCache(&thr->alloc_cache); + internal_allocator()->InitCache(&thr->internal_alloc_cache); +} + +void AllocatorThreadFinish(ThreadState *thr) { + allocator()->DestroyCache(&thr->alloc_cache); + internal_allocator()->DestroyCache(&thr->internal_alloc_cache); +} + +void AllocatorPrintStats() { + allocator()->PrintStats(); +} + +static void SignalUnsafeCall(ThreadState *thr, uptr pc) { + if (!thr->in_signal_handler || !flags()->report_signal_unsafe) + return; + Context *ctx = CTX(); + StackTrace stack; + stack.ObtainCurrent(thr, pc); + ThreadRegistryLock l(ctx->thread_registry); + ScopedReport rep(ReportTypeSignalUnsafe); + if (!IsFiredSuppression(ctx, rep, stack)) { + rep.AddStack(&stack); + OutputReport(ctx, rep, rep.GetReport()->stacks[0]); + } +} + +void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) { + CHECK_GT(thr->in_rtl, 0); + if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) + return AllocatorReturnNull(); + void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); + if (p == 0) + return 0; + MBlock *b = new(allocator()->GetMetaData(p)) MBlock; + b->Init(sz, thr->tid, CurrentStackId(thr, pc)); + if (CTX() && CTX()->initialized) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + else + MemoryResetRange(thr, pc, (uptr)p, sz); + } + DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); + SignalUnsafeCall(thr, pc); + return p; +} + +void user_free(ThreadState *thr, uptr pc, void *p) { + CHECK_GT(thr->in_rtl, 0); + CHECK_NE(p, (void*)0); + DPrintf("#%d: free(%p)\n", thr->tid, p); + MBlock *b = (MBlock*)allocator()->GetMetaData(p); + if (b->ListHead()) { + MBlock::ScopedLock l(b); + for (SyncVar *s = b->ListHead(); s;) { + SyncVar *res = s; + s = s->next; + StatInc(thr, StatSyncDestroyed); + res->mtx.Lock(); + res->mtx.Unlock(); + DestroyAndFree(res); + } + b->ListReset(); + } + if (CTX() && CTX()->initialized && thr->in_rtl == 1) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); + } + allocator()->Deallocate(&thr->alloc_cache, p); + SignalUnsafeCall(thr, pc); +} + +void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { + CHECK_GT(thr->in_rtl, 0); + void *p2 = 0; + // FIXME: Handle "shrinking" more efficiently, + // it seems that some software actually does this. + if (sz) { + p2 = user_alloc(thr, pc, sz); + if (p2 == 0) + return 0; + if (p) { + MBlock *b = user_mblock(thr, p); + CHECK_NE(b, 0); + internal_memcpy(p2, p, min(b->Size(), sz)); + } + } + if (p) + user_free(thr, pc, p); + return p2; +} + +uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p) { + CHECK_GT(thr->in_rtl, 0); + if (p == 0) + return 0; + MBlock *b = (MBlock*)allocator()->GetMetaData(p); + return b ? b->Size() : 0; +} + +MBlock *user_mblock(ThreadState *thr, void *p) { + CHECK_NE(p, 0); + Allocator *a = allocator(); + void *b = a->GetBlockBegin(p); + if (b == 0) + return 0; + return (MBlock*)a->GetMetaData(b); +} + +void invoke_malloc_hook(void *ptr, uptr size) { + Context *ctx = CTX(); + ThreadState *thr = cur_thread(); + if (ctx == 0 || !ctx->initialized || thr->in_rtl) + return; + __tsan_malloc_hook(ptr, size); +} + +void invoke_free_hook(void *ptr) { + Context *ctx = CTX(); + ThreadState *thr = cur_thread(); + if (ctx == 0 || !ctx->initialized || thr->in_rtl) + return; + __tsan_free_hook(ptr); +} + +void *internal_alloc(MBlockType typ, uptr sz) { + ThreadState *thr = cur_thread(); + CHECK_GT(thr->in_rtl, 0); + CHECK_LE(sz, InternalSizeClassMap::kMaxSize); + if (thr->nomalloc) { + thr->nomalloc = 0; // CHECK calls internal_malloc(). + CHECK(0); + } + return InternalAlloc(sz, &thr->internal_alloc_cache); +} + +void internal_free(void *p) { + ThreadState *thr = cur_thread(); + CHECK_GT(thr->in_rtl, 0); + if (thr->nomalloc) { + thr->nomalloc = 0; // CHECK calls internal_malloc(). + CHECK(0); + } + InternalFree(p, &thr->internal_alloc_cache); +} + +} // namespace __tsan + +using namespace __tsan; + +extern "C" { +uptr __tsan_get_current_allocated_bytes() { + u64 stats[AllocatorStatCount]; + allocator()->GetStats(stats); + u64 m = stats[AllocatorStatMalloced]; + u64 f = stats[AllocatorStatFreed]; + return m >= f ? m - f : 1; +} + +uptr __tsan_get_heap_size() { + u64 stats[AllocatorStatCount]; + allocator()->GetStats(stats); + u64 m = stats[AllocatorStatMmapped]; + u64 f = stats[AllocatorStatUnmapped]; + return m >= f ? m - f : 1; +} + +uptr __tsan_get_free_bytes() { + return 1; +} + +uptr __tsan_get_unmapped_bytes() { + return 1; +} + +uptr __tsan_get_estimated_allocated_size(uptr size) { + return size; +} + +bool __tsan_get_ownership(void *p) { + return allocator()->GetBlockBegin(p) != 0; +} + +uptr __tsan_get_allocated_size(void *p) { + if (p == 0) + return 0; + p = allocator()->GetBlockBegin(p); + if (p == 0) + return 0; + MBlock *b = (MBlock*)allocator()->GetMetaData(p); + return b->Size(); +} + +void __tsan_on_thread_idle() { + ThreadState *thr = cur_thread(); + allocator()->SwallowCache(&thr->alloc_cache); + internal_allocator()->SwallowCache(&thr->internal_alloc_cache); +} +} // extern "C" diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_mman.h b/projects/compiler-rt/lib/tsan/rtl/tsan_mman.h new file mode 100644 index 00000000..19d55543 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_mman.h @@ -0,0 +1,84 @@ +//===-- tsan_mman.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_MMAN_H +#define TSAN_MMAN_H + +#include "tsan_defs.h" + +namespace __tsan { + +const uptr kDefaultAlignment = 16; + +void InitializeAllocator(); +void AllocatorThreadStart(ThreadState *thr); +void AllocatorThreadFinish(ThreadState *thr); +void AllocatorPrintStats(); + +// For user allocations. +void *user_alloc(ThreadState *thr, uptr pc, uptr sz, + uptr align = kDefaultAlignment); +// Does not accept NULL. +void user_free(ThreadState *thr, uptr pc, void *p); +void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz); +void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align); +uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p); +// Given the pointer p into a valid allocated block, +// returns the descriptor of the block. +MBlock *user_mblock(ThreadState *thr, void *p); + +// Invoking malloc/free hooks that may be installed by the user. +void invoke_malloc_hook(void *ptr, uptr size); +void invoke_free_hook(void *ptr); + +enum MBlockType { + MBlockScopedBuf, + MBlockString, + MBlockStackTrace, + MBlockShadowStack, + MBlockSync, + MBlockClock, + MBlockThreadContex, + MBlockDeadInfo, + MBlockRacyStacks, + MBlockRacyAddresses, + MBlockAtExit, + MBlockFlag, + MBlockReport, + MBlockReportMop, + MBlockReportThread, + MBlockReportMutex, + MBlockReportLoc, + MBlockReportStack, + MBlockSuppression, + MBlockExpectRace, + MBlockSignal, + MBlockFD, + MBlockJmpBuf, + + // This must be the last. + MBlockTypeCount +}; + +// For internal data structures. +void *internal_alloc(MBlockType typ, uptr sz); +void internal_free(void *p); + +template +void DestroyAndFree(T *&p) { + p->~T(); + internal_free(p); + p = 0; +} + +} // namespace __tsan +#endif // TSAN_MMAN_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_mutex.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_mutex.cc new file mode 100644 index 00000000..a92fd90f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_mutex.cc @@ -0,0 +1,274 @@ +//===-- tsan_mutex.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_libc.h" +#include "tsan_mutex.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" + +namespace __tsan { + +// Simple reader-writer spin-mutex. Optimized for not-so-contended case. +// Readers have preference, can possibly starvate writers. + +// The table fixes what mutexes can be locked under what mutexes. +// E.g. if the row for MutexTypeThreads contains MutexTypeReport, +// then Report mutex can be locked while under Threads mutex. +// The leaf mutexes can be locked under any other mutexes. +// Recursive locking is not supported. +#if TSAN_DEBUG && !TSAN_GO +const MutexType MutexTypeLeaf = (MutexType)-1; +static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = { + /*0 MutexTypeInvalid*/ {}, + /*1 MutexTypeTrace*/ {MutexTypeLeaf}, + /*2 MutexTypeThreads*/ {MutexTypeReport}, + /*3 MutexTypeReport*/ {MutexTypeSyncTab, MutexTypeSyncVar, + MutexTypeMBlock, MutexTypeJavaMBlock}, + /*4 MutexTypeSyncVar*/ {}, + /*5 MutexTypeSyncTab*/ {MutexTypeSyncVar}, + /*6 MutexTypeSlab*/ {MutexTypeLeaf}, + /*7 MutexTypeAnnotations*/ {}, + /*8 MutexTypeAtExit*/ {MutexTypeSyncTab}, + /*9 MutexTypeMBlock*/ {MutexTypeSyncVar}, + /*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar}, +}; + +static bool CanLockAdj[MutexTypeCount][MutexTypeCount]; +#endif + +void InitializeMutex() { +#if TSAN_DEBUG && !TSAN_GO + // Build the "can lock" adjacency matrix. + // If [i][j]==true, then one can lock mutex j while under mutex i. + const int N = MutexTypeCount; + int cnt[N] = {}; + bool leaf[N] = {}; + for (int i = 1; i < N; i++) { + for (int j = 0; j < N; j++) { + MutexType z = CanLockTab[i][j]; + if (z == MutexTypeInvalid) + continue; + if (z == MutexTypeLeaf) { + CHECK(!leaf[i]); + leaf[i] = true; + continue; + } + CHECK(!CanLockAdj[i][(int)z]); + CanLockAdj[i][(int)z] = true; + cnt[i]++; + } + } + for (int i = 0; i < N; i++) { + CHECK(!leaf[i] || cnt[i] == 0); + } + // Add leaf mutexes. + for (int i = 0; i < N; i++) { + if (!leaf[i]) + continue; + for (int j = 0; j < N; j++) { + if (i == j || leaf[j] || j == MutexTypeInvalid) + continue; + CHECK(!CanLockAdj[j][i]); + CanLockAdj[j][i] = true; + } + } + // Build the transitive closure. + bool CanLockAdj2[MutexTypeCount][MutexTypeCount]; + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + CanLockAdj2[i][j] = CanLockAdj[i][j]; + } + } + for (int k = 0; k < N; k++) { + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) { + CanLockAdj2[i][j] = true; + } + } + } + } +#if 0 + Printf("Can lock graph:\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + Printf("%d ", CanLockAdj[i][j]); + } + Printf("\n"); + } + Printf("Can lock graph closure:\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + Printf("%d ", CanLockAdj2[i][j]); + } + Printf("\n"); + } +#endif + // Verify that the graph is acyclic. + for (int i = 0; i < N; i++) { + if (CanLockAdj2[i][i]) { + Printf("Mutex %d participates in a cycle\n", i); + Die(); + } + } +#endif +} + +DeadlockDetector::DeadlockDetector() { + // Rely on zero initialization because some mutexes can be locked before ctor. +} + +#if TSAN_DEBUG && !TSAN_GO +void DeadlockDetector::Lock(MutexType t) { + // Printf("LOCK %d @%zu\n", t, seq_ + 1); + CHECK_GT(t, MutexTypeInvalid); + CHECK_LT(t, MutexTypeCount); + u64 max_seq = 0; + u64 max_idx = MutexTypeInvalid; + for (int i = 0; i != MutexTypeCount; i++) { + if (locked_[i] == 0) + continue; + CHECK_NE(locked_[i], max_seq); + if (max_seq < locked_[i]) { + max_seq = locked_[i]; + max_idx = i; + } + } + locked_[t] = ++seq_; + if (max_idx == MutexTypeInvalid) + return; + // Printf(" last %d @%zu\n", max_idx, max_seq); + if (!CanLockAdj[max_idx][t]) { + Printf("ThreadSanitizer: internal deadlock detected\n"); + Printf("ThreadSanitizer: can't lock %d while under %zu\n", + t, (uptr)max_idx); + CHECK(0); + } +} + +void DeadlockDetector::Unlock(MutexType t) { + // Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]); + CHECK(locked_[t]); + locked_[t] = 0; +} +#endif + +const uptr kUnlocked = 0; +const uptr kWriteLock = 1; +const uptr kReadLock = 2; + +class Backoff { + public: + Backoff() + : iter_() { + } + + bool Do() { + if (iter_++ < kActiveSpinIters) + proc_yield(kActiveSpinCnt); + else + internal_sched_yield(); + return true; + } + + u64 Contention() const { + u64 active = iter_ % kActiveSpinIters; + u64 passive = iter_ - active; + return active + 10 * passive; + } + + private: + int iter_; + static const int kActiveSpinIters = 10; + static const int kActiveSpinCnt = 20; +}; + +Mutex::Mutex(MutexType type, StatType stat_type) { + CHECK_GT(type, MutexTypeInvalid); + CHECK_LT(type, MutexTypeCount); +#if TSAN_DEBUG + type_ = type; +#endif +#if TSAN_COLLECT_STATS + stat_type_ = stat_type; +#endif + atomic_store(&state_, kUnlocked, memory_order_relaxed); +} + +Mutex::~Mutex() { + CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked); +} + +void Mutex::Lock() { +#if TSAN_DEBUG && !TSAN_GO + cur_thread()->deadlock_detector.Lock(type_); +#endif + uptr cmp = kUnlocked; + if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock, + memory_order_acquire)) + return; + for (Backoff backoff; backoff.Do();) { + if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) { + cmp = kUnlocked; + if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock, + memory_order_acquire)) { +#if TSAN_COLLECT_STATS + StatInc(cur_thread(), stat_type_, backoff.Contention()); +#endif + return; + } + } + } +} + +void Mutex::Unlock() { + uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release); + (void)prev; + DCHECK_NE(prev & kWriteLock, 0); +#if TSAN_DEBUG && !TSAN_GO + cur_thread()->deadlock_detector.Unlock(type_); +#endif +} + +void Mutex::ReadLock() { +#if TSAN_DEBUG && !TSAN_GO + cur_thread()->deadlock_detector.Lock(type_); +#endif + uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire); + if ((prev & kWriteLock) == 0) + return; + for (Backoff backoff; backoff.Do();) { + prev = atomic_load(&state_, memory_order_acquire); + if ((prev & kWriteLock) == 0) { +#if TSAN_COLLECT_STATS + StatInc(cur_thread(), stat_type_, backoff.Contention()); +#endif + return; + } + } +} + +void Mutex::ReadUnlock() { + uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release); + (void)prev; + DCHECK_EQ(prev & kWriteLock, 0); + DCHECK_GT(prev & ~kWriteLock, 0); +#if TSAN_DEBUG && !TSAN_GO + cur_thread()->deadlock_detector.Unlock(type_); +#endif +} + +void Mutex::CheckLocked() { + CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_mutex.h b/projects/compiler-rt/lib/tsan/rtl/tsan_mutex.h new file mode 100644 index 00000000..a2b48910 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_mutex.h @@ -0,0 +1,82 @@ +//===-- tsan_mutex.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_MUTEX_H +#define TSAN_MUTEX_H + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "tsan_defs.h" + +namespace __tsan { + +enum MutexType { + MutexTypeInvalid, + MutexTypeTrace, + MutexTypeThreads, + MutexTypeReport, + MutexTypeSyncVar, + MutexTypeSyncTab, + MutexTypeSlab, + MutexTypeAnnotations, + MutexTypeAtExit, + MutexTypeMBlock, + MutexTypeJavaMBlock, + + // This must be the last. + MutexTypeCount +}; + +class Mutex { + public: + explicit Mutex(MutexType type, StatType stat_type); + ~Mutex(); + + void Lock(); + void Unlock(); + + void ReadLock(); + void ReadUnlock(); + + void CheckLocked(); + + private: + atomic_uintptr_t state_; +#if TSAN_DEBUG + MutexType type_; +#endif +#if TSAN_COLLECT_STATS + StatType stat_type_; +#endif + + Mutex(const Mutex&); + void operator = (const Mutex&); +}; + +typedef GenericScopedLock Lock; +typedef GenericScopedReadLock ReadLock; + +class DeadlockDetector { + public: + DeadlockDetector(); + void Lock(MutexType t); + void Unlock(MutexType t); + private: + u64 seq_; + u64 locked_[MutexTypeCount]; +}; + +void InitializeMutex(); + +} // namespace __tsan + +#endif // TSAN_MUTEX_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_mutexset.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_mutexset.cc new file mode 100644 index 00000000..21587770 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_mutexset.cc @@ -0,0 +1,89 @@ +//===-- tsan_mutexset.cc --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_mutexset.h" +#include "tsan_rtl.h" + +namespace __tsan { + +const uptr MutexSet::kMaxSize; + +MutexSet::MutexSet() { + size_ = 0; + internal_memset(&descs_, 0, sizeof(descs_)); +} + +void MutexSet::Add(u64 id, bool write, u64 epoch) { + // Look up existing mutex with the same id. + for (uptr i = 0; i < size_; i++) { + if (descs_[i].id == id) { + descs_[i].count++; + descs_[i].epoch = epoch; + return; + } + } + // On overflow, find the oldest mutex and drop it. + if (size_ == kMaxSize) { + u64 minepoch = (u64)-1; + u64 mini = (u64)-1; + for (uptr i = 0; i < size_; i++) { + if (descs_[i].epoch < minepoch) { + minepoch = descs_[i].epoch; + mini = i; + } + } + RemovePos(mini); + CHECK_EQ(size_, kMaxSize - 1); + } + // Add new mutex descriptor. + descs_[size_].id = id; + descs_[size_].write = write; + descs_[size_].epoch = epoch; + descs_[size_].count = 1; + size_++; +} + +void MutexSet::Del(u64 id, bool write) { + for (uptr i = 0; i < size_; i++) { + if (descs_[i].id == id) { + if (--descs_[i].count == 0) + RemovePos(i); + return; + } + } +} + +void MutexSet::Remove(u64 id) { + for (uptr i = 0; i < size_; i++) { + if (descs_[i].id == id) { + RemovePos(i); + return; + } + } +} + +void MutexSet::RemovePos(uptr i) { + CHECK_LT(i, size_); + descs_[i] = descs_[size_ - 1]; + size_--; +} + +uptr MutexSet::Size() const { + return size_; +} + +MutexSet::Desc MutexSet::Get(uptr i) const { + CHECK_LT(i, size_); + return descs_[i]; +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_mutexset.h b/projects/compiler-rt/lib/tsan/rtl/tsan_mutexset.h new file mode 100644 index 00000000..eebfd4d7 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_mutexset.h @@ -0,0 +1,65 @@ +//===-- tsan_mutexset.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// MutexSet holds the set of mutexes currently held by a thread. +//===----------------------------------------------------------------------===// +#ifndef TSAN_MUTEXSET_H +#define TSAN_MUTEXSET_H + +#include "tsan_defs.h" + +namespace __tsan { + +class MutexSet { + public: + // Holds limited number of mutexes. + // The oldest mutexes are discarded on overflow. + static const uptr kMaxSize = 16; + struct Desc { + u64 id; + u64 epoch; + int count; + bool write; + }; + + MutexSet(); + // The 'id' is obtained from SyncVar::GetId(). + void Add(u64 id, bool write, u64 epoch); + void Del(u64 id, bool write); + void Remove(u64 id); // Removes the mutex completely (if it's destroyed). + uptr Size() const; + Desc Get(uptr i) const; + + private: +#ifndef TSAN_GO + uptr size_; + Desc descs_[kMaxSize]; +#endif + + void RemovePos(uptr i); +}; + +// Go does not have mutexes, so do not spend memory and time. +// (Go sync.Mutex is actually a semaphore -- can be unlocked +// in different goroutine). +#ifdef TSAN_GO +MutexSet::MutexSet() {} +void MutexSet::Add(u64 id, bool write, u64 epoch) {} +void MutexSet::Del(u64 id, bool write) {} +void MutexSet::Remove(u64 id) {} +void MutexSet::RemovePos(uptr i) {} +uptr MutexSet::Size() const { return 0; } +MutexSet::Desc MutexSet::Get(uptr i) const { return Desc(); } +#endif + +} // namespace __tsan + +#endif // TSAN_REPORT_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_platform.h b/projects/compiler-rt/lib/tsan/rtl/tsan_platform.h new file mode 100644 index 00000000..32e22ba6 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_platform.h @@ -0,0 +1,172 @@ +//===-- tsan_platform.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Platform-specific code. +//===----------------------------------------------------------------------===// + +/* +C++ linux memory layout: +0000 0000 0000 - 03c0 0000 0000: protected +03c0 0000 0000 - 1000 0000 0000: shadow +1000 0000 0000 - 6000 0000 0000: protected +6000 0000 0000 - 6200 0000 0000: traces +6200 0000 0000 - 7d00 0000 0000: - +7d00 0000 0000 - 7e00 0000 0000: heap +7e00 0000 0000 - 7fff ffff ffff: modules and main thread stack + +C++ COMPAT linux memory layout: +0000 0000 0000 - 0400 0000 0000: protected +0400 0000 0000 - 1000 0000 0000: shadow +1000 0000 0000 - 2900 0000 0000: protected +2900 0000 0000 - 2c00 0000 0000: modules +2c00 0000 0000 - 6000 0000 0000: - +6000 0000 0000 - 6200 0000 0000: traces +6200 0000 0000 - 7d00 0000 0000: - +7d00 0000 0000 - 7e00 0000 0000: heap +7e00 0000 0000 - 7f00 0000 0000: - +7f00 0000 0000 - 7fff ffff ffff: main thread stack + +Go linux and darwin memory layout: +0000 0000 0000 - 0000 1000 0000: executable +0000 1000 0000 - 00f8 0000 0000: - +00c0 0000 0000 - 00e0 0000 0000: heap +00e0 0000 0000 - 1000 0000 0000: - +1000 0000 0000 - 1380 0000 0000: shadow +1460 0000 0000 - 6000 0000 0000: - +6000 0000 0000 - 6200 0000 0000: traces +6200 0000 0000 - 7fff ffff ffff: - + +Go windows memory layout: +0000 0000 0000 - 0000 1000 0000: executable +0000 1000 0000 - 00f8 0000 0000: - +00c0 0000 0000 - 00e0 0000 0000: heap +00e0 0000 0000 - 0100 0000 0000: - +0100 0000 0000 - 0560 0000 0000: shadow +0560 0000 0000 - 0760 0000 0000: traces +0760 0000 0000 - 07ff ffff ffff: - +*/ + +#ifndef TSAN_PLATFORM_H +#define TSAN_PLATFORM_H + +#include "tsan_defs.h" +#include "tsan_trace.h" + +#if defined(__LP64__) || defined(_WIN64) +namespace __tsan { + +#if defined(TSAN_GO) +static const uptr kLinuxAppMemBeg = 0x000000000000ULL; +static const uptr kLinuxAppMemEnd = 0x04dfffffffffULL; +# if SANITIZER_WINDOWS +static const uptr kLinuxShadowMsk = 0x010000000000ULL; +# else +static const uptr kLinuxShadowMsk = 0x200000000000ULL; +# endif +// TSAN_COMPAT_SHADOW is intended for COMPAT virtual memory layout, +// when memory addresses are of the 0x2axxxxxxxxxx form. +// The option is enabled with 'setarch x86_64 -L'. +#elif defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW +static const uptr kLinuxAppMemBeg = 0x290000000000ULL; +static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL; +#else +static const uptr kLinuxAppMemBeg = 0x7cf000000000ULL; +static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL; +#endif + +static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL; + +#if SANITIZER_WINDOWS +const uptr kTraceMemBegin = 0x056000000000ULL; +#else +const uptr kTraceMemBegin = 0x600000000000ULL; +#endif +const uptr kTraceMemSize = 0x020000000000ULL; + +// This has to be a macro to allow constant initialization of constants below. +#ifndef TSAN_GO +#define MemToShadow(addr) \ + (((addr) & ~(kLinuxAppMemMsk | (kShadowCell - 1))) * kShadowCnt) +#else +#define MemToShadow(addr) \ + ((((addr) & ~(kShadowCell - 1)) * kShadowCnt) | kLinuxShadowMsk) +#endif + +static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg); +static const uptr kLinuxShadowEnd = + MemToShadow(kLinuxAppMemEnd) | 0xff; + +static inline bool IsAppMem(uptr mem) { + return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd; +} + +static inline bool IsShadowMem(uptr mem) { + return mem >= kLinuxShadowBeg && mem <= kLinuxShadowEnd; +} + +static inline uptr ShadowToMem(uptr shadow) { + CHECK(IsShadowMem(shadow)); +#ifdef TSAN_GO + return (shadow & ~kLinuxShadowMsk) / kShadowCnt; +#else + return (shadow / kShadowCnt) | kLinuxAppMemMsk; +#endif +} + +// For COMPAT mapping returns an alternative address +// that mapped to the same shadow address. +// COMPAT mapping is not quite one-to-one. +static inline uptr AlternativeAddress(uptr addr) { +#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW + return (addr & ~kLinuxAppMemMsk) | 0x280000000000ULL; +#else + return 0; +#endif +} + +void FlushShadowMemory(); +void WriteMemoryProfile(char *buf, uptr buf_size); +uptr GetRSS(); + +const char *InitializePlatform(); +void FinalizePlatform(); + +// The additional page is to catch shadow stack overflow as paging fault. +const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + 4096 + + 4095) & ~4095; + +uptr ALWAYS_INLINE GetThreadTrace(int tid) { + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize; + DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); + return p; +} + +uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize + + kTraceSize * sizeof(Event); + DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); + return p; +} + +void internal_start_thread(void(*func)(void*), void *arg); + +// Says whether the addr relates to a global var. +// Guesses with high probability, may yield both false positives and negatives. +bool IsGlobalVar(uptr addr); +int ExtractResolvFDs(void *state, int *fds, int nfd); + +} // namespace __tsan + +#else // defined(__LP64__) || defined(_WIN64) +# error "Only 64-bit is supported" +#endif + +#endif // TSAN_PLATFORM_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc new file mode 100644 index 00000000..906b5dc7 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc @@ -0,0 +1,364 @@ +//===-- tsan_platform_linux.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Linux-specific code. +//===----------------------------------------------------------------------===// + + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" +#include "tsan_flags.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define __need_res_state +#include +#include + +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + +extern "C" struct mallinfo __libc_mallinfo(); + +namespace __tsan { + +const uptr kPageSize = 4096; + +#ifndef TSAN_GO +ScopedInRtl::ScopedInRtl() + : thr_(cur_thread()) { + in_rtl_ = thr_->in_rtl; + thr_->in_rtl++; + errno_ = errno; +} + +ScopedInRtl::~ScopedInRtl() { + thr_->in_rtl--; + errno = errno_; + CHECK_EQ(in_rtl_, thr_->in_rtl); +} +#else +ScopedInRtl::ScopedInRtl() { +} + +ScopedInRtl::~ScopedInRtl() { +} +#endif + +void FillProfileCallback(uptr start, uptr rss, bool file, + uptr *mem, uptr stats_size) { + CHECK_EQ(7, stats_size); + mem[6] += rss; // total + start >>= 40; + if (start < 0x10) // shadow + mem[0] += rss; + else if (start >= 0x20 && start < 0x30) // compat modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x7e) // modules + mem[file ? 1 : 2] += rss; + else if (start >= 0x60 && start < 0x62) // traces + mem[3] += rss; + else if (start >= 0x7d && start < 0x7e) // heap + mem[4] += rss; + else // other + mem[5] += rss; +} + +void WriteMemoryProfile(char *buf, uptr buf_size) { + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); + char *buf_pos = buf; + char *buf_end = buf + buf_size; + buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, + "RSS %zd MB: shadow:%zd file:%zd mmap:%zd trace:%zd heap:%zd other:%zd\n", + mem[6] >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20, + mem[3] >> 20, mem[4] >> 20, mem[5] >> 20); + struct mallinfo mi = __libc_mallinfo(); + buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos, + "mallinfo: arena=%d mmap=%d fordblks=%d keepcost=%d\n", + mi.arena >> 20, mi.hblkhd >> 20, mi.fordblks >> 20, mi.keepcost >> 20); +} + +uptr GetRSS() { + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); + return mem[6]; +} + + +void FlushShadowMemoryCallback( + const SuspendedThreadsList &suspended_threads_list, + void *argument) { + FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg); +} + +void FlushShadowMemory() { + StopTheWorld(FlushShadowMemoryCallback, 0); +} + +#ifndef TSAN_GO +static void ProtectRange(uptr beg, uptr end) { + ScopedInRtl in_rtl; + CHECK_LE(beg, end); + if (beg == end) + return; + if (beg != (uptr)Mprotect(beg, end - beg)) { + Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end); + Printf("FATAL: Make sure you are not using unlimited stack\n"); + Die(); + } +} +#endif + +#ifndef TSAN_GO +// Mark shadow for .rodata sections with the special kShadowRodata marker. +// Accesses to .rodata can't race, so this saves time, memory and trace space. +static void MapRodata() { + // First create temp file. + const char *tmpdir = GetEnv("TMPDIR"); + if (tmpdir == 0) + tmpdir = GetEnv("TEST_TMPDIR"); +#ifdef P_tmpdir + if (tmpdir == 0) + tmpdir = P_tmpdir; +#endif + if (tmpdir == 0) + return; + char filename[256]; + internal_snprintf(filename, sizeof(filename), "%s/tsan.rodata.%d", + tmpdir, (int)internal_getpid()); + uptr openrv = internal_open(filename, O_RDWR | O_CREAT | O_EXCL, 0600); + if (internal_iserror(openrv)) + return; + fd_t fd = openrv; + // Fill the file with kShadowRodata. + const uptr kMarkerSize = 512 * 1024 / sizeof(u64); + InternalScopedBuffer marker(kMarkerSize); + for (u64 *p = marker.data(); p < marker.data() + kMarkerSize; p++) + *p = kShadowRodata; + internal_write(fd, marker.data(), marker.size()); + // Map the file into memory. + uptr page = internal_mmap(0, kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, fd, 0); + if (internal_iserror(page)) { + internal_close(fd); + internal_unlink(filename); + return; + } + // Map the file into shadow of .rodata sections. + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + uptr start, end, offset, prot; + char name[128]; + while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), &prot)) { + if (name[0] != 0 && name[0] != '[' + && (prot & MemoryMappingLayout::kProtectionRead) + && (prot & MemoryMappingLayout::kProtectionExecute) + && !(prot & MemoryMappingLayout::kProtectionWrite) + && IsAppMem(start)) { + // Assume it's .rodata + char *shadow_start = (char*)MemToShadow(start); + char *shadow_end = (char*)MemToShadow(end); + for (char *p = shadow_start; p < shadow_end; p += marker.size()) { + internal_mmap(p, Min(marker.size(), shadow_end - p), + PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0); + } + } + } + internal_close(fd); + internal_unlink(filename); +} + +void InitializeShadowMemory() { + uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg, + kLinuxShadowEnd - kLinuxShadowBeg); + if (shadow != kLinuxShadowBeg) { + Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); + Printf("FATAL: Make sure to compile with -fPIE and " + "to link with -pie (%p, %p).\n", shadow, kLinuxShadowBeg); + Die(); + } + const uptr kClosedLowBeg = 0x200000; + const uptr kClosedLowEnd = kLinuxShadowBeg - 1; + const uptr kClosedMidBeg = kLinuxShadowEnd + 1; + const uptr kClosedMidEnd = min(kLinuxAppMemBeg, kTraceMemBegin); + ProtectRange(kClosedLowBeg, kClosedLowEnd); + ProtectRange(kClosedMidBeg, kClosedMidEnd); + DPrintf("kClosedLow %zx-%zx (%zuGB)\n", + kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30); + DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n", + kLinuxShadowBeg, kLinuxShadowEnd, + (kLinuxShadowEnd - kLinuxShadowBeg) >> 30); + DPrintf("kClosedMid %zx-%zx (%zuGB)\n", + kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30); + DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n", + kLinuxAppMemBeg, kLinuxAppMemEnd, + (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30); + DPrintf("stack %zx\n", (uptr)&shadow); + + MapRodata(); +} +#endif + +static uptr g_data_start; +static uptr g_data_end; + +#ifndef TSAN_GO +static void CheckPIE() { + // Ensure that the binary is indeed compiled with -pie. + MemoryMappingLayout proc_maps(true); + uptr start, end; + if (proc_maps.Next(&start, &end, + /*offset*/0, /*filename*/0, /*filename_size*/0, + /*protection*/0)) { + if ((u64)start < kLinuxAppMemBeg) { + Printf("FATAL: ThreadSanitizer can not mmap the shadow memory (" + "something is mapped at 0x%zx < 0x%zx)\n", + start, kLinuxAppMemBeg); + Printf("FATAL: Make sure to compile with -fPIE" + " and to link with -pie.\n"); + Die(); + } + } +} + +static void InitDataSeg() { + MemoryMappingLayout proc_maps(true); + uptr start, end, offset; + char name[128]; + bool prev_is_data = false; + while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), + /*protection*/ 0)) { + DPrintf("%p-%p %p %s\n", start, end, offset, name); + bool is_data = offset != 0 && name[0] != 0; + // BSS may get merged with [heap] in /proc/self/maps. This is not very + // reliable. + bool is_bss = offset == 0 && + (name[0] == 0 || internal_strcmp(name, "[heap]") == 0) && prev_is_data; + if (g_data_start == 0 && is_data) + g_data_start = start; + if (is_bss) + g_data_end = end; + prev_is_data = is_data; + } + DPrintf("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); +} + +#endif // #ifndef TSAN_GO + +static rlim_t getlim(int res) { + rlimit rlim; + CHECK_EQ(0, getrlimit(res, &rlim)); + return rlim.rlim_cur; +} + +static void setlim(int res, rlim_t lim) { + // The following magic is to prevent clang from replacing it with memset. + volatile rlimit rlim; + rlim.rlim_cur = lim; + rlim.rlim_max = lim; + setrlimit(res, (rlimit*)&rlim); +} + +const char *InitializePlatform() { + void *p = 0; + if (sizeof(p) == 8) { + // Disable core dumps, dumping of 16TB usually takes a bit long. + setlim(RLIMIT_CORE, 0); + } + + // Go maps shadow memory lazily and works fine with limited address space. + // Unlimited stack is not a problem as well, because the executable + // is not compiled with -pie. + if (kCppMode) { + bool reexec = false; + // TSan doesn't play well with unlimited stack size (as stack + // overlaps with shadow memory). If we detect unlimited stack size, + // we re-exec the program with limited stack size as a best effort. + if (getlim(RLIMIT_STACK) == (rlim_t)-1) { + const uptr kMaxStackSize = 32 * 1024 * 1024; + Report("WARNING: Program is run with unlimited stack size, which " + "wouldn't work with ThreadSanitizer.\n"); + Report("Re-execing with stack size limited to %zd bytes.\n", + kMaxStackSize); + SetStackSizeLimitInBytes(kMaxStackSize); + reexec = true; + } + + if (getlim(RLIMIT_AS) != (rlim_t)-1) { + Report("WARNING: Program is run with limited virtual address space," + " which wouldn't work with ThreadSanitizer.\n"); + Report("Re-execing with unlimited virtual address space.\n"); + setlim(RLIMIT_AS, -1); + reexec = true; + } + if (reexec) + ReExec(); + } + +#ifndef TSAN_GO + CheckPIE(); + InitTlsSize(); + InitDataSeg(); +#endif + return GetEnv(kTsanOptionsEnv); +} + +bool IsGlobalVar(uptr addr) { + return g_data_start && addr >= g_data_start && addr < g_data_end; +} + +#ifndef TSAN_GO +int ExtractResolvFDs(void *state, int *fds, int nfd) { + int cnt = 0; + __res_state *statp = (__res_state*)state; + for (int i = 0; i < MAXNS && cnt < nfd; i++) { + if (statp->_u._ext.nsaddrs[i] && statp->_u._ext.nssocks[i] != -1) + fds[cnt++] = statp->_u._ext.nssocks[i]; + } + return cnt; +} +#endif + + +} // namespace __tsan + +#endif // SANITIZER_LINUX diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc new file mode 100644 index 00000000..99d4533a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc @@ -0,0 +1,95 @@ +//===-- tsan_platform_mac.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Mac-specific code. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" +#include "tsan_flags.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace __tsan { + +ScopedInRtl::ScopedInRtl() { +} + +ScopedInRtl::~ScopedInRtl() { +} + +uptr GetShadowMemoryConsumption() { + return 0; +} + +void FlushShadowMemory() { +} + +#ifndef TSAN_GO +void InitializeShadowMemory() { + uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg, + kLinuxShadowEnd - kLinuxShadowBeg); + if (shadow != kLinuxShadowBeg) { + Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); + Printf("FATAL: Make sure to compile with -fPIE and " + "to link with -pie.\n"); + Die(); + } + DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n", + kLinuxShadowBeg, kLinuxShadowEnd, + (kLinuxShadowEnd - kLinuxShadowBeg) >> 30); + DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n", + kLinuxAppMemBeg, kLinuxAppMemEnd, + (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30); +} +#endif + +const char *InitializePlatform() { + void *p = 0; + if (sizeof(p) == 8) { + // Disable core dumps, dumping of 16TB usually takes a bit long. + // The following magic is to prevent clang from replacing it with memset. + volatile rlimit lim; + lim.rlim_cur = 0; + lim.rlim_max = 0; + setrlimit(RLIMIT_CORE, (rlimit*)&lim); + } + + return GetEnv(kTsanOptionsEnv); +} + +void FinalizePlatform() { + fflush(0); +} + +} // namespace __tsan + +#endif // SANITIZER_MAC diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc new file mode 100644 index 00000000..711db72c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc @@ -0,0 +1,47 @@ +//===-- tsan_platform_windows.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Windows-specific code. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS + +#include "tsan_platform.h" + +#include + +namespace __tsan { + +ScopedInRtl::ScopedInRtl() { +} + +ScopedInRtl::~ScopedInRtl() { +} + +uptr GetShadowMemoryConsumption() { + return 0; +} + +void FlushShadowMemory() { +} + +const char *InitializePlatform() { + return GetEnv(kTsanOptionsEnv); +} + +void FinalizePlatform() { + fflush(0); +} + +} // namespace __tsan + +#endif // SANITIZER_WINDOWS diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_report.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_report.cc new file mode 100644 index 00000000..66307ae0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_report.cc @@ -0,0 +1,304 @@ +//===-- tsan_report.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_report.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_report_decorator.h" + +namespace __tsan { + +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *EndWarning() { return Default(); } + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *ThreadDescription() { return Cyan(); } + const char *EndThreadDescription() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Sleep() { return Yellow(); } + const char *EndSleep() { return Default(); } + const char *Mutex() { return Magenta(); } + const char *EndMutex() { return Default(); } +}; + +ReportDesc::ReportDesc() + : stacks(MBlockReportStack) + , mops(MBlockReportMop) + , locs(MBlockReportLoc) + , mutexes(MBlockReportMutex) + , threads(MBlockReportThread) + , sleep() + , count() { +} + +ReportMop::ReportMop() + : mset(MBlockReportMutex) { +} + +ReportDesc::~ReportDesc() { + // FIXME(dvyukov): it must be leaking a lot of memory. +} + +#ifndef TSAN_GO + +const int kThreadBufSize = 32; +const char *thread_name(char *buf, int tid) { + if (tid == 0) + return "main thread"; + internal_snprintf(buf, kThreadBufSize, "thread T%d", tid); + return buf; +} + +static const char *ReportTypeString(ReportType typ) { + if (typ == ReportTypeRace) + return "data race"; + if (typ == ReportTypeVptrRace) + return "data race on vptr (ctor/dtor vs virtual call)"; + if (typ == ReportTypeUseAfterFree) + return "heap-use-after-free"; + if (typ == ReportTypeThreadLeak) + return "thread leak"; + if (typ == ReportTypeMutexDestroyLocked) + return "destroy of a locked mutex"; + if (typ == ReportTypeSignalUnsafe) + return "signal-unsafe call inside of a signal"; + if (typ == ReportTypeErrnoInSignal) + return "signal handler spoils errno"; + return ""; +} + +void PrintStack(const ReportStack *ent) { + if (ent == 0) { + Printf(" [failed to restore the stack]\n\n"); + return; + } + for (int i = 0; ent; ent = ent->next, i++) { + Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line); + if (ent->col) + Printf(":%d", ent->col); + if (ent->module && ent->offset) + Printf(" (%s+%p)\n", ent->module, (void*)ent->offset); + else + Printf(" (%p)\n", (void*)ent->pc); + } + Printf("\n"); +} + +static void PrintMutexSet(Vector const& mset) { + for (uptr i = 0; i < mset.Size(); i++) { + if (i == 0) + Printf(" (mutexes:"); + const ReportMopMutex m = mset[i]; + Printf(" %s M%llu", m.write ? "write" : "read", m.id); + Printf(i == mset.Size() - 1 ? ")" : ","); + } +} + +static const char *MopDesc(bool first, bool write, bool atomic) { + return atomic ? (first ? (write ? "Atomic write" : "Atomic read") + : (write ? "Previous atomic write" : "Previous atomic read")) + : (first ? (write ? "Write" : "Read") + : (write ? "Previous write" : "Previous read")); +} + +static void PrintMop(const ReportMop *mop, bool first) { + Decorator d; + char thrbuf[kThreadBufSize]; + Printf("%s", d.Access()); + Printf(" %s of size %d at %p by %s", + MopDesc(first, mop->write, mop->atomic), + mop->size, (void*)mop->addr, + thread_name(thrbuf, mop->tid)); + PrintMutexSet(mop->mset); + Printf(":\n"); + Printf("%s", d.EndAccess()); + PrintStack(mop->stack); +} + +static void PrintLocation(const ReportLocation *loc) { + Decorator d; + char thrbuf[kThreadBufSize]; + bool print_stack = false; + Printf("%s", d.Location()); + if (loc->type == ReportLocationGlobal) { + Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", + loc->name, loc->size, loc->addr, loc->module, loc->offset); + } else if (loc->type == ReportLocationHeap) { + char thrbuf[kThreadBufSize]; + Printf(" Location is heap block of size %zu at %p allocated by %s:\n", + loc->size, loc->addr, thread_name(thrbuf, loc->tid)); + print_stack = true; + } else if (loc->type == ReportLocationStack) { + Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); + } else if (loc->type == ReportLocationTLS) { + Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid)); + } else if (loc->type == ReportLocationFD) { + Printf(" Location is file descriptor %d created by %s at:\n", + loc->fd, thread_name(thrbuf, loc->tid)); + print_stack = true; + } + Printf("%s", d.EndLocation()); + if (print_stack) + PrintStack(loc->stack); +} + +static void PrintMutex(const ReportMutex *rm) { + Decorator d; + if (rm->destroyed) { + Printf("%s", d.Mutex()); + Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); + Printf("%s", d.EndMutex()); + } else { + Printf("%s", d.Mutex()); + Printf(" Mutex M%llu created at:\n", rm->id); + Printf("%s", d.EndMutex()); + PrintStack(rm->stack); + } +} + +static void PrintThread(const ReportThread *rt) { + Decorator d; + if (rt->id == 0) // Little sense in describing the main thread. + return; + Printf("%s", d.ThreadDescription()); + Printf(" Thread T%d", rt->id); + if (rt->name && rt->name[0] != '\0') + Printf(" '%s'", rt->name); + char thrbuf[kThreadBufSize]; + Printf(" (tid=%zu, %s) created by %s", + rt->pid, rt->running ? "running" : "finished", + thread_name(thrbuf, rt->parent_tid)); + if (rt->stack) + Printf(" at:"); + Printf("\n"); + Printf("%s", d.EndThreadDescription()); + PrintStack(rt->stack); +} + +static void PrintSleep(const ReportStack *s) { + Decorator d; + Printf("%s", d.Sleep()); + Printf(" As if synchronized via sleep:\n"); + Printf("%s", d.EndSleep()); + PrintStack(s); +} + +static ReportStack *ChooseSummaryStack(const ReportDesc *rep) { + if (rep->mops.Size()) + return rep->mops[0]->stack; + if (rep->stacks.Size()) + return rep->stacks[0]; + if (rep->mutexes.Size()) + return rep->mutexes[0]->stack; + if (rep->threads.Size()) + return rep->threads[0]->stack; + return 0; +} + +ReportStack *SkipTsanInternalFrames(ReportStack *ent) { + while (FrameIsInternal(ent) && ent->next) + ent = ent->next; + return ent; +} + +void PrintReport(const ReportDesc *rep) { + Decorator d; + Printf("==================\n"); + const char *rep_typ_str = ReportTypeString(rep->typ); + Printf("%s", d.Warning()); + Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, + (int)internal_getpid()); + Printf("%s", d.EndWarning()); + + for (uptr i = 0; i < rep->stacks.Size(); i++) { + if (i) + Printf(" and:\n"); + PrintStack(rep->stacks[i]); + } + + for (uptr i = 0; i < rep->mops.Size(); i++) + PrintMop(rep->mops[i], i == 0); + + if (rep->sleep) + PrintSleep(rep->sleep); + + for (uptr i = 0; i < rep->locs.Size(); i++) + PrintLocation(rep->locs[i]); + + for (uptr i = 0; i < rep->mutexes.Size(); i++) + PrintMutex(rep->mutexes[i]); + + for (uptr i = 0; i < rep->threads.Size(); i++) + PrintThread(rep->threads[i]); + + if (rep->typ == ReportTypeThreadLeak && rep->count > 1) + Printf(" And %d more similar thread leaks.\n\n", rep->count - 1); + + if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) + ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func); + + Printf("==================\n"); +} + +#else // #ifndef TSAN_GO + +const int kMainThreadId = 1; + +void PrintStack(const ReportStack *ent) { + if (ent == 0) { + Printf(" [failed to restore the stack]\n"); + return; + } + for (int i = 0; ent; ent = ent->next, i++) { + Printf(" %s()\n %s:%d +0x%zx\n", + ent->func, ent->file, ent->line, (void*)ent->offset); + } +} + +static void PrintMop(const ReportMop *mop, bool first) { + Printf("\n"); + Printf("%s by ", + (first ? (mop->write ? "Write" : "Read") + : (mop->write ? "Previous write" : "Previous read"))); + if (mop->tid == kMainThreadId) + Printf("main goroutine:\n"); + else + Printf("goroutine %d:\n", mop->tid); + PrintStack(mop->stack); +} + +static void PrintThread(const ReportThread *rt) { + if (rt->id == kMainThreadId) + return; + Printf("\n"); + Printf("Goroutine %d (%s) created at:\n", + rt->id, rt->running ? "running" : "finished"); + PrintStack(rt->stack); +} + +void PrintReport(const ReportDesc *rep) { + Printf("==================\n"); + Printf("WARNING: DATA RACE"); + for (uptr i = 0; i < rep->mops.Size(); i++) + PrintMop(rep->mops[i], i == 0); + for (uptr i = 0; i < rep->threads.Size(); i++) + PrintThread(rep->threads[i]); + Printf("==================\n"); +} + +#endif + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_report.h b/projects/compiler-rt/lib/tsan/rtl/tsan_report.h new file mode 100644 index 00000000..b2ce0dd6 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_report.h @@ -0,0 +1,121 @@ +//===-- tsan_report.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_REPORT_H +#define TSAN_REPORT_H + +#include "tsan_defs.h" +#include "tsan_vector.h" + +namespace __tsan { + +enum ReportType { + ReportTypeRace, + ReportTypeVptrRace, + ReportTypeUseAfterFree, + ReportTypeThreadLeak, + ReportTypeMutexDestroyLocked, + ReportTypeSignalUnsafe, + ReportTypeErrnoInSignal +}; + +struct ReportStack { + ReportStack *next; + char *module; + uptr offset; + uptr pc; + char *func; + char *file; + int line; + int col; +}; + +struct ReportMopMutex { + u64 id; + bool write; +}; + +struct ReportMop { + int tid; + uptr addr; + int size; + bool write; + bool atomic; + Vector mset; + ReportStack *stack; + + ReportMop(); +}; + +enum ReportLocationType { + ReportLocationGlobal, + ReportLocationHeap, + ReportLocationStack, + ReportLocationTLS, + ReportLocationFD +}; + +struct ReportLocation { + ReportLocationType type; + uptr addr; + uptr size; + char *module; + uptr offset; + int tid; + int fd; + char *name; + char *file; + int line; + ReportStack *stack; +}; + +struct ReportThread { + int id; + uptr pid; + bool running; + char *name; + int parent_tid; + ReportStack *stack; +}; + +struct ReportMutex { + u64 id; + bool destroyed; + ReportStack *stack; +}; + +class ReportDesc { + public: + ReportType typ; + Vector stacks; + Vector mops; + Vector locs; + Vector mutexes; + Vector threads; + ReportStack *sleep; + int count; + + ReportDesc(); + ~ReportDesc(); + + private: + ReportDesc(const ReportDesc&); + void operator = (const ReportDesc&); +}; + +// Format and output the report to the console/log. No additional logic. +void PrintReport(const ReportDesc *rep); +void PrintStack(const ReportStack *stack); + +} // namespace __tsan + +#endif // TSAN_REPORT_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_rtl.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl.cc new file mode 100644 index 00000000..1f66d29a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl.cc @@ -0,0 +1,758 @@ +//===-- tsan_rtl.cc -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Main file (entry points) for the TSan run-time. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_symbolizer.h" +#include "tsan_defs.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" +#include "tsan_suppressions.h" +#include "tsan_symbolize.h" + +volatile int __tsan_resumed = 0; + +extern "C" void __tsan_resume() { + __tsan_resumed = 1; +} + +namespace __tsan { + +#ifndef TSAN_GO +THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); +#endif +static char ctx_placeholder[sizeof(Context)] ALIGNED(64); + +// Can be overriden by a front-end. +#ifdef TSAN_EXTERNAL_HOOKS +bool OnFinalize(bool failed); +#else +bool WEAK OnFinalize(bool failed) { + return failed; +} +#endif + +static Context *ctx; +Context *CTX() { + return ctx; +} + +static char thread_registry_placeholder[sizeof(ThreadRegistry)]; + +static ThreadContextBase *CreateThreadContext(u32 tid) { + // Map thread trace when context is created. + MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event)); + MapThreadTrace(GetThreadTraceHeader(tid), sizeof(Trace)); + new(ThreadTrace(tid)) Trace(); + void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext)); + return new(mem) ThreadContext(tid); +} + +#ifndef TSAN_GO +static const u32 kThreadQuarantineSize = 16; +#else +static const u32 kThreadQuarantineSize = 64; +#endif + +Context::Context() + : initialized() + , report_mtx(MutexTypeReport, StatMtxReport) + , nreported() + , nmissed_expected() + , thread_registry(new(thread_registry_placeholder) ThreadRegistry( + CreateThreadContext, kMaxTid, kThreadQuarantineSize)) + , racy_stacks(MBlockRacyStacks) + , racy_addresses(MBlockRacyAddresses) + , fired_suppressions(8) { +} + +// The objects are allocated in TLS, so one may rely on zero-initialization. +ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, + uptr stk_addr, uptr stk_size, + uptr tls_addr, uptr tls_size) + : fast_state(tid, epoch) + // Do not touch these, rely on zero initialization, + // they may be accessed before the ctor. + // , ignore_reads_and_writes() + // , in_rtl() +#ifndef TSAN_GO + , jmp_bufs(MBlockJmpBuf) +#endif + , tid(tid) + , unique_id(unique_id) + , stk_addr(stk_addr) + , stk_size(stk_size) + , tls_addr(tls_addr) + , tls_size(tls_size) { +} + +static void MemoryProfiler(Context *ctx, fd_t fd, int i) { + uptr n_threads; + uptr n_running_threads; + ctx->thread_registry->GetNumberOfThreads(&n_threads, &n_running_threads); + InternalScopedBuffer buf(4096); + internal_snprintf(buf.data(), buf.size(), "%d: nthr=%d nlive=%d\n", + i, n_threads, n_running_threads); + internal_write(fd, buf.data(), internal_strlen(buf.data())); + WriteMemoryProfile(buf.data(), buf.size()); + internal_write(fd, buf.data(), internal_strlen(buf.data())); +} + +static void BackgroundThread(void *arg) { + ScopedInRtl in_rtl; + Context *ctx = CTX(); + const u64 kMs2Ns = 1000 * 1000; + + fd_t mprof_fd = kInvalidFd; + if (flags()->profile_memory && flags()->profile_memory[0]) { + InternalScopedBuffer filename(4096); + internal_snprintf(filename.data(), filename.size(), "%s.%d", + flags()->profile_memory, (int)internal_getpid()); + uptr openrv = OpenFile(filename.data(), true); + if (internal_iserror(openrv)) { + Printf("ThreadSanitizer: failed to open memory profile file '%s'\n", + &filename[0]); + } else { + mprof_fd = openrv; + } + } + + u64 last_flush = NanoTime(); + uptr last_rss = 0; + for (int i = 0; ; i++) { + SleepForSeconds(1); + u64 now = NanoTime(); + + // Flush memory if requested. + if (flags()->flush_memory_ms > 0) { + if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: periodic memory flush\n"); + FlushShadowMemory(); + last_flush = NanoTime(); + } + } + if (flags()->memory_limit_mb > 0) { + uptr rss = GetRSS(); + uptr limit = uptr(flags()->memory_limit_mb) << 20; + if (flags()->verbosity > 0) { + Printf("ThreadSanitizer: memory flush check" + " RSS=%llu LAST=%llu LIMIT=%llu\n", + (u64)rss>>20, (u64)last_rss>>20, (u64)limit>>20); + } + if (2 * rss > limit + last_rss) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: flushing memory due to RSS\n"); + FlushShadowMemory(); + rss = GetRSS(); + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20); + } + last_rss = rss; + } + + // Write memory profile if requested. + if (mprof_fd != kInvalidFd) + MemoryProfiler(ctx, mprof_fd, i); + +#ifndef TSAN_GO + // Flush symbolizer cache if requested. + if (flags()->flush_symbolizer_ms > 0) { + u64 last = atomic_load(&ctx->last_symbolize_time_ns, + memory_order_relaxed); + if (last != 0 && last + flags()->flush_symbolizer_ms * kMs2Ns < now) { + Lock l(&ctx->report_mtx); + SpinMutexLock l2(&CommonSanitizerReportMutex); + SymbolizeFlush(); + atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed); + } + } +#endif + } +} + +void DontNeedShadowFor(uptr addr, uptr size) { + uptr shadow_beg = MemToShadow(addr); + uptr shadow_end = MemToShadow(addr + size); + FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); +} + +void MapShadow(uptr addr, uptr size) { + MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier); +} + +void MapThreadTrace(uptr addr, uptr size) { + DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size); + CHECK_GE(addr, kTraceMemBegin); + CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize); + uptr addr1 = (uptr)MmapFixedNoReserve(addr, size); + if (addr1 != addr) { + Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n", + addr, size, addr1); + Die(); + } +} + +void Initialize(ThreadState *thr) { + // Thread safe because done before all threads exist. + static bool is_initialized = false; + if (is_initialized) + return; + is_initialized = true; + SanitizerToolName = "ThreadSanitizer"; + // Install tool-specific callbacks in sanitizer_common. + SetCheckFailedCallback(TsanCheckFailed); + + ScopedInRtl in_rtl; +#ifndef TSAN_GO + InitializeAllocator(); +#endif + InitializeInterceptors(); + const char *env = InitializePlatform(); + InitializeMutex(); + InitializeDynamicAnnotations(); + ctx = new(ctx_placeholder) Context; +#ifndef TSAN_GO + InitializeShadowMemory(); +#endif + InitializeFlags(&ctx->flags, env); + // Setup correct file descriptor for error reports. + __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; + bool external_symbolizer_started = + Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); + if (external_symbolizer != 0 && external_symbolizer[0] != '\0' && + !external_symbolizer_started) { + Printf("Failed to start external symbolizer: '%s'\n", + external_symbolizer); + Die(); + } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); +#endif + internal_start_thread(&BackgroundThread, 0); + + if (ctx->flags.verbosity) + Printf("***** Running under ThreadSanitizer v2 (pid %d) *****\n", + (int)internal_getpid()); + + // Initialize thread 0. + int tid = ThreadCreate(thr, 0, 0, true); + CHECK_EQ(tid, 0); + ThreadStart(thr, tid, internal_getpid()); + CHECK_EQ(thr->in_rtl, 1); + ctx->initialized = true; + + if (flags()->stop_on_start) { + Printf("ThreadSanitizer is suspended at startup (pid %d)." + " Call __tsan_resume().\n", + (int)internal_getpid()); + while (__tsan_resumed == 0) {} + } +} + +int Finalize(ThreadState *thr) { + ScopedInRtl in_rtl; + Context *ctx = __tsan::ctx; + bool failed = false; + + if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1) + SleepForMillis(flags()->atexit_sleep_ms); + + // Wait for pending reports. + ctx->report_mtx.Lock(); + CommonSanitizerReportMutex.Lock(); + CommonSanitizerReportMutex.Unlock(); + ctx->report_mtx.Unlock(); + +#ifndef TSAN_GO + if (ctx->flags.verbosity) + AllocatorPrintStats(); +#endif + + ThreadFinalize(thr); + + if (ctx->nreported) { + failed = true; +#ifndef TSAN_GO + Printf("ThreadSanitizer: reported %d warnings\n", ctx->nreported); +#else + Printf("Found %d data race(s)\n", ctx->nreported); +#endif + } + + if (ctx->nmissed_expected) { + failed = true; + Printf("ThreadSanitizer: missed %d expected races\n", + ctx->nmissed_expected); + } + + if (flags()->print_suppressions) + PrintMatchedSuppressions(); +#ifndef TSAN_GO + if (flags()->print_benign) + PrintMatchedBenignRaces(); +#endif + + failed = OnFinalize(failed); + + StatAggregate(ctx->stat, thr->stat); + StatOutput(ctx->stat); + return failed ? flags()->exitcode : 0; +} + +#ifndef TSAN_GO +u32 CurrentStackId(ThreadState *thr, uptr pc) { + if (thr->shadow_stack_pos == 0) // May happen during bootstrap. + return 0; + if (pc) { + thr->shadow_stack_pos[0] = pc; + thr->shadow_stack_pos++; + } + u32 id = StackDepotPut(thr->shadow_stack, + thr->shadow_stack_pos - thr->shadow_stack); + if (pc) + thr->shadow_stack_pos--; + return id; +} +#endif + +void TraceSwitch(ThreadState *thr) { + thr->nomalloc++; + ScopedInRtl in_rtl; + Trace *thr_trace = ThreadTrace(thr->tid); + Lock l(&thr_trace->mtx); + unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts(); + TraceHeader *hdr = &thr_trace->headers[trace]; + hdr->epoch0 = thr->fast_state.epoch(); + hdr->stack0.ObtainCurrent(thr, 0); + hdr->mset0 = thr->mset; + thr->nomalloc--; +} + +Trace *ThreadTrace(int tid) { + return (Trace*)GetThreadTraceHeader(tid); +} + +uptr TraceTopPC(ThreadState *thr) { + Event *events = (Event*)GetThreadTrace(thr->tid); + uptr pc = events[thr->fast_state.GetTracePos()]; + return pc; +} + +uptr TraceSize() { + return (uptr)(1ull << (kTracePartSizeBits + flags()->history_size + 1)); +} + +uptr TraceParts() { + return TraceSize() / kTracePartSize; +} + +#ifndef TSAN_GO +extern "C" void __tsan_trace_switch() { + TraceSwitch(cur_thread()); +} + +extern "C" void __tsan_report_race() { + ReportRace(cur_thread()); +} +#endif + +ALWAYS_INLINE +Shadow LoadShadow(u64 *p) { + u64 raw = atomic_load((atomic_uint64_t*)p, memory_order_relaxed); + return Shadow(raw); +} + +ALWAYS_INLINE +void StoreShadow(u64 *sp, u64 s) { + atomic_store((atomic_uint64_t*)sp, s, memory_order_relaxed); +} + +ALWAYS_INLINE +void StoreIfNotYetStored(u64 *sp, u64 *s) { + StoreShadow(sp, *s); + *s = 0; +} + +static inline void HandleRace(ThreadState *thr, u64 *shadow_mem, + Shadow cur, Shadow old) { + thr->racy_state[0] = cur.raw(); + thr->racy_state[1] = old.raw(); + thr->racy_shadow_addr = shadow_mem; +#ifndef TSAN_GO + HACKY_CALL(__tsan_report_race); +#else + ReportRace(thr); +#endif +} + +static inline bool OldIsInSameSynchEpoch(Shadow old, ThreadState *thr) { + return old.epoch() >= thr->fast_synch_epoch; +} + +static inline bool HappensBefore(Shadow old, ThreadState *thr) { + return thr->clock.get(old.TidWithIgnore()) >= old.epoch(); +} + +ALWAYS_INLINE USED +void MemoryAccessImpl(ThreadState *thr, uptr addr, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, + u64 *shadow_mem, Shadow cur) { + StatInc(thr, StatMop); + StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); + StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); + + // This potentially can live in an MMX/SSE scratch register. + // The required intrinsics are: + // __m128i _mm_move_epi64(__m128i*); + // _mm_storel_epi64(u64*, __m128i); + u64 store_word = cur.raw(); + + // scan all the shadow values and dispatch to 4 categories: + // same, replace, candidate and race (see comments below). + // we consider only 3 cases regarding access sizes: + // equal, intersect and not intersect. initially I considered + // larger and smaller as well, it allowed to replace some + // 'candidates' with 'same' or 'replace', but I think + // it's just not worth it (performance- and complexity-wise). + + Shadow old(0); + if (kShadowCnt == 1) { + int idx = 0; +#include "tsan_update_shadow_word_inl.h" + } else if (kShadowCnt == 2) { + int idx = 0; +#include "tsan_update_shadow_word_inl.h" + idx = 1; +#include "tsan_update_shadow_word_inl.h" + } else if (kShadowCnt == 4) { + int idx = 0; +#include "tsan_update_shadow_word_inl.h" + idx = 1; +#include "tsan_update_shadow_word_inl.h" + idx = 2; +#include "tsan_update_shadow_word_inl.h" + idx = 3; +#include "tsan_update_shadow_word_inl.h" + } else if (kShadowCnt == 8) { + int idx = 0; +#include "tsan_update_shadow_word_inl.h" + idx = 1; +#include "tsan_update_shadow_word_inl.h" + idx = 2; +#include "tsan_update_shadow_word_inl.h" + idx = 3; +#include "tsan_update_shadow_word_inl.h" + idx = 4; +#include "tsan_update_shadow_word_inl.h" + idx = 5; +#include "tsan_update_shadow_word_inl.h" + idx = 6; +#include "tsan_update_shadow_word_inl.h" + idx = 7; +#include "tsan_update_shadow_word_inl.h" + } else { + CHECK(false); + } + + // we did not find any races and had already stored + // the current access info, so we are done + if (LIKELY(store_word == 0)) + return; + // choose a random candidate slot and replace it + StoreShadow(shadow_mem + (cur.epoch() % kShadowCnt), store_word); + StatInc(thr, StatShadowReplace); + return; + RACE: + HandleRace(thr, shadow_mem, cur, old); + return; +} + +void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr, + int size, bool kAccessIsWrite, bool kIsAtomic) { + while (size) { + int size1 = 1; + int kAccessSizeLog = kSizeLog1; + if (size >= 8 && (addr & ~7) == ((addr + 8) & ~7)) { + size1 = 8; + kAccessSizeLog = kSizeLog8; + } else if (size >= 4 && (addr & ~7) == ((addr + 4) & ~7)) { + size1 = 4; + kAccessSizeLog = kSizeLog4; + } else if (size >= 2 && (addr & ~7) == ((addr + 2) & ~7)) { + size1 = 2; + kAccessSizeLog = kSizeLog2; + } + MemoryAccess(thr, pc, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic); + addr += size1; + size -= size1; + } +} + +ALWAYS_INLINE USED +void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) { + u64 *shadow_mem = (u64*)MemToShadow(addr); + DPrintf2("#%d: MemoryAccess: @%p %p size=%d" + " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n", + (int)thr->fast_state.tid(), (void*)pc, (void*)addr, + (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem, + (uptr)shadow_mem[0], (uptr)shadow_mem[1], + (uptr)shadow_mem[2], (uptr)shadow_mem[3]); +#if TSAN_DEBUG + if (!IsAppMem(addr)) { + Printf("Access to non app mem %zx\n", addr); + DCHECK(IsAppMem(addr)); + } + if (!IsShadowMem((uptr)shadow_mem)) { + Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr); + DCHECK(IsShadowMem((uptr)shadow_mem)); + } +#endif + + if (*shadow_mem == kShadowRodata) { + // Access to .rodata section, no races here. + // Measurements show that it can be 10-20% of all memory accesses. + StatInc(thr, StatMop); + StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); + StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); + StatInc(thr, StatMopRodata); + return; + } + + FastState fast_state = thr->fast_state; + if (fast_state.GetIgnoreBit()) + return; + fast_state.IncrementEpoch(); + thr->fast_state = fast_state; + Shadow cur(fast_state); + cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog); + cur.SetWrite(kAccessIsWrite); + cur.SetAtomic(kIsAtomic); + + // We must not store to the trace if we do not store to the shadow. + // That is, this call must be moved somewhere below. + TraceAddEvent(thr, fast_state, EventTypeMop, pc); + + MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic, + shadow_mem, cur); +} + +static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, + u64 val) { + (void)thr; + (void)pc; + if (size == 0) + return; + // FIXME: fix me. + uptr offset = addr % kShadowCell; + if (offset) { + offset = kShadowCell - offset; + if (size <= offset) + return; + addr += offset; + size -= offset; + } + DCHECK_EQ(addr % 8, 0); + // If a user passes some insane arguments (memset(0)), + // let it just crash as usual. + if (!IsAppMem(addr) || !IsAppMem(addr + size - 1)) + return; + // Don't want to touch lots of shadow memory. + // If a program maps 10MB stack, there is no need reset the whole range. + size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1); + // UnmapOrDie/MmapFixedNoReserve does not work on Windows, + // so we do it only for C/C++. + if (kGoMode || size < 64*1024) { + u64 *p = (u64*)MemToShadow(addr); + CHECK(IsShadowMem((uptr)p)); + CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); + // FIXME: may overwrite a part outside the region + for (uptr i = 0; i < size / kShadowCell * kShadowCnt;) { + p[i++] = val; + for (uptr j = 1; j < kShadowCnt; j++) + p[i++] = 0; + } + } else { + // The region is big, reset only beginning and end. + const uptr kPageSize = 4096; + u64 *begin = (u64*)MemToShadow(addr); + u64 *end = begin + size / kShadowCell * kShadowCnt; + u64 *p = begin; + // Set at least first kPageSize/2 to page boundary. + while ((p < begin + kPageSize / kShadowSize / 2) || ((uptr)p % kPageSize)) { + *p++ = val; + for (uptr j = 1; j < kShadowCnt; j++) + *p++ = 0; + } + // Reset middle part. + u64 *p1 = p; + p = RoundDown(end, kPageSize); + UnmapOrDie((void*)p1, (uptr)p - (uptr)p1); + MmapFixedNoReserve((uptr)p1, (uptr)p - (uptr)p1); + // Set the ending. + while (p < end) { + *p++ = val; + for (uptr j = 1; j < kShadowCnt; j++) + *p++ = 0; + } + } +} + +void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) { + MemoryRangeSet(thr, pc, addr, size, 0); +} + +void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) { + // Processing more than 1k (4k of shadow) is expensive, + // can cause excessive memory consumption (user does not necessary touch + // the whole range) and most likely unnecessary. + if (size > 1024) + size = 1024; + CHECK_EQ(thr->is_freeing, false); + thr->is_freeing = true; + MemoryAccessRange(thr, pc, addr, size, true); + thr->is_freeing = false; + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc); + Shadow s(thr->fast_state); + s.ClearIgnoreBit(); + s.MarkAsFreed(); + s.SetWrite(true); + s.SetAddr0AndSizeLog(0, 3); + MemoryRangeSet(thr, pc, addr, size, s.raw()); +} + +void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) { + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc); + Shadow s(thr->fast_state); + s.ClearIgnoreBit(); + s.SetWrite(true); + s.SetAddr0AndSizeLog(0, 3); + MemoryRangeSet(thr, pc, addr, size, s.raw()); +} + +ALWAYS_INLINE USED +void FuncEntry(ThreadState *thr, uptr pc) { + DCHECK_EQ(thr->in_rtl, 0); + StatInc(thr, StatFuncEnter); + DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc); + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeFuncEnter, pc); + + // Shadow stack maintenance can be replaced with + // stack unwinding during trace switch (which presumably must be faster). + DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack); +#ifndef TSAN_GO + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); +#else + if (thr->shadow_stack_pos == thr->shadow_stack_end) { + const int sz = thr->shadow_stack_end - thr->shadow_stack; + const int newsz = 2 * sz; + uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack, + newsz * sizeof(uptr)); + internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr)); + internal_free(thr->shadow_stack); + thr->shadow_stack = newstack; + thr->shadow_stack_pos = newstack + sz; + thr->shadow_stack_end = newstack + newsz; + } +#endif + thr->shadow_stack_pos[0] = pc; + thr->shadow_stack_pos++; +} + +ALWAYS_INLINE USED +void FuncExit(ThreadState *thr) { + DCHECK_EQ(thr->in_rtl, 0); + StatInc(thr, StatFuncExit); + DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid()); + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0); + + DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack); +#ifndef TSAN_GO + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); +#endif + thr->shadow_stack_pos--; +} + +void ThreadIgnoreBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); + thr->ignore_reads_and_writes++; + CHECK_GT(thr->ignore_reads_and_writes, 0); + thr->fast_state.SetIgnoreBit(); +} + +void ThreadIgnoreEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); + thr->ignore_reads_and_writes--; + CHECK_GE(thr->ignore_reads_and_writes, 0); + if (thr->ignore_reads_and_writes == 0) + thr->fast_state.ClearIgnoreBit(); +} + +void ThreadIgnoreSyncBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); + thr->ignore_sync++; + CHECK_GT(thr->ignore_sync, 0); +} + +void ThreadIgnoreSyncEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); + thr->ignore_sync--; + CHECK_GE(thr->ignore_sync, 0); +} + +bool MD5Hash::operator==(const MD5Hash &other) const { + return hash[0] == other.hash[0] && hash[1] == other.hash[1]; +} + +#if TSAN_DEBUG +void build_consistency_debug() {} +#else +void build_consistency_release() {} +#endif + +#if TSAN_COLLECT_STATS +void build_consistency_stats() {} +#else +void build_consistency_nostats() {} +#endif + +#if TSAN_SHADOW_COUNT == 1 +void build_consistency_shadow1() {} +#elif TSAN_SHADOW_COUNT == 2 +void build_consistency_shadow2() {} +#elif TSAN_SHADOW_COUNT == 4 +void build_consistency_shadow4() {} +#else +void build_consistency_shadow8() {} +#endif + +} // namespace __tsan + +#ifndef TSAN_GO +// Must be included in this file to make sure everything is inlined. +#include "tsan_interface_inl.h" +#endif diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl.h new file mode 100644 index 00000000..4ee66754 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl.h @@ -0,0 +1,770 @@ +//===-- tsan_rtl.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Main internal TSan header file. +// +// Ground rules: +// - C++ run-time should not be used (static CTORs, RTTI, exceptions, static +// function-scope locals) +// - All functions/classes/etc reside in namespace __tsan, except for those +// declared in tsan_interface.h. +// - Platform-specific files should be used instead of ifdefs (*). +// - No system headers included in header files (*). +// - Platform specific headres included only into platform-specific files (*). +// +// (*) Except when inlining is critical for performance. +//===----------------------------------------------------------------------===// + +#ifndef TSAN_RTL_H +#define TSAN_RTL_H + +#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" +#include "tsan_defs.h" +#include "tsan_flags.h" +#include "tsan_sync.h" +#include "tsan_trace.h" +#include "tsan_vector.h" +#include "tsan_report.h" +#include "tsan_platform.h" +#include "tsan_mutexset.h" + +#if SANITIZER_WORDSIZE != 64 +# error "ThreadSanitizer is supported only on 64-bit platforms" +#endif + +namespace __tsan { + +// Descriptor of user's memory block. +struct MBlock { + /* + u64 mtx : 1; // must be first + u64 lst : 44; + u64 stk : 31; // on word boundary + u64 tid : kTidBits; + u64 siz : 128 - 1 - 31 - 44 - kTidBits; // 39 + */ + u64 raw[2]; + + void Init(uptr siz, u32 tid, u32 stk) { + raw[0] = raw[1] = 0; + raw[1] |= (u64)siz << ((1 + 44 + 31 + kTidBits) % 64); + raw[1] |= (u64)tid << ((1 + 44 + 31) % 64); + raw[0] |= (u64)stk << (1 + 44); + raw[1] |= (u64)stk >> (64 - 44 - 1); + DCHECK_EQ(Size(), siz); + DCHECK_EQ(Tid(), tid); + DCHECK_EQ(StackId(), stk); + } + + u32 Tid() const { + return GetLsb(raw[1] >> ((1 + 44 + 31) % 64), kTidBits); + } + + uptr Size() const { + return raw[1] >> ((1 + 31 + 44 + kTidBits) % 64); + } + + u32 StackId() const { + return (raw[0] >> (1 + 44)) | GetLsb(raw[1] << (64 - 44 - 1), 31); + } + + SyncVar *ListHead() const { + return (SyncVar*)(GetLsb(raw[0] >> 1, 44) << 3); + } + + void ListPush(SyncVar *v) { + SyncVar *lst = ListHead(); + v->next = lst; + u64 x = (u64)v ^ (u64)lst; + x = (x >> 3) << 1; + raw[0] ^= x; + DCHECK_EQ(ListHead(), v); + } + + SyncVar *ListPop() { + SyncVar *lst = ListHead(); + SyncVar *nxt = lst->next; + lst->next = 0; + u64 x = (u64)lst ^ (u64)nxt; + x = (x >> 3) << 1; + raw[0] ^= x; + DCHECK_EQ(ListHead(), nxt); + return lst; + } + + void ListReset() { + SyncVar *lst = ListHead(); + u64 x = (u64)lst; + x = (x >> 3) << 1; + raw[0] ^= x; + DCHECK_EQ(ListHead(), 0); + } + + void Lock(); + void Unlock(); + typedef GenericScopedLock ScopedLock; +}; + +#ifndef TSAN_GO +#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW +const uptr kAllocatorSpace = 0x7d0000000000ULL; +#else +const uptr kAllocatorSpace = 0x7d0000000000ULL; +#endif +const uptr kAllocatorSize = 0x10000000000ULL; // 1T. + +struct MapUnmapCallback; +typedef SizeClassAllocator64 PrimaryAllocator; +typedef SizeClassAllocatorLocalCache AllocatorCache; +typedef LargeMmapAllocator SecondaryAllocator; +typedef CombinedAllocator Allocator; +Allocator *allocator(); +#endif + +void TsanCheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2); + +const u64 kShadowRodata = (u64)-1; // .rodata shadow marker + +// FastState (from most significant bit): +// ignore : 1 +// tid : kTidBits +// epoch : kClkBits +// unused : - +// history_size : 3 +class FastState { + public: + FastState(u64 tid, u64 epoch) { + x_ = tid << kTidShift; + x_ |= epoch << kClkShift; + DCHECK_EQ(tid, this->tid()); + DCHECK_EQ(epoch, this->epoch()); + DCHECK_EQ(GetIgnoreBit(), false); + } + + explicit FastState(u64 x) + : x_(x) { + } + + u64 raw() const { + return x_; + } + + u64 tid() const { + u64 res = (x_ & ~kIgnoreBit) >> kTidShift; + return res; + } + + u64 TidWithIgnore() const { + u64 res = x_ >> kTidShift; + return res; + } + + u64 epoch() const { + u64 res = (x_ << (kTidBits + 1)) >> (64 - kClkBits); + return res; + } + + void IncrementEpoch() { + u64 old_epoch = epoch(); + x_ += 1 << kClkShift; + DCHECK_EQ(old_epoch + 1, epoch()); + (void)old_epoch; + } + + void SetIgnoreBit() { x_ |= kIgnoreBit; } + void ClearIgnoreBit() { x_ &= ~kIgnoreBit; } + bool GetIgnoreBit() const { return (s64)x_ < 0; } + + void SetHistorySize(int hs) { + CHECK_GE(hs, 0); + CHECK_LE(hs, 7); + x_ = (x_ & ~7) | hs; + } + + int GetHistorySize() const { + return (int)(x_ & 7); + } + + void ClearHistorySize() { + x_ &= ~7; + } + + u64 GetTracePos() const { + const int hs = GetHistorySize(); + // When hs == 0, the trace consists of 2 parts. + const u64 mask = (1ull << (kTracePartSizeBits + hs + 1)) - 1; + return epoch() & mask; + } + + private: + friend class Shadow; + static const int kTidShift = 64 - kTidBits - 1; + static const int kClkShift = kTidShift - kClkBits; + static const u64 kIgnoreBit = 1ull << 63; + static const u64 kFreedBit = 1ull << 63; + u64 x_; +}; + +// Shadow (from most significant bit): +// freed : 1 +// tid : kTidBits +// epoch : kClkBits +// is_atomic : 1 +// is_read : 1 +// size_log : 2 +// addr0 : 3 +class Shadow : public FastState { + public: + explicit Shadow(u64 x) + : FastState(x) { + } + + explicit Shadow(const FastState &s) + : FastState(s.x_) { + ClearHistorySize(); + } + + void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) { + DCHECK_EQ(x_ & 31, 0); + DCHECK_LE(addr0, 7); + DCHECK_LE(kAccessSizeLog, 3); + x_ |= (kAccessSizeLog << 3) | addr0; + DCHECK_EQ(kAccessSizeLog, size_log()); + DCHECK_EQ(addr0, this->addr0()); + } + + void SetWrite(unsigned kAccessIsWrite) { + DCHECK_EQ(x_ & kReadBit, 0); + if (!kAccessIsWrite) + x_ |= kReadBit; + DCHECK_EQ(kAccessIsWrite, IsWrite()); + } + + void SetAtomic(bool kIsAtomic) { + DCHECK(!IsAtomic()); + if (kIsAtomic) + x_ |= kAtomicBit; + DCHECK_EQ(IsAtomic(), kIsAtomic); + } + + bool IsAtomic() const { + return x_ & kAtomicBit; + } + + bool IsZero() const { + return x_ == 0; + } + + static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) { + u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift; + DCHECK_EQ(shifted_xor == 0, s1.TidWithIgnore() == s2.TidWithIgnore()); + return shifted_xor == 0; + } + + static inline bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) { + u64 masked_xor = (s1.x_ ^ s2.x_) & 31; + return masked_xor == 0; + } + + static inline bool TwoRangesIntersect(Shadow s1, Shadow s2, + unsigned kS2AccessSize) { + bool res = false; + u64 diff = s1.addr0() - s2.addr0(); + if ((s64)diff < 0) { // s1.addr0 < s2.addr0 // NOLINT + // if (s1.addr0() + size1) > s2.addr0()) return true; + if (s1.size() > -diff) res = true; + } else { + // if (s2.addr0() + kS2AccessSize > s1.addr0()) return true; + if (kS2AccessSize > diff) res = true; + } + DCHECK_EQ(res, TwoRangesIntersectSLOW(s1, s2)); + DCHECK_EQ(res, TwoRangesIntersectSLOW(s2, s1)); + return res; + } + + // The idea behind the offset is as follows. + // Consider that we have 8 bool's contained within a single 8-byte block + // (mapped to a single shadow "cell"). Now consider that we write to the bools + // from a single thread (which we consider the common case). + // W/o offsetting each access will have to scan 4 shadow values at average + // to find the corresponding shadow value for the bool. + // With offsetting we start scanning shadow with the offset so that + // each access hits necessary shadow straight off (at least in an expected + // optimistic case). + // This logic works seamlessly for any layout of user data. For example, + // if user data is {int, short, char, char}, then accesses to the int are + // offsetted to 0, short - 4, 1st char - 6, 2nd char - 7. Hopefully, accesses + // from a single thread won't need to scan all 8 shadow values. + unsigned ComputeSearchOffset() { + return x_ & 7; + } + u64 addr0() const { return x_ & 7; } + u64 size() const { return 1ull << size_log(); } + bool IsWrite() const { return !IsRead(); } + bool IsRead() const { return x_ & kReadBit; } + + // The idea behind the freed bit is as follows. + // When the memory is freed (or otherwise unaccessible) we write to the shadow + // values with tid/epoch related to the free and the freed bit set. + // During memory accesses processing the freed bit is considered + // as msb of tid. So any access races with shadow with freed bit set + // (it is as if write from a thread with which we never synchronized before). + // This allows us to detect accesses to freed memory w/o additional + // overheads in memory access processing and at the same time restore + // tid/epoch of free. + void MarkAsFreed() { + x_ |= kFreedBit; + } + + bool IsFreed() const { + return x_ & kFreedBit; + } + + bool GetFreedAndReset() { + bool res = x_ & kFreedBit; + x_ &= ~kFreedBit; + return res; + } + + bool IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const { + // analyzes 5-th bit (is_read) and 6-th bit (is_atomic) + bool v = x_ & u64(((kIsWrite ^ 1) << kReadShift) + | (kIsAtomic << kAtomicShift)); + DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic)); + return v; + } + + bool IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const { + bool v = ((x_ >> kReadShift) & 3) + <= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); + DCHECK_EQ(v, (IsAtomic() < kIsAtomic) || + (IsAtomic() == kIsAtomic && !IsWrite() <= !kIsWrite)); + return v; + } + + bool IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const { + bool v = ((x_ >> kReadShift) & 3) + >= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); + DCHECK_EQ(v, (IsAtomic() > kIsAtomic) || + (IsAtomic() == kIsAtomic && !IsWrite() >= !kIsWrite)); + return v; + } + + private: + static const u64 kReadShift = 5; + static const u64 kReadBit = 1ull << kReadShift; + static const u64 kAtomicShift = 6; + static const u64 kAtomicBit = 1ull << kAtomicShift; + + u64 size_log() const { return (x_ >> 3) & 3; } + + static bool TwoRangesIntersectSLOW(const Shadow s1, const Shadow s2) { + if (s1.addr0() == s2.addr0()) return true; + if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0()) + return true; + if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0()) + return true; + return false; + } +}; + +struct SignalContext; + +struct JmpBuf { + uptr sp; + uptr mangled_sp; + uptr *shadow_stack_pos; +}; + +// This struct is stored in TLS. +struct ThreadState { + FastState fast_state; + // Synch epoch represents the threads's epoch before the last synchronization + // action. It allows to reduce number of shadow state updates. + // For example, fast_synch_epoch=100, last write to addr X was at epoch=150, + // if we are processing write to X from the same thread at epoch=200, + // we do nothing, because both writes happen in the same 'synch epoch'. + // That is, if another memory access does not race with the former write, + // it does not race with the latter as well. + // QUESTION: can we can squeeze this into ThreadState::Fast? + // E.g. ThreadState::Fast is a 44-bit, 32 are taken by synch_epoch and 12 are + // taken by epoch between synchs. + // This way we can save one load from tls. + u64 fast_synch_epoch; + // This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read. + // We do not distinguish beteween ignoring reads and writes + // for better performance. + int ignore_reads_and_writes; + int ignore_sync; + // C/C++ uses fixed size shadow stack embed into Trace. + // Go uses malloc-allocated shadow stack with dynamic size. + uptr *shadow_stack; + uptr *shadow_stack_end; + uptr *shadow_stack_pos; + u64 *racy_shadow_addr; + u64 racy_state[2]; + MutexSet mset; + ThreadClock clock; +#ifndef TSAN_GO + AllocatorCache alloc_cache; + InternalAllocatorCache internal_alloc_cache; + Vector jmp_bufs; +#endif + u64 stat[StatCnt]; + const int tid; + const int unique_id; + int in_rtl; + bool in_symbolizer; + bool in_ignored_lib; + bool is_alive; + bool is_freeing; + bool is_vptr_access; + const uptr stk_addr; + const uptr stk_size; + const uptr tls_addr; + const uptr tls_size; + + DeadlockDetector deadlock_detector; + + bool in_signal_handler; + SignalContext *signal_ctx; + +#ifndef TSAN_GO + u32 last_sleep_stack_id; + ThreadClock last_sleep_clock; +#endif + + // Set in regions of runtime that must be signal-safe and fork-safe. + // If set, malloc must not be called. + int nomalloc; + + explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, + uptr stk_addr, uptr stk_size, + uptr tls_addr, uptr tls_size); +}; + +Context *CTX(); + +#ifndef TSAN_GO +extern THREADLOCAL char cur_thread_placeholder[]; +INLINE ThreadState *cur_thread() { + return reinterpret_cast(&cur_thread_placeholder); +} +#endif + +class ThreadContext : public ThreadContextBase { + public: + explicit ThreadContext(int tid); + ~ThreadContext(); + ThreadState *thr; +#ifdef TSAN_GO + StackTrace creation_stack; +#else + u32 creation_stack_id; +#endif + SyncClock sync; + // Epoch at which the thread had started. + // If we see an event from the thread stamped by an older epoch, + // the event is from a dead thread that shared tid with this thread. + u64 epoch0; + u64 epoch1; + + // Override superclass callbacks. + void OnDead(); + void OnJoined(void *arg); + void OnFinished(); + void OnStarted(void *arg); + void OnCreated(void *arg); + void OnReset(); +}; + +struct RacyStacks { + MD5Hash hash[2]; + bool operator==(const RacyStacks &other) const { + if (hash[0] == other.hash[0] && hash[1] == other.hash[1]) + return true; + if (hash[0] == other.hash[1] && hash[1] == other.hash[0]) + return true; + return false; + } +}; + +struct RacyAddress { + uptr addr_min; + uptr addr_max; +}; + +struct FiredSuppression { + ReportType type; + uptr pc; + Suppression *supp; +}; + +struct Context { + Context(); + + bool initialized; + + SyncTab synctab; + + Mutex report_mtx; + int nreported; + int nmissed_expected; + atomic_uint64_t last_symbolize_time_ns; + + ThreadRegistry *thread_registry; + + Vector racy_stacks; + Vector racy_addresses; + // Number of fired suppressions may be large enough. + InternalMmapVector fired_suppressions; + + Flags flags; + + u64 stat[StatCnt]; + u64 int_alloc_cnt[MBlockTypeCount]; + u64 int_alloc_siz[MBlockTypeCount]; +}; + +class ScopedInRtl { + public: + ScopedInRtl(); + ~ScopedInRtl(); + private: + ThreadState*thr_; + int in_rtl_; + int errno_; +}; + +class ScopedReport { + public: + explicit ScopedReport(ReportType typ); + ~ScopedReport(); + + void AddStack(const StackTrace *stack); + void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack, + const MutexSet *mset); + void AddThread(const ThreadContext *tctx); + void AddMutex(const SyncVar *s); + void AddLocation(uptr addr, uptr size); + void AddSleep(u32 stack_id); + void SetCount(int count); + + const ReportDesc *GetReport() const; + + private: + Context *ctx_; + ReportDesc *rep_; + + void AddMutex(u64 id); + + ScopedReport(const ScopedReport&); + void operator = (const ScopedReport&); +}; + +void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset); + +void StatAggregate(u64 *dst, u64 *src); +void StatOutput(u64 *stat); +void ALWAYS_INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) { + if (kCollectStats) + thr->stat[typ] += n; +} +void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) { + if (kCollectStats) + thr->stat[typ] = n; +} + +void MapShadow(uptr addr, uptr size); +void MapThreadTrace(uptr addr, uptr size); +void DontNeedShadowFor(uptr addr, uptr size); +void InitializeShadowMemory(); +void InitializeInterceptors(); +void InitializeLibIgnore(); +void InitializeDynamicAnnotations(); + +void ReportRace(ThreadState *thr); +bool OutputReport(Context *ctx, + const ScopedReport &srep, + const ReportStack *suppress_stack1 = 0, + const ReportStack *suppress_stack2 = 0, + const ReportLocation *suppress_loc = 0); +bool IsFiredSuppression(Context *ctx, + const ScopedReport &srep, + const StackTrace &trace); +bool IsExpectedReport(uptr addr, uptr size); +void PrintMatchedBenignRaces(); +bool FrameIsInternal(const ReportStack *frame); +ReportStack *SkipTsanInternalFrames(ReportStack *ent); + +#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 +# define DPrintf Printf +#else +# define DPrintf(...) +#endif + +#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 2 +# define DPrintf2 Printf +#else +# define DPrintf2(...) +#endif + +u32 CurrentStackId(ThreadState *thr, uptr pc); +void PrintCurrentStack(ThreadState *thr, uptr pc); +void PrintCurrentStackSlow(); // uses libunwind + +void Initialize(ThreadState *thr); +int Finalize(ThreadState *thr); + +SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr, + bool write_lock, bool create); +SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr); + +void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic); +void MemoryAccessImpl(ThreadState *thr, uptr addr, + int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, + u64 *shadow_mem, Shadow cur); +void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, + uptr size, bool is_write); +void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, + uptr size, uptr step, bool is_write); +void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr, + int size, bool kAccessIsWrite, bool kIsAtomic); + +const int kSizeLog1 = 0; +const int kSizeLog2 = 1; +const int kSizeLog4 = 2; +const int kSizeLog8 = 3; + +void ALWAYS_INLINE MemoryRead(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, false, false); +} + +void ALWAYS_INLINE MemoryWrite(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, true, false); +} + +void ALWAYS_INLINE MemoryReadAtomic(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, false, true); +} + +void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, + uptr addr, int kAccessSizeLog) { + MemoryAccess(thr, pc, addr, kAccessSizeLog, true, true); +} + +void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); +void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); +void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); + +void ThreadIgnoreBegin(ThreadState *thr); +void ThreadIgnoreEnd(ThreadState *thr); +void ThreadIgnoreSyncBegin(ThreadState *thr); +void ThreadIgnoreSyncEnd(ThreadState *thr); + +void FuncEntry(ThreadState *thr, uptr pc); +void FuncExit(ThreadState *thr); + +int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached); +void ThreadStart(ThreadState *thr, int tid, uptr os_id); +void ThreadFinish(ThreadState *thr); +int ThreadTid(ThreadState *thr, uptr pc, uptr uid); +void ThreadJoin(ThreadState *thr, uptr pc, int tid); +void ThreadDetach(ThreadState *thr, uptr pc, int tid); +void ThreadFinalize(ThreadState *thr); +void ThreadSetName(ThreadState *thr, const char *name); +int ThreadCount(ThreadState *thr); +void ProcessPendingSignals(ThreadState *thr); + +void MutexCreate(ThreadState *thr, uptr pc, uptr addr, + bool rw, bool recursive, bool linker_init); +void MutexDestroy(ThreadState *thr, uptr pc, uptr addr); +void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1); +int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); +void MutexReadLock(ThreadState *thr, uptr pc, uptr addr); +void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); +void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); +void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD + +void Acquire(ThreadState *thr, uptr pc, uptr addr); +void AcquireGlobal(ThreadState *thr, uptr pc); +void Release(ThreadState *thr, uptr pc, uptr addr); +void ReleaseStore(ThreadState *thr, uptr pc, uptr addr); +void AfterSleep(ThreadState *thr, uptr pc); +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c); +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); + +// The hacky call uses custom calling convention and an assembly thunk. +// It is considerably faster that a normal call for the caller +// if it is not executed (it is intended for slow paths from hot functions). +// The trick is that the call preserves all registers and the compiler +// does not treat it as a call. +// If it does not work for you, use normal call. +#if TSAN_DEBUG == 0 +// The caller may not create the stack frame for itself at all, +// so we create a reserve stack frame for it (1024b must be enough). +#define HACKY_CALL(f) \ + __asm__ __volatile__("sub $1024, %%rsp;" \ + ".cfi_adjust_cfa_offset 1024;" \ + ".hidden " #f "_thunk;" \ + "call " #f "_thunk;" \ + "add $1024, %%rsp;" \ + ".cfi_adjust_cfa_offset -1024;" \ + ::: "memory", "cc"); +#else +#define HACKY_CALL(f) f() +#endif + +void TraceSwitch(ThreadState *thr); +uptr TraceTopPC(ThreadState *thr); +uptr TraceSize(); +uptr TraceParts(); +Trace *ThreadTrace(int tid); + +extern "C" void __tsan_trace_switch(); +void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs, + EventType typ, u64 addr) { + DCHECK_GE((int)typ, 0); + DCHECK_LE((int)typ, 7); + DCHECK_EQ(GetLsb(addr, 61), addr); + StatInc(thr, StatEvents); + u64 pos = fs.GetTracePos(); + if (UNLIKELY((pos % kTracePartSize) == 0)) { +#ifndef TSAN_GO + HACKY_CALL(__tsan_trace_switch); +#else + TraceSwitch(thr); +#endif + } + Event *trace = (Event*)GetThreadTrace(fs.tid()); + Event *evp = &trace[pos]; + Event ev = (u64)addr | ((u64)typ << 61); + *evp = ev; +} + +} // namespace __tsan + +#endif // TSAN_RTL_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S new file mode 100644 index 00000000..11c75c72 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S @@ -0,0 +1,303 @@ +.section .text + +.hidden __tsan_trace_switch +.globl __tsan_trace_switch_thunk +__tsan_trace_switch_thunk: + .cfi_startproc + # Save scratch registers. + push %rax + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rax, 0 + push %rcx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rcx, 0 + push %rdx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdx, 0 + push %rsi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rsi, 0 + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + push %r8 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r8, 0 + push %r9 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r9, 0 + push %r10 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r10, 0 + push %r11 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r11, 0 + # Align stack frame. + push %rbx # non-scratch + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbx, 0 + mov %rsp, %rbx # save current rsp + .cfi_def_cfa_register %rbx + shr $4, %rsp # clear 4 lsb, align to 16 + shl $4, %rsp + + call __tsan_trace_switch + + # Unalign stack frame back. + mov %rbx, %rsp # restore the original rsp + .cfi_def_cfa_register %rsp + pop %rbx + .cfi_adjust_cfa_offset -8 + # Restore scratch registers. + pop %r11 + .cfi_adjust_cfa_offset -8 + pop %r10 + .cfi_adjust_cfa_offset -8 + pop %r9 + .cfi_adjust_cfa_offset -8 + pop %r8 + .cfi_adjust_cfa_offset -8 + pop %rdi + .cfi_adjust_cfa_offset -8 + pop %rsi + .cfi_adjust_cfa_offset -8 + pop %rdx + .cfi_adjust_cfa_offset -8 + pop %rcx + .cfi_adjust_cfa_offset -8 + pop %rax + .cfi_adjust_cfa_offset -8 + .cfi_restore %rax + .cfi_restore %rbx + .cfi_restore %rcx + .cfi_restore %rdx + .cfi_restore %rsi + .cfi_restore %rdi + .cfi_restore %r8 + .cfi_restore %r9 + .cfi_restore %r10 + .cfi_restore %r11 + ret + .cfi_endproc + +.hidden __tsan_report_race +.globl __tsan_report_race_thunk +__tsan_report_race_thunk: + .cfi_startproc + # Save scratch registers. + push %rax + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rax, 0 + push %rcx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rcx, 0 + push %rdx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdx, 0 + push %rsi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rsi, 0 + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + push %r8 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r8, 0 + push %r9 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r9, 0 + push %r10 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r10, 0 + push %r11 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r11, 0 + # Align stack frame. + push %rbx # non-scratch + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbx, 0 + mov %rsp, %rbx # save current rsp + .cfi_def_cfa_register %rbx + shr $4, %rsp # clear 4 lsb, align to 16 + shl $4, %rsp + + call __tsan_report_race + + # Unalign stack frame back. + mov %rbx, %rsp # restore the original rsp + .cfi_def_cfa_register %rsp + pop %rbx + .cfi_adjust_cfa_offset -8 + # Restore scratch registers. + pop %r11 + .cfi_adjust_cfa_offset -8 + pop %r10 + .cfi_adjust_cfa_offset -8 + pop %r9 + .cfi_adjust_cfa_offset -8 + pop %r8 + .cfi_adjust_cfa_offset -8 + pop %rdi + .cfi_adjust_cfa_offset -8 + pop %rsi + .cfi_adjust_cfa_offset -8 + pop %rdx + .cfi_adjust_cfa_offset -8 + pop %rcx + .cfi_adjust_cfa_offset -8 + pop %rax + .cfi_adjust_cfa_offset -8 + .cfi_restore %rax + .cfi_restore %rbx + .cfi_restore %rcx + .cfi_restore %rdx + .cfi_restore %rsi + .cfi_restore %rdi + .cfi_restore %r8 + .cfi_restore %r9 + .cfi_restore %r10 + .cfi_restore %r11 + ret + .cfi_endproc + +.hidden __tsan_setjmp +.comm _ZN14__interception11real_setjmpE,8,8 +.globl setjmp +.type setjmp, @function +setjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // obtain %rsp + lea 16(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc setjmp + movl $0, %eax + movq _ZN14__interception11real_setjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size setjmp, .-setjmp + +.comm _ZN14__interception12real__setjmpE,8,8 +.globl _setjmp +.type _setjmp, @function +_setjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // obtain %rsp + lea 16(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc setjmp + movl $0, %eax + movq _ZN14__interception12real__setjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size _setjmp, .-_setjmp + +.comm _ZN14__interception14real_sigsetjmpE,8,8 +.globl sigsetjmp +.type sigsetjmp, @function +sigsetjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // save savesigs parameter + push %rsi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rsi, 0 + // align stack frame + sub $8, %rsp + .cfi_adjust_cfa_offset 8 + // obtain %rsp + lea 32(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // unalign stack frame + add $8, %rsp + .cfi_adjust_cfa_offset -8 + // restore savesigs parameter + pop %rsi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rsi + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc sigsetjmp + movl $0, %eax + movq _ZN14__interception14real_sigsetjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size sigsetjmp, .-sigsetjmp + +.comm _ZN14__interception16real___sigsetjmpE,8,8 +.globl __sigsetjmp +.type __sigsetjmp, @function +__sigsetjmp: + .cfi_startproc + // save env parameter + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + // save savesigs parameter + push %rsi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rsi, 0 + // align stack frame + sub $8, %rsp + .cfi_adjust_cfa_offset 8 + // obtain %rsp + lea 32(%rsp), %rdi + mov %rdi, %rsi + xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp) + rol $0x11, %rsi + // call tsan interceptor + call __tsan_setjmp + // unalign stack frame + add $8, %rsp + .cfi_adjust_cfa_offset -8 + // restore savesigs parameter + pop %rsi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rsi + // restore env parameter + pop %rdi + .cfi_adjust_cfa_offset -8 + .cfi_restore %rdi + // tail jump to libc sigsetjmp + movl $0, %eax + movq _ZN14__interception16real___sigsetjmpE@GOTPCREL(%rip), %rdx + jmp *(%rdx) + .cfi_endproc +.size __sigsetjmp, .-__sigsetjmp + +#ifdef __linux__ +/* We do not need executable stack. */ +.section .note.GNU-stack,"",@progbits +#endif diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc new file mode 100644 index 00000000..40939167 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -0,0 +1,348 @@ +//===-- tsan_rtl_mutex.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "tsan_rtl.h" +#include "tsan_flags.h" +#include "tsan_sync.h" +#include "tsan_report.h" +#include "tsan_symbolize.h" +#include "tsan_platform.h" + +namespace __tsan { + +void MutexCreate(ThreadState *thr, uptr pc, uptr addr, + bool rw, bool recursive, bool linker_init) { + Context *ctx = CTX(); + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr); + StatInc(thr, StatMutexCreate); + if (!linker_init && IsAppMem(addr)) { + CHECK(!thr->is_freeing); + thr->is_freeing = true; + MemoryWrite(thr, pc, addr, kSizeLog1); + thr->is_freeing = false; + } + SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); + s->is_rw = rw; + s->is_recursive = recursive; + s->is_linker_init = linker_init; + s->mtx.Unlock(); +} + +void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { + Context *ctx = CTX(); + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr); + StatInc(thr, StatMutexDestroy); +#ifndef TSAN_GO + // Global mutexes not marked as LINKER_INITIALIZED + // cause tons of not interesting reports, so just ignore it. + if (IsGlobalVar(addr)) + return; +#endif + SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr); + if (s == 0) + return; + if (IsAppMem(addr)) { + CHECK(!thr->is_freeing); + thr->is_freeing = true; + MemoryWrite(thr, pc, addr, kSizeLog1); + thr->is_freeing = false; + } + if (flags()->report_destroy_locked + && s->owner_tid != SyncVar::kInvalidTid + && !s->is_broken) { + s->is_broken = true; + ThreadRegistryLock l(ctx->thread_registry); + ScopedReport rep(ReportTypeMutexDestroyLocked); + rep.AddMutex(s); + StackTrace trace; + trace.ObtainCurrent(thr, pc); + rep.AddStack(&trace); + FastState last(s->last_lock); + RestoreStack(last.tid(), last.epoch(), &trace, 0); + rep.AddStack(&trace); + rep.AddLocation(s->addr, 1); + OutputReport(ctx, rep); + } + thr->mset.Remove(s->GetId()); + DestroyAndFree(s); +} + +void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec); + CHECK_GT(rec, 0); + if (IsAppMem(addr)) + MemoryReadAtomic(thr, pc, addr, kSizeLog1); + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId()); + if (s->owner_tid == SyncVar::kInvalidTid) { + CHECK_EQ(s->recursion, 0); + s->owner_tid = thr->tid; + s->last_lock = thr->fast_state.raw(); + } else if (s->owner_tid == thr->tid) { + CHECK_GT(s->recursion, 0); + } else { + Printf("ThreadSanitizer WARNING: double lock of mutex %p\n", addr); + PrintCurrentStack(thr, pc); + } + if (s->recursion == 0) { + StatInc(thr, StatMutexLock); + AcquireImpl(thr, pc, &s->clock); + AcquireImpl(thr, pc, &s->read_clock); + } else if (!s->is_recursive) { + StatInc(thr, StatMutexRecLock); + } + s->recursion += rec; + thr->mset.Add(s->GetId(), true, thr->fast_state.epoch()); + s->mtx.Unlock(); +} + +int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexUnlock %zx all=%d\n", thr->tid, addr, all); + if (IsAppMem(addr)) + MemoryReadAtomic(thr, pc, addr, kSizeLog1); + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId()); + int rec = 0; + if (s->recursion == 0) { + if (!s->is_broken) { + s->is_broken = true; + Printf("ThreadSanitizer WARNING: unlock of unlocked mutex %p\n", addr); + PrintCurrentStack(thr, pc); + } + } else if (s->owner_tid != thr->tid) { + if (!s->is_broken) { + s->is_broken = true; + Printf("ThreadSanitizer WARNING: mutex %p is unlocked by wrong thread\n", + addr); + PrintCurrentStack(thr, pc); + } + } else { + rec = all ? s->recursion : 1; + s->recursion -= rec; + if (s->recursion == 0) { + StatInc(thr, StatMutexUnlock); + s->owner_tid = SyncVar::kInvalidTid; + ReleaseStoreImpl(thr, pc, &s->clock); + } else { + StatInc(thr, StatMutexRecUnlock); + } + } + thr->mset.Del(s->GetId(), true); + s->mtx.Unlock(); + return rec; +} + +void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr); + StatInc(thr, StatMutexReadLock); + if (IsAppMem(addr)) + MemoryReadAtomic(thr, pc, addr, kSizeLog1); + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); + if (s->owner_tid != SyncVar::kInvalidTid) { + Printf("ThreadSanitizer WARNING: read lock of a write locked mutex %p\n", + addr); + PrintCurrentStack(thr, pc); + } + AcquireImpl(thr, pc, &s->clock); + s->last_lock = thr->fast_state.raw(); + thr->mset.Add(s->GetId(), false, thr->fast_state.epoch()); + s->mtx.ReadUnlock(); +} + +void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr); + StatInc(thr, StatMutexReadUnlock); + if (IsAppMem(addr)) + MemoryReadAtomic(thr, pc, addr, kSizeLog1); + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); + if (s->owner_tid != SyncVar::kInvalidTid) { + Printf("ThreadSanitizer WARNING: read unlock of a write locked mutex %p\n", + addr); + PrintCurrentStack(thr, pc); + } + ReleaseImpl(thr, pc, &s->read_clock); + s->mtx.Unlock(); + thr->mset.Del(s->GetId(), false); +} + +void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr); + if (IsAppMem(addr)) + MemoryReadAtomic(thr, pc, addr, kSizeLog1); + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); + bool write = true; + if (s->owner_tid == SyncVar::kInvalidTid) { + // Seems to be read unlock. + write = false; + StatInc(thr, StatMutexReadUnlock); + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); + ReleaseImpl(thr, pc, &s->read_clock); + } else if (s->owner_tid == thr->tid) { + // Seems to be write unlock. + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId()); + CHECK_GT(s->recursion, 0); + s->recursion--; + if (s->recursion == 0) { + StatInc(thr, StatMutexUnlock); + s->owner_tid = SyncVar::kInvalidTid; + ReleaseImpl(thr, pc, &s->clock); + } else { + StatInc(thr, StatMutexRecUnlock); + } + } else if (!s->is_broken) { + s->is_broken = true; + Printf("ThreadSanitizer WARNING: mutex %p is unlock by wrong thread\n", + addr); + PrintCurrentStack(thr, pc); + } + thr->mset.Del(s->GetId(), write); + s->mtx.Unlock(); +} + +void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { + Context *ctx = CTX(); + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); + SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); + s->owner_tid = SyncVar::kInvalidTid; + s->recursion = 0; + s->mtx.Unlock(); +} + +void Acquire(ThreadState *thr, uptr pc, uptr addr) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: Acquire %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); + AcquireImpl(thr, pc, &s->clock); + s->mtx.ReadUnlock(); +} + +static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) { + ThreadState *thr = reinterpret_cast(arg); + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->status == ThreadStatusRunning) + thr->clock.set(tctx->tid, tctx->thr->fast_state.epoch()); + else + thr->clock.set(tctx->tid, tctx->epoch1); +} + +void AcquireGlobal(ThreadState *thr, uptr pc) { + DPrintf("#%d: AcquireGlobal\n", thr->tid); + if (thr->ignore_sync) + return; + ThreadRegistryLock l(CTX()->thread_registry); + CTX()->thread_registry->RunCallbackForEachThreadLocked( + UpdateClockCallback, thr); +} + +void Release(ThreadState *thr, uptr pc, uptr addr) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: Release %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); + s->mtx.Unlock(); +} + +void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; + SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseStoreImpl(thr, pc, &s->clock); + s->mtx.Unlock(); +} + +#ifndef TSAN_GO +static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) { + ThreadState *thr = reinterpret_cast(arg); + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->status == ThreadStatusRunning) + thr->last_sleep_clock.set(tctx->tid, tctx->thr->fast_state.epoch()); + else + thr->last_sleep_clock.set(tctx->tid, tctx->epoch1); +} + +void AfterSleep(ThreadState *thr, uptr pc) { + DPrintf("#%d: AfterSleep %zx\n", thr->tid); + if (thr->ignore_sync) + return; + thr->last_sleep_stack_id = CurrentStackId(thr, pc); + ThreadRegistryLock l(CTX()->thread_registry); + CTX()->thread_registry->RunCallbackForEachThreadLocked( + UpdateSleepClockCallback, thr); +} +#endif + +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->clock.acquire(c); + StatInc(thr, StatSyncAcquire); +} + +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.release(c); + StatInc(thr, StatSyncRelease); +} + +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.ReleaseStore(c); + StatInc(thr, StatSyncRelease); +} + +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.acq_rel(c); + StatInc(thr, StatSyncAcquire); + StatInc(thr, StatSyncRelease); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc new file mode 100644 index 00000000..4fed43fa --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc @@ -0,0 +1,742 @@ +//===-- tsan_rtl_report.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "tsan_platform.h" +#include "tsan_rtl.h" +#include "tsan_suppressions.h" +#include "tsan_symbolize.h" +#include "tsan_report.h" +#include "tsan_sync.h" +#include "tsan_mman.h" +#include "tsan_flags.h" +#include "tsan_fd.h" + +namespace __tsan { + +using namespace __sanitizer; // NOLINT + +static ReportStack *SymbolizeStack(const StackTrace& trace); + +void TsanCheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + ScopedInRtl in_rtl; + Printf("FATAL: ThreadSanitizer CHECK failed: " + "%s:%d \"%s\" (0x%zx, 0x%zx)\n", + file, line, cond, (uptr)v1, (uptr)v2); + PrintCurrentStackSlow(); + Die(); +} + +// Can be overriden by an application/test to intercept reports. +#ifdef TSAN_EXTERNAL_HOOKS +bool OnReport(const ReportDesc *rep, bool suppressed); +#else +SANITIZER_INTERFACE_ATTRIBUTE +bool WEAK OnReport(const ReportDesc *rep, bool suppressed) { + (void)rep; + return suppressed; +} +#endif + +static void StackStripMain(ReportStack *stack) { + ReportStack *last_frame = 0; + ReportStack *last_frame2 = 0; + const char *prefix = "__interceptor_"; + uptr prefix_len = internal_strlen(prefix); + const char *path_prefix = flags()->strip_path_prefix; + uptr path_prefix_len = internal_strlen(path_prefix); + char *pos; + for (ReportStack *ent = stack; ent; ent = ent->next) { + if (ent->func && 0 == internal_strncmp(ent->func, prefix, prefix_len)) + ent->func += prefix_len; + if (ent->file && (pos = internal_strstr(ent->file, path_prefix))) + ent->file = pos + path_prefix_len; + if (ent->file && ent->file[0] == '.' && ent->file[1] == '/') + ent->file += 2; + last_frame2 = last_frame; + last_frame = ent; + } + + if (last_frame2 == 0) + return; + const char *last = last_frame->func; +#ifndef TSAN_GO + const char *last2 = last_frame2->func; + // Strip frame above 'main' + if (last2 && 0 == internal_strcmp(last2, "main")) { + last_frame2->next = 0; + // Strip our internal thread start routine. + } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) { + last_frame2->next = 0; + // Strip global ctors init. + } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) { + last_frame2->next = 0; + // If both are 0, then we probably just failed to symbolize. + } else if (last || last2) { + // Ensure that we recovered stack completely. Trimmed stack + // can actually happen if we do not instrument some code, + // so it's only a debug print. However we must try hard to not miss it + // due to our fault. + DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc); + } +#else + // The last frame always point into runtime (gosched0, goexit0, runtime.main). + last_frame2->next = 0; + (void)last; +#endif +} + +static ReportStack *SymbolizeStack(const StackTrace& trace) { + if (trace.IsEmpty()) + return 0; + ReportStack *stack = 0; + for (uptr si = 0; si < trace.Size(); si++) { + const uptr pc = trace.Get(si); +#ifndef TSAN_GO + // We obtain the return address, that is, address of the next instruction, + // so offset it by 1 byte. + const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); +#else + // FIXME(dvyukov): Go sometimes uses address of a function as top pc. + uptr pc1 = pc; + if (si != trace.Size() - 1) + pc1 -= 1; +#endif + ReportStack *ent = SymbolizeCode(pc1); + CHECK_NE(ent, 0); + ReportStack *last = ent; + while (last->next) { + last->pc = pc; // restore original pc for report + last = last->next; + } + last->pc = pc; // restore original pc for report + last->next = stack; + stack = ent; + } + StackStripMain(stack); + return stack; +} + +ScopedReport::ScopedReport(ReportType typ) { + ctx_ = CTX(); + ctx_->thread_registry->CheckLocked(); + void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc)); + rep_ = new(mem) ReportDesc; + rep_->typ = typ; + ctx_->report_mtx.Lock(); + CommonSanitizerReportMutex.Lock(); +} + +ScopedReport::~ScopedReport() { + CommonSanitizerReportMutex.Unlock(); + ctx_->report_mtx.Unlock(); + DestroyAndFree(rep_); +} + +void ScopedReport::AddStack(const StackTrace *stack) { + ReportStack **rs = rep_->stacks.PushBack(); + *rs = SymbolizeStack(*stack); +} + +void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, + const StackTrace *stack, const MutexSet *mset) { + void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop)); + ReportMop *mop = new(mem) ReportMop; + rep_->mops.PushBack(mop); + mop->tid = s.tid(); + mop->addr = addr + s.addr0(); + mop->size = s.size(); + mop->write = s.IsWrite(); + mop->atomic = s.IsAtomic(); + mop->stack = SymbolizeStack(*stack); + for (uptr i = 0; i < mset->Size(); i++) { + MutexSet::Desc d = mset->Get(i); + u64 uid = 0; + uptr addr = SyncVar::SplitId(d.id, &uid); + SyncVar *s = ctx_->synctab.GetIfExistsAndLock(addr, false); + // Check that the mutex is still alive. + // Another mutex can be created at the same address, + // so check uid as well. + if (s && s->CheckId(uid)) { + ReportMopMutex mtx = {s->uid, d.write}; + mop->mset.PushBack(mtx); + AddMutex(s); + } else { + ReportMopMutex mtx = {d.id, d.write}; + mop->mset.PushBack(mtx); + AddMutex(d.id); + } + if (s) + s->mtx.ReadUnlock(); + } +} + +void ScopedReport::AddThread(const ThreadContext *tctx) { + for (uptr i = 0; i < rep_->threads.Size(); i++) { + if ((u32)rep_->threads[i]->id == tctx->tid) + return; + } + void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread)); + ReportThread *rt = new(mem) ReportThread(); + rep_->threads.PushBack(rt); + rt->id = tctx->tid; + rt->pid = tctx->os_id; + rt->running = (tctx->status == ThreadStatusRunning); + rt->name = tctx->name ? internal_strdup(tctx->name) : 0; + rt->parent_tid = tctx->parent_tid; + rt->stack = 0; +#ifdef TSAN_GO + rt->stack = SymbolizeStack(tctx->creation_stack); +#else + uptr ssz = 0; + const uptr *stack = StackDepotGet(tctx->creation_stack_id, &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + rt->stack = SymbolizeStack(trace); + } +#endif +} + +#ifndef TSAN_GO +static ThreadContext *FindThreadByUidLocked(int unique_id) { + Context *ctx = CTX(); + ctx->thread_registry->CheckLocked(); + for (unsigned i = 0; i < kMaxTid; i++) { + ThreadContext *tctx = static_cast( + ctx->thread_registry->GetThreadLocked(i)); + if (tctx && tctx->unique_id == (u32)unique_id) { + return tctx; + } + } + return 0; +} + +static ThreadContext *FindThreadByTidLocked(int tid) { + Context *ctx = CTX(); + ctx->thread_registry->CheckLocked(); + return static_cast( + ctx->thread_registry->GetThreadLocked(tid)); +} + +static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) { + uptr addr = (uptr)arg; + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->status != ThreadStatusRunning) + return false; + ThreadState *thr = tctx->thr; + CHECK(thr); + return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) || + (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size)); +} + +ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) { + Context *ctx = CTX(); + ctx->thread_registry->CheckLocked(); + ThreadContext *tctx = static_cast( + ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls, + (void*)addr)); + if (!tctx) + return 0; + ThreadState *thr = tctx->thr; + CHECK(thr); + *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size); + return tctx; +} +#endif + +void ScopedReport::AddMutex(const SyncVar *s) { + for (uptr i = 0; i < rep_->mutexes.Size(); i++) { + if (rep_->mutexes[i]->id == s->uid) + return; + } + void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); + ReportMutex *rm = new(mem) ReportMutex(); + rep_->mutexes.PushBack(rm); + rm->id = s->uid; + rm->destroyed = false; + rm->stack = 0; +#ifndef TSAN_GO + uptr ssz = 0; + const uptr *stack = StackDepotGet(s->creation_stack_id, &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + rm->stack = SymbolizeStack(trace); + } +#endif +} + +void ScopedReport::AddMutex(u64 id) { + for (uptr i = 0; i < rep_->mutexes.Size(); i++) { + if (rep_->mutexes[i]->id == id) + return; + } + void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); + ReportMutex *rm = new(mem) ReportMutex(); + rep_->mutexes.PushBack(rm); + rm->id = id; + rm->destroyed = true; + rm->stack = 0; +} + +void ScopedReport::AddLocation(uptr addr, uptr size) { + if (addr == 0) + return; +#ifndef TSAN_GO + int fd = -1; + int creat_tid = -1; + u32 creat_stack = 0; + if (FdLocation(addr, &fd, &creat_tid, &creat_stack) + || FdLocation(AlternativeAddress(addr), &fd, &creat_tid, &creat_stack)) { + void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation)); + ReportLocation *loc = new(mem) ReportLocation(); + rep_->locs.PushBack(loc); + loc->type = ReportLocationFD; + loc->fd = fd; + loc->tid = creat_tid; + uptr ssz = 0; + const uptr *stack = StackDepotGet(creat_stack, &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + loc->stack = SymbolizeStack(trace); + } + ThreadContext *tctx = FindThreadByUidLocked(creat_tid); + if (tctx) + AddThread(tctx); + return; + } + MBlock *b = 0; + if (allocator()->PointerIsMine((void*)addr) + && (b = user_mblock(0, (void*)addr))) { + ThreadContext *tctx = FindThreadByTidLocked(b->Tid()); + void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation)); + ReportLocation *loc = new(mem) ReportLocation(); + rep_->locs.PushBack(loc); + loc->type = ReportLocationHeap; + loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr); + loc->size = b->Size(); + loc->tid = tctx ? tctx->tid : b->Tid(); + loc->name = 0; + loc->file = 0; + loc->line = 0; + loc->stack = 0; + uptr ssz = 0; + const uptr *stack = StackDepotGet(b->StackId(), &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + loc->stack = SymbolizeStack(trace); + } + if (tctx) + AddThread(tctx); + return; + } + bool is_stack = false; + if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) { + void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation)); + ReportLocation *loc = new(mem) ReportLocation(); + rep_->locs.PushBack(loc); + loc->type = is_stack ? ReportLocationStack : ReportLocationTLS; + loc->tid = tctx->tid; + AddThread(tctx); + } + ReportLocation *loc = SymbolizeData(addr); + if (loc) { + rep_->locs.PushBack(loc); + return; + } +#endif +} + +#ifndef TSAN_GO +void ScopedReport::AddSleep(u32 stack_id) { + uptr ssz = 0; + const uptr *stack = StackDepotGet(stack_id, &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + rep_->sleep = SymbolizeStack(trace); + } +} +#endif + +void ScopedReport::SetCount(int count) { + rep_->count = count; +} + +const ReportDesc *ScopedReport::GetReport() const { + return rep_; +} + +void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) { + // This function restores stack trace and mutex set for the thread/epoch. + // It does so by getting stack trace and mutex set at the beginning of + // trace part, and then replaying the trace till the given epoch. + Context *ctx = CTX(); + ctx->thread_registry->CheckLocked(); + ThreadContext *tctx = static_cast( + ctx->thread_registry->GetThreadLocked(tid)); + if (tctx == 0) + return; + if (tctx->status != ThreadStatusRunning + && tctx->status != ThreadStatusFinished + && tctx->status != ThreadStatusDead) + return; + Trace* trace = ThreadTrace(tctx->tid); + Lock l(&trace->mtx); + const int partidx = (epoch / kTracePartSize) % TraceParts(); + TraceHeader* hdr = &trace->headers[partidx]; + if (epoch < hdr->epoch0) + return; + const u64 epoch0 = RoundDown(epoch, TraceSize()); + const u64 eend = epoch % TraceSize(); + const u64 ebegin = RoundDown(eend, kTracePartSize); + DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", + tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); + InternalScopedBuffer stack(kShadowStackSize); + for (uptr i = 0; i < hdr->stack0.Size(); i++) { + stack[i] = hdr->stack0.Get(i); + DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); + } + if (mset) + *mset = hdr->mset0; + uptr pos = hdr->stack0.Size(); + Event *events = (Event*)GetThreadTrace(tid); + for (uptr i = ebegin; i <= eend; i++) { + Event ev = events[i]; + EventType typ = (EventType)(ev >> 61); + uptr pc = (uptr)(ev & ((1ull << 61) - 1)); + DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc); + if (typ == EventTypeMop) { + stack[pos] = pc; + } else if (typ == EventTypeFuncEnter) { + stack[pos++] = pc; + } else if (typ == EventTypeFuncExit) { + if (pos > 0) + pos--; + } + if (mset) { + if (typ == EventTypeLock) { + mset->Add(pc, true, epoch0 + i); + } else if (typ == EventTypeUnlock) { + mset->Del(pc, true); + } else if (typ == EventTypeRLock) { + mset->Add(pc, false, epoch0 + i); + } else if (typ == EventTypeRUnlock) { + mset->Del(pc, false); + } + } + for (uptr j = 0; j <= pos; j++) + DPrintf2(" #%zu: %zx\n", j, stack[j]); + } + if (pos == 0 && stack[0] == 0) + return; + pos++; + stk->Init(stack.data(), pos); +} + +static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], + uptr addr_min, uptr addr_max) { + Context *ctx = CTX(); + bool equal_stack = false; + RacyStacks hash; + if (flags()->suppress_equal_stacks) { + hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr)); + hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr)); + for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) { + if (hash == ctx->racy_stacks[i]) { + DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n"); + equal_stack = true; + break; + } + } + } + bool equal_address = false; + RacyAddress ra0 = {addr_min, addr_max}; + if (flags()->suppress_equal_addresses) { + for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) { + RacyAddress ra2 = ctx->racy_addresses[i]; + uptr maxbeg = max(ra0.addr_min, ra2.addr_min); + uptr minend = min(ra0.addr_max, ra2.addr_max); + if (maxbeg < minend) { + DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n"); + equal_address = true; + break; + } + } + } + if (equal_stack || equal_address) { + if (!equal_stack) + ctx->racy_stacks.PushBack(hash); + if (!equal_address) + ctx->racy_addresses.PushBack(ra0); + return true; + } + return false; +} + +static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], + uptr addr_min, uptr addr_max) { + Context *ctx = CTX(); + if (flags()->suppress_equal_stacks) { + RacyStacks hash; + hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr)); + hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr)); + ctx->racy_stacks.PushBack(hash); + } + if (flags()->suppress_equal_addresses) { + RacyAddress ra0 = {addr_min, addr_max}; + ctx->racy_addresses.PushBack(ra0); + } +} + +bool OutputReport(Context *ctx, + const ScopedReport &srep, + const ReportStack *suppress_stack1, + const ReportStack *suppress_stack2, + const ReportLocation *suppress_loc) { + atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed); + const ReportDesc *rep = srep.GetReport(); + Suppression *supp = 0; + uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1, &supp); + if (suppress_pc == 0) + suppress_pc = IsSuppressed(rep->typ, suppress_stack2, &supp); + if (suppress_pc == 0) + suppress_pc = IsSuppressed(rep->typ, suppress_loc, &supp); + if (suppress_pc != 0) { + FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp}; + ctx->fired_suppressions.push_back(s); + } + if (OnReport(rep, suppress_pc != 0)) + return false; + PrintReport(rep); + ctx->nreported++; + if (flags()->halt_on_error) + internal__exit(flags()->exitcode); + return true; +} + +bool IsFiredSuppression(Context *ctx, + const ScopedReport &srep, + const StackTrace &trace) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { + if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + continue; + for (uptr j = 0; j < trace.Size(); j++) { + FiredSuppression *s = &ctx->fired_suppressions[k]; + if (trace.Get(j) == s->pc) { + if (s->supp) + s->supp->hit_count++; + return true; + } + } + } + return false; +} + +static bool IsFiredSuppression(Context *ctx, + const ScopedReport &srep, + uptr addr) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { + if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + continue; + FiredSuppression *s = &ctx->fired_suppressions[k]; + if (addr == s->pc) { + if (s->supp) + s->supp->hit_count++; + return true; + } + } + return false; +} + +bool FrameIsInternal(const ReportStack *frame) { + return frame != 0 && frame->file != 0 + && (internal_strstr(frame->file, "tsan_interceptors.cc") || + internal_strstr(frame->file, "sanitizer_common_interceptors.inc") || + internal_strstr(frame->file, "tsan_interface_")); +} + +// On programs that use Java we see weird reports like: +// WARNING: ThreadSanitizer: data race (pid=22512) +// Read of size 8 at 0x7d2b00084318 by thread 100: +// #0 memcpy tsan_interceptors.cc:406 (foo+0x00000d8dfae3) +// #1 :0 (0x7f7ad9b40193) +// Previous write of size 8 at 0x7d2b00084318 by thread 105: +// #0 strncpy tsan_interceptors.cc:501 (foo+0x00000d8e0919) +// #1 :0 (0x7f7ad9b42707) +static bool IsJavaNonsense(const ReportDesc *rep) { +#ifndef TSAN_GO + for (uptr i = 0; i < rep->mops.Size(); i++) { + ReportMop *mop = rep->mops[i]; + ReportStack *frame = mop->stack; + if (frame == 0 + || (frame->func == 0 && frame->file == 0 && frame->line == 0 + && frame->module == 0)) { + return true; + } + if (FrameIsInternal(frame)) { + frame = frame->next; + if (frame == 0 + || (frame->func == 0 && frame->file == 0 && frame->line == 0 + && frame->module == 0)) { + if (frame) { + FiredSuppression supp = {rep->typ, frame->pc, 0}; + CTX()->fired_suppressions.push_back(supp); + } + return true; + } + } + } +#endif + return false; +} + +static bool RaceBetweenAtomicAndFree(ThreadState *thr) { + Shadow s0(thr->racy_state[0]); + Shadow s1(thr->racy_state[1]); + CHECK(!(s0.IsAtomic() && s1.IsAtomic())); + if (!s0.IsAtomic() && !s1.IsAtomic()) + return true; + if (s0.IsAtomic() && s1.IsFreed()) + return true; + if (s1.IsAtomic() && thr->is_freeing) + return true; + return false; +} + +void ReportRace(ThreadState *thr) { + if (!flags()->report_bugs) + return; + ScopedInRtl in_rtl; + + if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) + return; + + bool freed = false; + { + Shadow s(thr->racy_state[1]); + freed = s.GetFreedAndReset(); + thr->racy_state[1] = s.raw(); + } + + uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr); + uptr addr_min = 0; + uptr addr_max = 0; + { + uptr a0 = addr + Shadow(thr->racy_state[0]).addr0(); + uptr a1 = addr + Shadow(thr->racy_state[1]).addr0(); + uptr e0 = a0 + Shadow(thr->racy_state[0]).size(); + uptr e1 = a1 + Shadow(thr->racy_state[1]).size(); + addr_min = min(a0, a1); + addr_max = max(e0, e1); + if (IsExpectedReport(addr_min, addr_max - addr_min)) + return; + } + + Context *ctx = CTX(); + ThreadRegistryLock l0(ctx->thread_registry); + + ReportType typ = ReportTypeRace; + if (thr->is_vptr_access) + typ = ReportTypeVptrRace; + else if (freed) + typ = ReportTypeUseAfterFree; + ScopedReport rep(typ); + if (IsFiredSuppression(ctx, rep, addr)) + return; + const uptr kMop = 2; + StackTrace traces[kMop]; + const uptr toppc = TraceTopPC(thr); + traces[0].ObtainCurrent(thr, toppc); + if (IsFiredSuppression(ctx, rep, traces[0])) + return; + InternalScopedBuffer mset2(1); + new(mset2.data()) MutexSet(); + Shadow s2(thr->racy_state[1]); + RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data()); + if (IsFiredSuppression(ctx, rep, traces[1])) + return; + + if (HandleRacyStacks(thr, traces, addr_min, addr_max)) + return; + + for (uptr i = 0; i < kMop; i++) { + Shadow s(thr->racy_state[i]); + rep.AddMemoryAccess(addr, s, &traces[i], + i == 0 ? &thr->mset : mset2.data()); + } + + if (flags()->suppress_java && IsJavaNonsense(rep.GetReport())) + return; + + for (uptr i = 0; i < kMop; i++) { + FastState s(thr->racy_state[i]); + ThreadContext *tctx = static_cast( + ctx->thread_registry->GetThreadLocked(s.tid())); + if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1) + continue; + rep.AddThread(tctx); + } + + rep.AddLocation(addr_min, addr_max - addr_min); + +#ifndef TSAN_GO + { // NOLINT + Shadow s(thr->racy_state[1]); + if (s.epoch() <= thr->last_sleep_clock.get(s.tid())) + rep.AddSleep(thr->last_sleep_stack_id); + } +#endif + + ReportLocation *suppress_loc = rep.GetReport()->locs.Size() ? + rep.GetReport()->locs[0] : 0; + if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack, + rep.GetReport()->mops[1]->stack, + suppress_loc)) + return; + + AddRacyStacks(thr, traces, addr_min, addr_max); +} + +void PrintCurrentStack(ThreadState *thr, uptr pc) { + StackTrace trace; + trace.ObtainCurrent(thr, pc); + PrintStack(SymbolizeStack(trace)); +} + +void PrintCurrentStackSlow() { +#ifndef TSAN_GO + __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace, + sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; + ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), + 0, 0, 0, false); + for (uptr i = 0; i < ptrace->size / 2; i++) { + uptr tmp = ptrace->trace[i]; + ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1]; + ptrace->trace[ptrace->size - i - 1] = tmp; + } + StackTrace trace; + trace.Init(ptrace->trace, ptrace->size); + PrintStack(SymbolizeStack(trace)); +#endif +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc new file mode 100644 index 00000000..4e451b04 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc @@ -0,0 +1,376 @@ +//===-- tsan_rtl_thread.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_placement_new.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" +#include "tsan_platform.h" +#include "tsan_report.h" +#include "tsan_sync.h" + +namespace __tsan { + +// ThreadContext implementation. + +ThreadContext::ThreadContext(int tid) + : ThreadContextBase(tid) + , thr() + , sync() + , epoch0() + , epoch1() { +} + +#ifndef TSAN_GO +ThreadContext::~ThreadContext() { +} +#endif + +void ThreadContext::OnDead() { + sync.Reset(); +} + +void ThreadContext::OnJoined(void *arg) { + ThreadState *caller_thr = static_cast(arg); + AcquireImpl(caller_thr, 0, &sync); + sync.Reset(); +} + +struct OnCreatedArgs { + ThreadState *thr; + uptr pc; +}; + +void ThreadContext::OnCreated(void *arg) { + thr = 0; + if (tid == 0) + return; + OnCreatedArgs *args = static_cast(arg); + args->thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); + ReleaseImpl(args->thr, 0, &sync); +#ifdef TSAN_GO + creation_stack.ObtainCurrent(args->thr, args->pc); +#else + creation_stack_id = CurrentStackId(args->thr, args->pc); +#endif + if (reuse_count == 0) + StatInc(args->thr, StatThreadMaxTid); +} + +void ThreadContext::OnReset() { + sync.Reset(); + FlushUnneededShadowMemory(GetThreadTrace(tid), TraceSize() * sizeof(Event)); + //!!! FlushUnneededShadowMemory(GetThreadTraceHeader(tid), sizeof(Trace)); +} + +struct OnStartedArgs { + ThreadState *thr; + uptr stk_addr; + uptr stk_size; + uptr tls_addr; + uptr tls_size; +}; + +void ThreadContext::OnStarted(void *arg) { + OnStartedArgs *args = static_cast(arg); + thr = args->thr; + // RoundUp so that one trace part does not contain events + // from different threads. + epoch0 = RoundUp(epoch1 + 1, kTracePartSize); + epoch1 = (u64)-1; + new(thr) ThreadState(CTX(), tid, unique_id, + epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); +#ifndef TSAN_GO + thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0]; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize; +#else + // Setup dynamic shadow stack. + const int kInitStackSize = 8; + thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, + kInitStackSize * sizeof(uptr)); + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; +#endif +#ifndef TSAN_GO + AllocatorThreadStart(thr); +#endif + thr->fast_synch_epoch = epoch0; + AcquireImpl(thr, 0, &sync); + thr->fast_state.SetHistorySize(flags()->history_size); + const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); + Trace *thr_trace = ThreadTrace(thr->tid); + thr_trace->headers[trace].epoch0 = epoch0; + StatInc(thr, StatSyncAcquire); + sync.Reset(); + DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx " + "tls_addr=%zx tls_size=%zx\n", + tid, (uptr)epoch0, args->stk_addr, args->stk_size, + args->tls_addr, args->tls_size); + thr->is_alive = true; +} + +void ThreadContext::OnFinished() { + if (!detached) { + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, 0, &sync); + } + epoch1 = thr->fast_state.epoch(); + +#ifndef TSAN_GO + AllocatorThreadFinish(thr); +#endif + thr->~ThreadState(); + StatAggregate(CTX()->stat, thr->stat); + thr = 0; +} + +#ifndef TSAN_GO +struct ThreadLeak { + ThreadContext *tctx; + int count; +}; + +static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) { + Vector &leaks = *(Vector*)arg; + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->detached || tctx->status != ThreadStatusFinished) + return; + for (uptr i = 0; i < leaks.Size(); i++) { + if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) { + leaks[i].count++; + return; + } + } + ThreadLeak leak = {tctx, 1}; + leaks.PushBack(leak); +} +#endif + +static void ThreadCheckIgnore(ThreadState *thr) { + if (thr->ignore_reads_and_writes) { + Printf("ThreadSanitizer: thread T%d finished with ignores enabled.\n", + thr->tid); + } + if (thr->ignore_sync) { + Printf("ThreadSanitizer: thread T%d finished with sync ignores enabled.\n", + thr->tid); + } +} + +void ThreadFinalize(ThreadState *thr) { + CHECK_GT(thr->in_rtl, 0); + ThreadCheckIgnore(thr); +#ifndef TSAN_GO + if (!flags()->report_thread_leaks) + return; + ThreadRegistryLock l(CTX()->thread_registry); + Vector leaks(MBlockScopedBuf); + CTX()->thread_registry->RunCallbackForEachThreadLocked( + MaybeReportThreadLeak, &leaks); + for (uptr i = 0; i < leaks.Size(); i++) { + ScopedReport rep(ReportTypeThreadLeak); + rep.AddThread(leaks[i].tctx); + rep.SetCount(leaks[i].count); + OutputReport(CTX(), rep); + } +#endif +} + +int ThreadCount(ThreadState *thr) { + CHECK_GT(thr->in_rtl, 0); + Context *ctx = CTX(); + uptr result; + ctx->thread_registry->GetNumberOfThreads(0, 0, &result); + return (int)result; +} + +int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { + CHECK_GT(thr->in_rtl, 0); + StatInc(thr, StatThreadCreate); + Context *ctx = CTX(); + OnCreatedArgs args = { thr, pc }; + int tid = ctx->thread_registry->CreateThread(uid, detached, thr->tid, &args); + DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid); + StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads()); + return tid; +} + +void ThreadStart(ThreadState *thr, int tid, uptr os_id) { + CHECK_GT(thr->in_rtl, 0); + uptr stk_addr = 0; + uptr stk_size = 0; + uptr tls_addr = 0; + uptr tls_size = 0; + GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size); + + if (tid) { + if (stk_addr && stk_size) + MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size); + + if (tls_addr && tls_size) { + // Check that the thr object is in tls; + const uptr thr_beg = (uptr)thr; + const uptr thr_end = (uptr)thr + sizeof(*thr); + CHECK_GE(thr_beg, tls_addr); + CHECK_LE(thr_beg, tls_addr + tls_size); + CHECK_GE(thr_end, tls_addr); + CHECK_LE(thr_end, tls_addr + tls_size); + // Since the thr object is huge, skip it. + MemoryRangeImitateWrite(thr, /*pc=*/ 2, tls_addr, thr_beg - tls_addr); + MemoryRangeImitateWrite(thr, /*pc=*/ 2, + thr_end, tls_addr + tls_size - thr_end); + } + } + + OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size }; + CTX()->thread_registry->StartThread(tid, os_id, &args); +} + +void ThreadFinish(ThreadState *thr) { + CHECK_GT(thr->in_rtl, 0); + ThreadCheckIgnore(thr); + StatInc(thr, StatThreadFinish); + if (thr->stk_addr && thr->stk_size) + DontNeedShadowFor(thr->stk_addr, thr->stk_size); + if (thr->tls_addr && thr->tls_size) + DontNeedShadowFor(thr->tls_addr, thr->tls_size); + thr->is_alive = false; + Context *ctx = CTX(); + ctx->thread_registry->FinishThread(thr->tid); +} + +static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { + uptr uid = (uptr)arg; + if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { + tctx->user_id = 0; + return true; + } + return false; +} + +int ThreadTid(ThreadState *thr, uptr pc, uptr uid) { + CHECK_GT(thr->in_rtl, 0); + Context *ctx = CTX(); + int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid); + DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res); + return res; +} + +void ThreadJoin(ThreadState *thr, uptr pc, int tid) { + CHECK_GT(thr->in_rtl, 0); + CHECK_GT(tid, 0); + CHECK_LT(tid, kMaxTid); + DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid); + Context *ctx = CTX(); + ctx->thread_registry->JoinThread(tid, thr); +} + +void ThreadDetach(ThreadState *thr, uptr pc, int tid) { + CHECK_GT(thr->in_rtl, 0); + CHECK_GT(tid, 0); + CHECK_LT(tid, kMaxTid); + Context *ctx = CTX(); + ctx->thread_registry->DetachThread(tid); +} + +void ThreadSetName(ThreadState *thr, const char *name) { + CHECK_GT(thr->in_rtl, 0); + CTX()->thread_registry->SetThreadName(thr->tid, name); +} + +void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, + uptr size, bool is_write) { + if (size == 0) + return; + + u64 *shadow_mem = (u64*)MemToShadow(addr); + DPrintf2("#%d: MemoryAccessRange: @%p %p size=%d is_write=%d\n", + thr->tid, (void*)pc, (void*)addr, + (int)size, is_write); + +#if TSAN_DEBUG + if (!IsAppMem(addr)) { + Printf("Access to non app mem %zx\n", addr); + DCHECK(IsAppMem(addr)); + } + if (!IsAppMem(addr + size - 1)) { + Printf("Access to non app mem %zx\n", addr + size - 1); + DCHECK(IsAppMem(addr + size - 1)); + } + if (!IsShadowMem((uptr)shadow_mem)) { + Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr); + DCHECK(IsShadowMem((uptr)shadow_mem)); + } + if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) { + Printf("Bad shadow addr %p (%zx)\n", + shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1); + DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))); + } +#endif + + StatInc(thr, StatMopRange); + + if (*shadow_mem == kShadowRodata) { + // Access to .rodata section, no races here. + // Measurements show that it can be 10-20% of all memory accesses. + StatInc(thr, StatMopRangeRodata); + return; + } + + FastState fast_state = thr->fast_state; + if (fast_state.GetIgnoreBit()) + return; + + fast_state.IncrementEpoch(); + thr->fast_state = fast_state; + TraceAddEvent(thr, fast_state, EventTypeMop, pc); + + bool unaligned = (addr % kShadowCell) != 0; + + // Handle unaligned beginning, if any. + for (; addr % kShadowCell && size; addr++, size--) { + int const kAccessSizeLog = 0; + Shadow cur(fast_state); + cur.SetWrite(is_write); + cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, + shadow_mem, cur); + } + if (unaligned) + shadow_mem += kShadowCnt; + // Handle middle part, if any. + for (; size >= kShadowCell; addr += kShadowCell, size -= kShadowCell) { + int const kAccessSizeLog = 3; + Shadow cur(fast_state); + cur.SetWrite(is_write); + cur.SetAddr0AndSizeLog(0, kAccessSizeLog); + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, + shadow_mem, cur); + shadow_mem += kShadowCnt; + } + // Handle ending, if any. + for (; size; addr++, size--) { + int const kAccessSizeLog = 0; + Shadow cur(fast_state); + cur.SetWrite(is_write); + cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); + MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, + shadow_mem, cur); + } +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_stat.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_stat.cc new file mode 100644 index 00000000..f9dbf121 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_stat.cc @@ -0,0 +1,483 @@ +//===-- tsan_stat.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_stat.h" +#include "tsan_rtl.h" + +namespace __tsan { + +void StatAggregate(u64 *dst, u64 *src) { + if (!kCollectStats) + return; + for (int i = 0; i < StatCnt; i++) + dst[i] += src[i]; +} + +void StatOutput(u64 *stat) { + if (!kCollectStats) + return; + + stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero]; + + static const char *name[StatCnt] = {}; + name[StatMop] = "Memory accesses "; + name[StatMopRead] = " Including reads "; + name[StatMopWrite] = " writes "; + name[StatMop1] = " Including size 1 "; + name[StatMop2] = " size 2 "; + name[StatMop4] = " size 4 "; + name[StatMop8] = " size 8 "; + name[StatMopSame] = " Including same "; + name[StatMopRange] = " Including range "; + name[StatMopRodata] = " Including .rodata "; + name[StatMopRangeRodata] = " Including .rodata range "; + name[StatShadowProcessed] = "Shadow processed "; + name[StatShadowZero] = " Including empty "; + name[StatShadowNonZero] = " Including non empty "; + name[StatShadowSameSize] = " Including same size "; + name[StatShadowIntersect] = " intersect "; + name[StatShadowNotIntersect] = " not intersect "; + name[StatShadowSameThread] = " Including same thread "; + name[StatShadowAnotherThread] = " another thread "; + name[StatShadowReplace] = " Including evicted "; + + name[StatFuncEnter] = "Function entries "; + name[StatFuncExit] = "Function exits "; + name[StatEvents] = "Events collected "; + + name[StatThreadCreate] = "Total threads created "; + name[StatThreadFinish] = " threads finished "; + name[StatThreadReuse] = " threads reused "; + name[StatThreadMaxTid] = " max tid "; + name[StatThreadMaxAlive] = " max alive threads "; + + name[StatMutexCreate] = "Mutexes created "; + name[StatMutexDestroy] = " destroyed "; + name[StatMutexLock] = " lock "; + name[StatMutexUnlock] = " unlock "; + name[StatMutexRecLock] = " recursive lock "; + name[StatMutexRecUnlock] = " recursive unlock "; + name[StatMutexReadLock] = " read lock "; + name[StatMutexReadUnlock] = " read unlock "; + + name[StatSyncCreated] = "Sync objects created "; + name[StatSyncDestroyed] = " destroyed "; + name[StatSyncAcquire] = " acquired "; + name[StatSyncRelease] = " released "; + + name[StatAtomic] = "Atomic operations "; + name[StatAtomicLoad] = " Including load "; + name[StatAtomicStore] = " store "; + name[StatAtomicExchange] = " exchange "; + name[StatAtomicFetchAdd] = " fetch_add "; + name[StatAtomicFetchSub] = " fetch_sub "; + name[StatAtomicFetchAnd] = " fetch_and "; + name[StatAtomicFetchOr] = " fetch_or "; + name[StatAtomicFetchXor] = " fetch_xor "; + name[StatAtomicFetchNand] = " fetch_nand "; + name[StatAtomicCAS] = " compare_exchange "; + name[StatAtomicFence] = " fence "; + name[StatAtomicRelaxed] = " Including relaxed "; + name[StatAtomicConsume] = " consume "; + name[StatAtomicAcquire] = " acquire "; + name[StatAtomicRelease] = " release "; + name[StatAtomicAcq_Rel] = " acq_rel "; + name[StatAtomicSeq_Cst] = " seq_cst "; + name[StatAtomic1] = " Including size 1 "; + name[StatAtomic2] = " size 2 "; + name[StatAtomic4] = " size 4 "; + name[StatAtomic8] = " size 8 "; + name[StatAtomic16] = " size 16 "; + + name[StatInterceptor] = "Interceptors "; + name[StatInt_longjmp] = " longjmp "; + name[StatInt_siglongjmp] = " siglongjmp "; + name[StatInt_malloc] = " malloc "; + name[StatInt___libc_memalign] = " __libc_memalign "; + name[StatInt_calloc] = " calloc "; + name[StatInt_realloc] = " realloc "; + name[StatInt_free] = " free "; + name[StatInt_cfree] = " cfree "; + name[StatInt_malloc_usable_size] = " malloc_usable_size "; + name[StatInt_mmap] = " mmap "; + name[StatInt_mmap64] = " mmap64 "; + name[StatInt_munmap] = " munmap "; + name[StatInt_memalign] = " memalign "; + name[StatInt_valloc] = " valloc "; + name[StatInt_pvalloc] = " pvalloc "; + name[StatInt_posix_memalign] = " posix_memalign "; + name[StatInt__Znwm] = " _Znwm "; + name[StatInt__ZnwmRKSt9nothrow_t] = " _ZnwmRKSt9nothrow_t "; + name[StatInt__Znam] = " _Znam "; + name[StatInt__ZnamRKSt9nothrow_t] = " _ZnamRKSt9nothrow_t "; + name[StatInt__ZdlPv] = " _ZdlPv "; + name[StatInt__ZdlPvRKSt9nothrow_t] = " _ZdlPvRKSt9nothrow_t "; + name[StatInt__ZdaPv] = " _ZdaPv "; + name[StatInt__ZdaPvRKSt9nothrow_t] = " _ZdaPvRKSt9nothrow_t "; + name[StatInt_strlen] = " strlen "; + name[StatInt_memset] = " memset "; + name[StatInt_memcpy] = " memcpy "; + name[StatInt_strcmp] = " strcmp "; + name[StatInt_memchr] = " memchr "; + name[StatInt_memrchr] = " memrchr "; + name[StatInt_memmove] = " memmove "; + name[StatInt_memcmp] = " memcmp "; + name[StatInt_strchr] = " strchr "; + name[StatInt_strchrnul] = " strchrnul "; + name[StatInt_strrchr] = " strrchr "; + name[StatInt_strncmp] = " strncmp "; + name[StatInt_strcpy] = " strcpy "; + name[StatInt_strncpy] = " strncpy "; + name[StatInt_strstr] = " strstr "; + name[StatInt_strdup] = " strdup "; + name[StatInt_strcasecmp] = " strcasecmp "; + name[StatInt_strncasecmp] = " strncasecmp "; + name[StatInt_atexit] = " atexit "; + name[StatInt__exit] = " _exit "; + name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire "; + name[StatInt___cxa_guard_release] = " __cxa_guard_release "; + name[StatInt___cxa_guard_abort] = " __cxa_guard_abort "; + name[StatInt_pthread_create] = " pthread_create "; + name[StatInt_pthread_join] = " pthread_join "; + name[StatInt_pthread_detach] = " pthread_detach "; + name[StatInt_pthread_mutex_init] = " pthread_mutex_init "; + name[StatInt_pthread_mutex_destroy] = " pthread_mutex_destroy "; + name[StatInt_pthread_mutex_lock] = " pthread_mutex_lock "; + name[StatInt_pthread_mutex_trylock] = " pthread_mutex_trylock "; + name[StatInt_pthread_mutex_timedlock] = " pthread_mutex_timedlock "; + name[StatInt_pthread_mutex_unlock] = " pthread_mutex_unlock "; + name[StatInt_pthread_spin_init] = " pthread_spin_init "; + name[StatInt_pthread_spin_destroy] = " pthread_spin_destroy "; + name[StatInt_pthread_spin_lock] = " pthread_spin_lock "; + name[StatInt_pthread_spin_trylock] = " pthread_spin_trylock "; + name[StatInt_pthread_spin_unlock] = " pthread_spin_unlock "; + name[StatInt_pthread_rwlock_init] = " pthread_rwlock_init "; + name[StatInt_pthread_rwlock_destroy] = " pthread_rwlock_destroy "; + name[StatInt_pthread_rwlock_rdlock] = " pthread_rwlock_rdlock "; + name[StatInt_pthread_rwlock_tryrdlock] = " pthread_rwlock_tryrdlock "; + name[StatInt_pthread_rwlock_timedrdlock] + = " pthread_rwlock_timedrdlock "; + name[StatInt_pthread_rwlock_wrlock] = " pthread_rwlock_wrlock "; + name[StatInt_pthread_rwlock_trywrlock] = " pthread_rwlock_trywrlock "; + name[StatInt_pthread_rwlock_timedwrlock] + = " pthread_rwlock_timedwrlock "; + name[StatInt_pthread_rwlock_unlock] = " pthread_rwlock_unlock "; + name[StatInt_pthread_cond_init] = " pthread_cond_init "; + name[StatInt_pthread_cond_destroy] = " pthread_cond_destroy "; + name[StatInt_pthread_cond_signal] = " pthread_cond_signal "; + name[StatInt_pthread_cond_broadcast] = " pthread_cond_broadcast "; + name[StatInt_pthread_cond_wait] = " pthread_cond_wait "; + name[StatInt_pthread_cond_timedwait] = " pthread_cond_timedwait "; + name[StatInt_pthread_barrier_init] = " pthread_barrier_init "; + name[StatInt_pthread_barrier_destroy] = " pthread_barrier_destroy "; + name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait "; + name[StatInt_pthread_once] = " pthread_once "; + name[StatInt_pthread_getschedparam] = " pthread_getschedparam "; + name[StatInt_pthread_setname_np] = " pthread_setname_np "; + name[StatInt_sem_init] = " sem_init "; + name[StatInt_sem_destroy] = " sem_destroy "; + name[StatInt_sem_wait] = " sem_wait "; + name[StatInt_sem_trywait] = " sem_trywait "; + name[StatInt_sem_timedwait] = " sem_timedwait "; + name[StatInt_sem_post] = " sem_post "; + name[StatInt_sem_getvalue] = " sem_getvalue "; + name[StatInt_stat] = " stat "; + name[StatInt___xstat] = " __xstat "; + name[StatInt_stat64] = " stat64 "; + name[StatInt___xstat64] = " __xstat64 "; + name[StatInt_lstat] = " lstat "; + name[StatInt___lxstat] = " __lxstat "; + name[StatInt_lstat64] = " lstat64 "; + name[StatInt___lxstat64] = " __lxstat64 "; + name[StatInt_fstat] = " fstat "; + name[StatInt___fxstat] = " __fxstat "; + name[StatInt_fstat64] = " fstat64 "; + name[StatInt___fxstat64] = " __fxstat64 "; + name[StatInt_open] = " open "; + name[StatInt_open64] = " open64 "; + name[StatInt_creat] = " creat "; + name[StatInt_creat64] = " creat64 "; + name[StatInt_dup] = " dup "; + name[StatInt_dup2] = " dup2 "; + name[StatInt_dup3] = " dup3 "; + name[StatInt_eventfd] = " eventfd "; + name[StatInt_signalfd] = " signalfd "; + name[StatInt_inotify_init] = " inotify_init "; + name[StatInt_inotify_init1] = " inotify_init1 "; + name[StatInt_socket] = " socket "; + name[StatInt_socketpair] = " socketpair "; + name[StatInt_connect] = " connect "; + name[StatInt_bind] = " bind "; + name[StatInt_listen] = " listen "; + name[StatInt_accept] = " accept "; + name[StatInt_accept4] = " accept4 "; + name[StatInt_epoll_create] = " epoll_create "; + name[StatInt_epoll_create1] = " epoll_create1 "; + name[StatInt_close] = " close "; + name[StatInt___close] = " __close "; + name[StatInt___res_iclose] = " __res_iclose "; + name[StatInt_pipe] = " pipe "; + name[StatInt_pipe2] = " pipe2 "; + name[StatInt_read] = " read "; + name[StatInt_prctl] = " prctl "; + name[StatInt_pread] = " pread "; + name[StatInt_pread64] = " pread64 "; + name[StatInt_readv] = " readv "; + name[StatInt_preadv] = " preadv "; + name[StatInt_preadv64] = " preadv64 "; + name[StatInt_write] = " write "; + name[StatInt_pwrite] = " pwrite "; + name[StatInt_pwrite64] = " pwrite64 "; + name[StatInt_writev] = " writev "; + name[StatInt_pwritev] = " pwritev "; + name[StatInt_pwritev64] = " pwritev64 "; + name[StatInt_send] = " send "; + name[StatInt_sendmsg] = " sendmsg "; + name[StatInt_recv] = " recv "; + name[StatInt_recvmsg] = " recvmsg "; + name[StatInt_unlink] = " unlink "; + name[StatInt_fopen] = " fopen "; + name[StatInt_freopen] = " freopen "; + name[StatInt_fclose] = " fclose "; + name[StatInt_fread] = " fread "; + name[StatInt_fwrite] = " fwrite "; + name[StatInt_fflush] = " fflush "; + name[StatInt_abort] = " abort "; + name[StatInt_puts] = " puts "; + name[StatInt_rmdir] = " rmdir "; + name[StatInt_opendir] = " opendir "; + name[StatInt_epoll_ctl] = " epoll_ctl "; + name[StatInt_epoll_wait] = " epoll_wait "; + name[StatInt_poll] = " poll "; + name[StatInt_ppoll] = " ppoll "; + name[StatInt_sigaction] = " sigaction "; + name[StatInt_signal] = " signal "; + name[StatInt_sigsuspend] = " sigsuspend "; + name[StatInt_raise] = " raise "; + name[StatInt_kill] = " kill "; + name[StatInt_pthread_kill] = " pthread_kill "; + name[StatInt_sleep] = " sleep "; + name[StatInt_usleep] = " usleep "; + name[StatInt_nanosleep] = " nanosleep "; + name[StatInt_gettimeofday] = " gettimeofday "; + name[StatInt_fork] = " fork "; + name[StatInt_vscanf] = " vscanf "; + name[StatInt_vsscanf] = " vsscanf "; + name[StatInt_vfscanf] = " vfscanf "; + name[StatInt_scanf] = " scanf "; + name[StatInt_sscanf] = " sscanf "; + name[StatInt_fscanf] = " fscanf "; + name[StatInt___isoc99_vscanf] = " vscanf "; + name[StatInt___isoc99_vsscanf] = " vsscanf "; + name[StatInt___isoc99_vfscanf] = " vfscanf "; + name[StatInt___isoc99_scanf] = " scanf "; + name[StatInt___isoc99_sscanf] = " sscanf "; + name[StatInt___isoc99_fscanf] = " fscanf "; + name[StatInt_on_exit] = " on_exit "; + name[StatInt___cxa_atexit] = " __cxa_atexit "; + name[StatInt_localtime] = " localtime "; + name[StatInt_localtime_r] = " localtime_r "; + name[StatInt_gmtime] = " gmtime "; + name[StatInt_gmtime_r] = " gmtime_r "; + name[StatInt_ctime] = " ctime "; + name[StatInt_ctime_r] = " ctime_r "; + name[StatInt_asctime] = " asctime "; + name[StatInt_asctime_r] = " asctime_r "; + name[StatInt_strptime] = " strptime "; + name[StatInt_frexp] = " frexp "; + name[StatInt_frexpf] = " frexpf "; + name[StatInt_frexpl] = " frexpl "; + name[StatInt_getpwnam] = " getpwnam "; + name[StatInt_getpwuid] = " getpwuid "; + name[StatInt_getgrnam] = " getgrnam "; + name[StatInt_getgrgid] = " getgrgid "; + name[StatInt_getpwnam_r] = " getpwnam_r "; + name[StatInt_getpwuid_r] = " getpwuid_r "; + name[StatInt_getgrnam_r] = " getgrnam_r "; + name[StatInt_getgrgid_r] = " getgrgid_r "; + name[StatInt_clock_getres] = " clock_getres "; + name[StatInt_clock_gettime] = " clock_gettime "; + name[StatInt_clock_settime] = " clock_settime "; + name[StatInt_getitimer] = " getitimer "; + name[StatInt_setitimer] = " setitimer "; + name[StatInt_time] = " time "; + name[StatInt_glob] = " glob "; + name[StatInt_glob64] = " glob64 "; + name[StatInt_wait] = " wait "; + name[StatInt_waitid] = " waitid "; + name[StatInt_waitpid] = " waitpid "; + name[StatInt_wait3] = " wait3 "; + name[StatInt_wait4] = " wait4 "; + name[StatInt_inet_ntop] = " inet_ntop "; + name[StatInt_inet_pton] = " inet_pton "; + name[StatInt_inet_aton] = " inet_aton "; + name[StatInt_getaddrinfo] = " getaddrinfo "; + name[StatInt_getnameinfo] = " getnameinfo "; + name[StatInt_getsockname] = " getsockname "; + name[StatInt_gethostent] = " gethostent "; + name[StatInt_gethostbyname] = " gethostbyname "; + name[StatInt_gethostbyname2] = " gethostbyname2 "; + name[StatInt_gethostbyaddr] = " gethostbyaddr "; + name[StatInt_gethostent_r] = " gethostent_r "; + name[StatInt_gethostbyname_r] = " gethostbyname_r "; + name[StatInt_gethostbyname2_r] = " gethostbyname2_r "; + name[StatInt_gethostbyaddr_r] = " gethostbyaddr_r "; + name[StatInt_getsockopt] = " getsockopt "; + name[StatInt_modf] = " modf "; + name[StatInt_modff] = " modff "; + name[StatInt_modfl] = " modfl "; + name[StatInt_getpeername] = " getpeername "; + name[StatInt_ioctl] = " ioctl "; + name[StatInt_sysinfo] = " sysinfo "; + name[StatInt_readdir] = " readdir "; + name[StatInt_readdir64] = " readdir64 "; + name[StatInt_readdir_r] = " readdir_r "; + name[StatInt_readdir64_r] = " readdir64_r "; + name[StatInt_ptrace] = " ptrace "; + name[StatInt_setlocale] = " setlocale "; + name[StatInt_getcwd] = " getcwd "; + name[StatInt_get_current_dir_name] = " get_current_dir_name "; + name[StatInt_strtoimax] = " strtoimax "; + name[StatInt_strtoumax] = " strtoumax "; + name[StatInt_mbstowcs] = " mbstowcs "; + name[StatInt_mbsrtowcs] = " mbsrtowcs "; + name[StatInt_mbsnrtowcs] = " mbsnrtowcs "; + name[StatInt_wcstombs] = " wcstombs "; + name[StatInt_wcsrtombs] = " wcsrtombs "; + name[StatInt_wcsnrtombs] = " wcsnrtombs "; + name[StatInt_tcgetattr] = " tcgetattr "; + name[StatInt_realpath] = " realpath "; + name[StatInt_canonicalize_file_name] = " canonicalize_file_name "; + name[StatInt_confstr] = " confstr "; + name[StatInt_sched_getaffinity] = " sched_getaffinity "; + name[StatInt_strerror] = " strerror "; + name[StatInt_strerror_r] = " strerror_r "; + name[StatInt_scandir] = " scandir "; + name[StatInt_scandir64] = " scandir64 "; + name[StatInt_getgroups] = " getgroups "; + name[StatInt_wordexp] = " wordexp "; + name[StatInt_sigwait] = " sigwait "; + name[StatInt_sigwaitinfo] = " sigwaitinfo "; + name[StatInt_sigtimedwait] = " sigtimedwait "; + name[StatInt_sigemptyset] = " sigemptyset "; + name[StatInt_sigfillset] = " sigfillset "; + name[StatInt_sigpending] = " sigpending "; + name[StatInt_sigprocmask] = " sigprocmask "; + name[StatInt_backtrace] = " backtrace "; + name[StatInt_backtrace_symbols] = " backtrace_symbols "; + name[StatInt_dlopen] = " dlopen "; + name[StatInt_dlclose] = " dlclose "; + name[StatInt_getmntent] = " getmntent "; + name[StatInt_getmntent_r] = " getmntent_r "; + name[StatInt_statfs] = " statfs "; + name[StatInt_statfs64] = " statfs64 "; + name[StatInt_fstatfs] = " fstatfs "; + name[StatInt_fstatfs64] = " fstatfs64 "; + name[StatInt_statvfs] = " statvfs "; + name[StatInt_statvfs64] = " statvfs64 "; + name[StatInt_fstatvfs] = " fstatvfs "; + name[StatInt_fstatvfs64] = " fstatvfs64 "; + name[StatInt_initgroups] = " initgroups "; + name[StatInt_ether_ntoa] = " ether_ntoa "; + name[StatInt_ether_aton] = " ether_aton "; + name[StatInt_ether_ntoa_r] = " ether_ntoa_r "; + name[StatInt_ether_aton_r] = " ether_aton_r "; + name[StatInt_ether_ntohost] = " ether_ntohost "; + name[StatInt_ether_hostton] = " ether_hostton "; + name[StatInt_ether_line] = " ether_line "; + name[StatInt_shmctl] = " shmctl "; + name[StatInt_random_r] = " random_r "; + name[StatInt_tmpnam] = " tmpnam "; + name[StatInt_tmpnam_r] = " tmpnam_r "; + name[StatInt_tempnam] = " tempnam "; + name[StatInt_sincos] = " sincos "; + name[StatInt_sincosf] = " sincosf "; + name[StatInt_sincosl] = " sincosl "; + name[StatInt_remquo] = " remquo "; + name[StatInt_remquof] = " remquof "; + name[StatInt_remquol] = " remquol "; + name[StatInt_lgamma] = " lgamma "; + name[StatInt_lgammaf] = " lgammaf "; + name[StatInt_lgammal] = " lgammal "; + name[StatInt_lgamma_r] = " lgamma_r "; + name[StatInt_lgammaf_r] = " lgammaf_r "; + name[StatInt_lgammal_r] = " lgammal_r "; + name[StatInt_drand48_r] = " drand48_r "; + name[StatInt_lrand48_r] = " lrand48_r "; + name[StatInt_getline] = " getline "; + name[StatInt_getdelim] = " getdelim "; + + name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT + name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT + name[StatInt_pthread_attr_getschedparam] = " pthread_addr_getschedparam "; // NOLINT + name[StatInt_pthread_attr_getschedpolicy] = " pthread_addr_getschedpolicy "; // NOLINT + name[StatInt_pthread_attr_getinheritsched] = " pthread_addr_getinheritsched "; // NOLINT + name[StatInt_pthread_attr_getscope] = " pthread_addr_getscope "; // NOLINT + name[StatInt_pthread_attr_getstacksize] = " pthread_addr_getstacksize "; // NOLINT + name[StatInt_pthread_attr_getstack] = " pthread_addr_getstack "; // NOLINT + name[StatInt_pthread_attr_getaffinity_np] = " pthread_addr_getaffinity_np "; // NOLINT + + name[StatAnnotation] = "Dynamic annotations "; + name[StatAnnotateHappensBefore] = " HappensBefore "; + name[StatAnnotateHappensAfter] = " HappensAfter "; + name[StatAnnotateCondVarSignal] = " CondVarSignal "; + name[StatAnnotateCondVarSignalAll] = " CondVarSignalAll "; + name[StatAnnotateMutexIsNotPHB] = " MutexIsNotPHB "; + name[StatAnnotateCondVarWait] = " CondVarWait "; + name[StatAnnotateRWLockCreate] = " RWLockCreate "; + name[StatAnnotateRWLockCreateStatic] = " StatAnnotateRWLockCreateStatic "; + name[StatAnnotateRWLockDestroy] = " RWLockDestroy "; + name[StatAnnotateRWLockAcquired] = " RWLockAcquired "; + name[StatAnnotateRWLockReleased] = " RWLockReleased "; + name[StatAnnotateTraceMemory] = " TraceMemory "; + name[StatAnnotateFlushState] = " FlushState "; + name[StatAnnotateNewMemory] = " NewMemory "; + name[StatAnnotateNoOp] = " NoOp "; + name[StatAnnotateFlushExpectedRaces] = " FlushExpectedRaces "; + name[StatAnnotateEnableRaceDetection] = " EnableRaceDetection "; + name[StatAnnotateMutexIsUsedAsCondVar] = " MutexIsUsedAsCondVar "; + name[StatAnnotatePCQGet] = " PCQGet "; + name[StatAnnotatePCQPut] = " PCQPut "; + name[StatAnnotatePCQDestroy] = " PCQDestroy "; + name[StatAnnotatePCQCreate] = " PCQCreate "; + name[StatAnnotateExpectRace] = " ExpectRace "; + name[StatAnnotateBenignRaceSized] = " BenignRaceSized "; + name[StatAnnotateBenignRace] = " BenignRace "; + name[StatAnnotateIgnoreReadsBegin] = " IgnoreReadsBegin "; + name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd "; + name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin "; + name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd "; + name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin "; + name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd "; + name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange "; + name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange "; + name[StatAnnotateThreadName] = " ThreadName "; + + name[StatMtxTotal] = "Contentionz "; + name[StatMtxTrace] = " Trace "; + name[StatMtxThreads] = " Threads "; + name[StatMtxReport] = " Report "; + name[StatMtxSyncVar] = " SyncVar "; + name[StatMtxSyncTab] = " SyncTab "; + name[StatMtxSlab] = " Slab "; + name[StatMtxAtExit] = " Atexit "; + name[StatMtxAnnotations] = " Annotations "; + name[StatMtxMBlock] = " MBlock "; + name[StatMtxJavaMBlock] = " JavaMBlock "; + name[StatMtxFD] = " FD "; + + Printf("Statistics:\n"); + for (int i = 0; i < StatCnt; i++) + Printf("%s: %zu\n", name[i], (uptr)stat[i]); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_stat.h b/projects/compiler-rt/lib/tsan/rtl/tsan_stat.h new file mode 100644 index 00000000..a44edfcd --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_stat.h @@ -0,0 +1,481 @@ +//===-- tsan_stat.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAN_STAT_H +#define TSAN_STAT_H + +namespace __tsan { + +enum StatType { + // Memory access processing related stuff. + StatMop, + StatMopRead, + StatMopWrite, + StatMop1, // These must be consequtive. + StatMop2, + StatMop4, + StatMop8, + StatMopSame, + StatMopRange, + StatMopRodata, + StatMopRangeRodata, + StatShadowProcessed, + StatShadowZero, + StatShadowNonZero, // Derived. + StatShadowSameSize, + StatShadowIntersect, + StatShadowNotIntersect, + StatShadowSameThread, + StatShadowAnotherThread, + StatShadowReplace, + + // Func processing. + StatFuncEnter, + StatFuncExit, + + // Trace processing. + StatEvents, + + // Threads. + StatThreadCreate, + StatThreadFinish, + StatThreadReuse, + StatThreadMaxTid, + StatThreadMaxAlive, + + // Mutexes. + StatMutexCreate, + StatMutexDestroy, + StatMutexLock, + StatMutexUnlock, + StatMutexRecLock, + StatMutexRecUnlock, + StatMutexReadLock, + StatMutexReadUnlock, + + // Synchronization. + StatSyncCreated, + StatSyncDestroyed, + StatSyncAcquire, + StatSyncRelease, + + // Atomics. + StatAtomic, + StatAtomicLoad, + StatAtomicStore, + StatAtomicExchange, + StatAtomicFetchAdd, + StatAtomicFetchSub, + StatAtomicFetchAnd, + StatAtomicFetchOr, + StatAtomicFetchXor, + StatAtomicFetchNand, + StatAtomicCAS, + StatAtomicFence, + StatAtomicRelaxed, + StatAtomicConsume, + StatAtomicAcquire, + StatAtomicRelease, + StatAtomicAcq_Rel, + StatAtomicSeq_Cst, + StatAtomic1, + StatAtomic2, + StatAtomic4, + StatAtomic8, + StatAtomic16, + + // Interceptors. + StatInterceptor, + StatInt_longjmp, + StatInt_siglongjmp, + StatInt_malloc, + StatInt___libc_memalign, + StatInt_calloc, + StatInt_realloc, + StatInt_free, + StatInt_cfree, + StatInt_malloc_usable_size, + StatInt_mmap, + StatInt_mmap64, + StatInt_munmap, + StatInt_memalign, + StatInt_valloc, + StatInt_pvalloc, + StatInt_posix_memalign, + StatInt__Znwm, + StatInt__ZnwmRKSt9nothrow_t, + StatInt__Znam, + StatInt__ZnamRKSt9nothrow_t, + StatInt__ZdlPv, + StatInt__ZdlPvRKSt9nothrow_t, + StatInt__ZdaPv, + StatInt__ZdaPvRKSt9nothrow_t, + StatInt_strlen, + StatInt_memset, + StatInt_memcpy, + StatInt_strcmp, + StatInt_memchr, + StatInt_memrchr, + StatInt_memmove, + StatInt_memcmp, + StatInt_strchr, + StatInt_strchrnul, + StatInt_strrchr, + StatInt_strncmp, + StatInt_strcpy, + StatInt_strncpy, + StatInt_strcasecmp, + StatInt_strncasecmp, + StatInt_strstr, + StatInt_strdup, + StatInt_atexit, + StatInt__exit, + StatInt___cxa_guard_acquire, + StatInt___cxa_guard_release, + StatInt___cxa_guard_abort, + StatInt_pthread_create, + StatInt_pthread_join, + StatInt_pthread_detach, + StatInt_pthread_mutex_init, + StatInt_pthread_mutex_destroy, + StatInt_pthread_mutex_lock, + StatInt_pthread_mutex_trylock, + StatInt_pthread_mutex_timedlock, + StatInt_pthread_mutex_unlock, + StatInt_pthread_spin_init, + StatInt_pthread_spin_destroy, + StatInt_pthread_spin_lock, + StatInt_pthread_spin_trylock, + StatInt_pthread_spin_unlock, + StatInt_pthread_rwlock_init, + StatInt_pthread_rwlock_destroy, + StatInt_pthread_rwlock_rdlock, + StatInt_pthread_rwlock_tryrdlock, + StatInt_pthread_rwlock_timedrdlock, + StatInt_pthread_rwlock_wrlock, + StatInt_pthread_rwlock_trywrlock, + StatInt_pthread_rwlock_timedwrlock, + StatInt_pthread_rwlock_unlock, + StatInt_pthread_cond_init, + StatInt_pthread_cond_destroy, + StatInt_pthread_cond_signal, + StatInt_pthread_cond_broadcast, + StatInt_pthread_cond_wait, + StatInt_pthread_cond_timedwait, + StatInt_pthread_barrier_init, + StatInt_pthread_barrier_destroy, + StatInt_pthread_barrier_wait, + StatInt_pthread_once, + StatInt_pthread_getschedparam, + StatInt_pthread_setname_np, + StatInt_sem_init, + StatInt_sem_destroy, + StatInt_sem_wait, + StatInt_sem_trywait, + StatInt_sem_timedwait, + StatInt_sem_post, + StatInt_sem_getvalue, + StatInt_stat, + StatInt___xstat, + StatInt_stat64, + StatInt___xstat64, + StatInt_lstat, + StatInt___lxstat, + StatInt_lstat64, + StatInt___lxstat64, + StatInt_fstat, + StatInt___fxstat, + StatInt_fstat64, + StatInt___fxstat64, + StatInt_open, + StatInt_open64, + StatInt_creat, + StatInt_creat64, + StatInt_dup, + StatInt_dup2, + StatInt_dup3, + StatInt_eventfd, + StatInt_signalfd, + StatInt_inotify_init, + StatInt_inotify_init1, + StatInt_socket, + StatInt_socketpair, + StatInt_connect, + StatInt_bind, + StatInt_listen, + StatInt_accept, + StatInt_accept4, + StatInt_epoll_create, + StatInt_epoll_create1, + StatInt_close, + StatInt___close, + StatInt___res_iclose, + StatInt_pipe, + StatInt_pipe2, + StatInt_read, + StatInt_prctl, + StatInt_pread, + StatInt_pread64, + StatInt_readv, + StatInt_preadv, + StatInt_preadv64, + StatInt_write, + StatInt_pwrite, + StatInt_pwrite64, + StatInt_writev, + StatInt_pwritev, + StatInt_pwritev64, + StatInt_send, + StatInt_sendmsg, + StatInt_recv, + StatInt_recvmsg, + StatInt_unlink, + StatInt_fopen, + StatInt_freopen, + StatInt_fclose, + StatInt_fread, + StatInt_fwrite, + StatInt_fflush, + StatInt_abort, + StatInt_puts, + StatInt_rmdir, + StatInt_opendir, + StatInt_epoll_ctl, + StatInt_epoll_wait, + StatInt_poll, + StatInt_ppoll, + StatInt_sigaction, + StatInt_signal, + StatInt_sigsuspend, + StatInt_raise, + StatInt_kill, + StatInt_pthread_kill, + StatInt_sleep, + StatInt_usleep, + StatInt_nanosleep, + StatInt_gettimeofday, + StatInt_fork, + StatInt_vscanf, + StatInt_vsscanf, + StatInt_vfscanf, + StatInt_scanf, + StatInt_sscanf, + StatInt_fscanf, + StatInt___isoc99_vscanf, + StatInt___isoc99_vsscanf, + StatInt___isoc99_vfscanf, + StatInt___isoc99_scanf, + StatInt___isoc99_sscanf, + StatInt___isoc99_fscanf, + StatInt_on_exit, + StatInt___cxa_atexit, + StatInt_localtime, + StatInt_localtime_r, + StatInt_gmtime, + StatInt_gmtime_r, + StatInt_ctime, + StatInt_ctime_r, + StatInt_asctime, + StatInt_asctime_r, + StatInt_strptime, + StatInt_frexp, + StatInt_frexpf, + StatInt_frexpl, + StatInt_getpwnam, + StatInt_getpwuid, + StatInt_getgrnam, + StatInt_getgrgid, + StatInt_getpwnam_r, + StatInt_getpwuid_r, + StatInt_getgrnam_r, + StatInt_getgrgid_r, + StatInt_clock_getres, + StatInt_clock_gettime, + StatInt_clock_settime, + StatInt_getitimer, + StatInt_setitimer, + StatInt_time, + StatInt_glob, + StatInt_glob64, + StatInt_wait, + StatInt_waitid, + StatInt_waitpid, + StatInt_wait3, + StatInt_wait4, + StatInt_inet_ntop, + StatInt_inet_pton, + StatInt_inet_aton, + StatInt_getaddrinfo, + StatInt_getnameinfo, + StatInt_getsockname, + StatInt_gethostent, + StatInt_gethostbyname, + StatInt_gethostbyname2, + StatInt_gethostbyaddr, + StatInt_gethostent_r, + StatInt_gethostbyname_r, + StatInt_gethostbyname2_r, + StatInt_gethostbyaddr_r, + StatInt_getsockopt, + StatInt_modf, + StatInt_modff, + StatInt_modfl, + StatInt_getpeername, + StatInt_ioctl, + StatInt_sysinfo, + StatInt_readdir, + StatInt_readdir64, + StatInt_readdir_r, + StatInt_readdir64_r, + StatInt_ptrace, + StatInt_setlocale, + StatInt_getcwd, + StatInt_get_current_dir_name, + StatInt_strtoimax, + StatInt_strtoumax, + StatInt_mbstowcs, + StatInt_mbsrtowcs, + StatInt_mbsnrtowcs, + StatInt_wcstombs, + StatInt_wcsrtombs, + StatInt_wcsnrtombs, + StatInt_tcgetattr, + StatInt_realpath, + StatInt_canonicalize_file_name, + StatInt_confstr, + StatInt_sched_getaffinity, + StatInt_strerror, + StatInt_strerror_r, + StatInt_scandir, + StatInt_scandir64, + StatInt_getgroups, + StatInt_wordexp, + StatInt_sigwait, + StatInt_sigwaitinfo, + StatInt_sigtimedwait, + StatInt_sigemptyset, + StatInt_sigfillset, + StatInt_sigpending, + StatInt_sigprocmask, + StatInt_backtrace, + StatInt_backtrace_symbols, + StatInt_dlopen, + StatInt_dlclose, + StatInt_getmntent, + StatInt_getmntent_r, + StatInt_statfs, + StatInt_statfs64, + StatInt_fstatfs, + StatInt_fstatfs64, + StatInt_statvfs, + StatInt_statvfs64, + StatInt_fstatvfs, + StatInt_fstatvfs64, + StatInt_initgroups, + StatInt_ether_ntoa, + StatInt_ether_aton, + StatInt_ether_ntoa_r, + StatInt_ether_aton_r, + StatInt_ether_ntohost, + StatInt_ether_hostton, + StatInt_ether_line, + StatInt_shmctl, + StatInt_random_r, + StatInt_tmpnam, + StatInt_tmpnam_r, + StatInt_tempnam, + StatInt_sincos, + StatInt_sincosf, + StatInt_sincosl, + StatInt_remquo, + StatInt_remquof, + StatInt_remquol, + StatInt_lgamma, + StatInt_lgammaf, + StatInt_lgammal, + StatInt_lgamma_r, + StatInt_lgammaf_r, + StatInt_lgammal_r, + StatInt_drand48_r, + StatInt_lrand48_r, + StatInt_getline, + StatInt_getdelim, + + StatInt_pthread_attr_getdetachstate, + StatInt_pthread_attr_getguardsize, + StatInt_pthread_attr_getschedparam, + StatInt_pthread_attr_getschedpolicy, + StatInt_pthread_attr_getinheritsched, + StatInt_pthread_attr_getscope, + StatInt_pthread_attr_getstacksize, + StatInt_pthread_attr_getstack, + StatInt_pthread_attr_getaffinity_np, + + // Dynamic annotations. + StatAnnotation, + StatAnnotateHappensBefore, + StatAnnotateHappensAfter, + StatAnnotateCondVarSignal, + StatAnnotateCondVarSignalAll, + StatAnnotateMutexIsNotPHB, + StatAnnotateCondVarWait, + StatAnnotateRWLockCreate, + StatAnnotateRWLockCreateStatic, + StatAnnotateRWLockDestroy, + StatAnnotateRWLockAcquired, + StatAnnotateRWLockReleased, + StatAnnotateTraceMemory, + StatAnnotateFlushState, + StatAnnotateNewMemory, + StatAnnotateNoOp, + StatAnnotateFlushExpectedRaces, + StatAnnotateEnableRaceDetection, + StatAnnotateMutexIsUsedAsCondVar, + StatAnnotatePCQGet, + StatAnnotatePCQPut, + StatAnnotatePCQDestroy, + StatAnnotatePCQCreate, + StatAnnotateExpectRace, + StatAnnotateBenignRaceSized, + StatAnnotateBenignRace, + StatAnnotateIgnoreReadsBegin, + StatAnnotateIgnoreReadsEnd, + StatAnnotateIgnoreWritesBegin, + StatAnnotateIgnoreWritesEnd, + StatAnnotateIgnoreSyncBegin, + StatAnnotateIgnoreSyncEnd, + StatAnnotatePublishMemoryRange, + StatAnnotateUnpublishMemoryRange, + StatAnnotateThreadName, + + // Internal mutex contentionz. + StatMtxTotal, + StatMtxTrace, + StatMtxThreads, + StatMtxReport, + StatMtxSyncVar, + StatMtxSyncTab, + StatMtxSlab, + StatMtxAnnotations, + StatMtxAtExit, + StatMtxMBlock, + StatMtxJavaMBlock, + StatMtxFD, + + // This must be the last. + StatCnt +}; + +} // namespace __tsan + +#endif // TSAN_STAT_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc new file mode 100644 index 00000000..4b0ac04c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc @@ -0,0 +1,170 @@ +//===-- tsan_suppressions.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_suppressions.h" +#include "tsan_suppressions.h" +#include "tsan_rtl.h" +#include "tsan_flags.h" +#include "tsan_mman.h" +#include "tsan_platform.h" + +// Suppressions for true/false positives in standard libraries. +static const char *const std_suppressions = +// Libstdc++ 4.4 has data races in std::string. +// See http://crbug.com/181502 for an example. +"race:^_M_rep$\n" +"race:^_M_is_leaked$\n" +// False positive when using std . +// Happens because we miss atomic synchronization in libstdc++. +// See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. +"race:std::_Sp_counted_ptr_inplace tmp(4*1024); + if (filename[0] == '/' || GetPwd() == 0) + internal_snprintf(tmp.data(), tmp.size(), "%s", filename); + else + internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename); + uptr openrv = OpenFile(tmp.data(), false); + if (internal_iserror(openrv)) { + Printf("ThreadSanitizer: failed to open suppressions file '%s'\n", + tmp.data()); + Die(); + } + fd_t fd = openrv; + const uptr fsize = internal_filesize(fd); + if (fsize == (uptr)-1) { + Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n", + tmp.data()); + Die(); + } + char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1); + if (fsize != internal_read(fd, buf, fsize)) { + Printf("ThreadSanitizer: failed to read suppressions file '%s'\n", + tmp.data()); + Die(); + } + internal_close(fd); + buf[fsize] = 0; + return buf; +} + +void InitializeSuppressions() { + ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; + g_ctx = new(placeholder_) SuppressionContext; + const char *supp = ReadFile(flags()->suppressions); + g_ctx->Parse(supp); +#ifndef TSAN_GO + supp = __tsan_default_suppressions(); + g_ctx->Parse(supp); + g_ctx->Parse(std_suppressions); +#endif +} + +SuppressionContext *GetSuppressionContext() { + CHECK_NE(g_ctx, 0); + return g_ctx; +} + +SuppressionType conv(ReportType typ) { + if (typ == ReportTypeRace) + return SuppressionRace; + else if (typ == ReportTypeVptrRace) + return SuppressionRace; + else if (typ == ReportTypeUseAfterFree) + return SuppressionRace; + else if (typ == ReportTypeThreadLeak) + return SuppressionThread; + else if (typ == ReportTypeMutexDestroyLocked) + return SuppressionMutex; + else if (typ == ReportTypeSignalUnsafe) + return SuppressionSignal; + else if (typ == ReportTypeErrnoInSignal) + return SuppressionNone; + Printf("ThreadSanitizer: unknown report type %d\n", typ), + Die(); +} + +uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || stack == 0) return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) + return 0; + Suppression *s; + for (const ReportStack *frame = stack; frame; frame = frame->next) { + if (g_ctx->Match(frame->func, stype, &s) || + g_ctx->Match(frame->file, stype, &s) || + g_ctx->Match(frame->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return frame->pc; + } + } + return 0; +} + +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || loc == 0 || + loc->type != ReportLocationGlobal) + return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) + return 0; + Suppression *s; + if (g_ctx->Match(loc->name, stype, &s) || + g_ctx->Match(loc->file, stype, &s) || + g_ctx->Match(loc->module, stype, &s)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); + s->hit_count++; + *sp = s; + return loc->addr; + } + return 0; +} + +void PrintMatchedSuppressions() { + CHECK(g_ctx); + InternalMmapVector matched(1); + g_ctx->GetMatched(&matched); + if (!matched.size()) + return; + int hit_count = 0; + for (uptr i = 0; i < matched.size(); i++) + hit_count += matched[i]->hit_count; + Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count, + (int)internal_getpid()); + for (uptr i = 0; i < matched.size(); i++) { + Printf("%d %s:%s\n", matched[i]->hit_count, + SuppressionTypeString(matched[i]->type), matched[i]->templ); + } +} +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_suppressions.h b/projects/compiler-rt/lib/tsan/rtl/tsan_suppressions.h new file mode 100644 index 00000000..fe7db588 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_suppressions.h @@ -0,0 +1,29 @@ +//===-- tsan_suppressions.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_SUPPRESSIONS_H +#define TSAN_SUPPRESSIONS_H + +#include "sanitizer_common/sanitizer_suppressions.h" +#include "tsan_report.h" + +namespace __tsan { + +void InitializeSuppressions(); +void PrintMatchedSuppressions(); +uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); +SuppressionContext *GetSuppressionContext(); + +} // namespace __tsan + +#endif // TSAN_SUPPRESSIONS_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc new file mode 100644 index 00000000..75acf1d8 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc @@ -0,0 +1,158 @@ +//===-- tsan_symbolize.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +#include "tsan_symbolize.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_symbolizer.h" +#include "tsan_flags.h" +#include "tsan_report.h" +#include "tsan_rtl.h" + +namespace __tsan { + +void EnterSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(!thr->in_symbolizer); + thr->in_symbolizer = true; +} + +void ExitSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(thr->in_symbolizer); + thr->in_symbolizer = false; +} + +ReportStack *NewReportStackEntry(uptr addr) { + ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, + sizeof(ReportStack)); + internal_memset(ent, 0, sizeof(*ent)); + ent->pc = addr; + return ent; +} + +static ReportStack *NewReportStackEntry(const AddressInfo &info) { + ReportStack *ent = NewReportStackEntry(info.address); + ent->module = StripModuleName(info.module); + ent->offset = info.module_offset; + if (info.function) + ent->func = internal_strdup(info.function); + if (info.file) + ent->file = internal_strdup(info.file); + ent->line = info.line; + ent->col = info.column; + return ent; +} + + + ReportStack *next; + char *module; + uptr offset; + uptr pc; + char *func; + char *file; + int line; + int col; + + +// Denotes fake PC values that come from JIT/JAVA/etc. +// For such PC values __tsan_symbolize_external() will be called. +const uptr kExternalPCBit = 1ULL << 60; + +// May be overriden by JIT/JAVA/etc, +// whatever produces PCs marked with kExternalPCBit. +extern "C" bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) + SANITIZER_WEAK_ATTRIBUTE; + +bool __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) { + return false; +} + +ReportStack *SymbolizeCode(uptr addr) { + // Check if PC comes from non-native land. + if (addr & kExternalPCBit) { + // Declare static to not consume too much stack space. + // We symbolize reports in a single thread, so this is fine. + static char func_buf[1024]; + static char file_buf[1024]; + int line, col; + if (!__tsan_symbolize_external(addr, func_buf, sizeof(func_buf), + file_buf, sizeof(file_buf), &line, &col)) + return NewReportStackEntry(addr); + ReportStack *ent = NewReportStackEntry(addr); + ent->module = 0; + ent->offset = 0; + ent->func = internal_strdup(func_buf); + ent->file = internal_strdup(file_buf); + ent->line = line; + ent->col = col; + return ent; + } + if (!Symbolizer::Get()->IsAvailable()) + return SymbolizeCodeAddr2Line(addr); + static const uptr kMaxAddrFrames = 16; + InternalScopedBuffer addr_frames(kMaxAddrFrames); + for (uptr i = 0; i < kMaxAddrFrames; i++) + new(&addr_frames[i]) AddressInfo(); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); + if (addr_frames_num == 0) + return NewReportStackEntry(addr); + ReportStack *top = 0; + ReportStack *bottom = 0; + for (uptr i = 0; i < addr_frames_num; i++) { + ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]); + CHECK(cur_entry); + addr_frames[i].Clear(); + if (i == 0) + top = cur_entry; + else + bottom->next = cur_entry; + bottom = cur_entry; + } + return top; +} + +ReportLocation *SymbolizeData(uptr addr) { + if (!Symbolizer::Get()->IsAvailable()) + return 0; + DataInfo info; + if (!Symbolizer::Get()->SymbolizeData(addr, &info)) + return 0; + ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack, + sizeof(ReportLocation)); + internal_memset(ent, 0, sizeof(*ent)); + ent->type = ReportLocationGlobal; + ent->module = StripModuleName(info.module); + ent->offset = info.module_offset; + if (info.name) + ent->name = internal_strdup(info.name); + ent->addr = info.start; + ent->size = info.size; + return ent; +} + +void SymbolizeFlush() { + if (!Symbolizer::Get()->IsAvailable()) + return; + Symbolizer::Get()->Flush(); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize.h b/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize.h new file mode 100644 index 00000000..6282e45b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize.h @@ -0,0 +1,33 @@ +//===-- tsan_symbolize.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_SYMBOLIZE_H +#define TSAN_SYMBOLIZE_H + +#include "tsan_defs.h" +#include "tsan_report.h" + +namespace __tsan { + +void EnterSymbolizer(); +void ExitSymbolizer(); +ReportStack *SymbolizeCode(uptr addr); +ReportLocation *SymbolizeData(uptr addr); +void SymbolizeFlush(); + +ReportStack *SymbolizeCodeAddr2Line(uptr addr); + +ReportStack *NewReportStackEntry(uptr addr); + +} // namespace __tsan + +#endif // TSAN_SYMBOLIZE_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc new file mode 100644 index 00000000..b186d3b3 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc @@ -0,0 +1,193 @@ +//===-- tsan_symbolize_addr2line.cc ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "tsan_symbolize.h" +#include "tsan_mman.h" +#include "tsan_rtl.h" +#include "tsan_platform.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace __tsan { + +struct ModuleDesc { + const char *fullname; + const char *name; + uptr base; + int inp_fd; + int out_fd; +}; + +struct SectionDesc { + SectionDesc *next; + ModuleDesc *module; + uptr base; + uptr end; +}; + +struct DlIteratePhdrCtx { + SectionDesc *sections; + bool is_first; +}; + +static void NOINLINE InitModule(ModuleDesc *m) { + int outfd[2] = {}; + if (pipe(&outfd[0])) { + Printf("ThreadSanitizer: outfd pipe() failed (%d)\n", errno); + Die(); + } + int infd[2] = {}; + if (pipe(&infd[0])) { + Printf("ThreadSanitizer: infd pipe() failed (%d)\n", errno); + Die(); + } + int pid = fork(); + if (pid == 0) { + internal_close(STDOUT_FILENO); + internal_close(STDIN_FILENO); + internal_dup2(outfd[0], STDIN_FILENO); + internal_dup2(infd[1], STDOUT_FILENO); + internal_close(outfd[0]); + internal_close(outfd[1]); + internal_close(infd[0]); + internal_close(infd[1]); + for (int fd = getdtablesize(); fd > 2; fd--) + internal_close(fd); + execl("/usr/bin/addr2line", "/usr/bin/addr2line", "-Cfe", m->fullname, 0); + _exit(0); + } else if (pid < 0) { + Printf("ThreadSanitizer: failed to fork symbolizer\n"); + Die(); + } + internal_close(outfd[0]); + internal_close(infd[1]); + m->inp_fd = infd[0]; + m->out_fd = outfd[1]; +} + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrCtx *ctx = (DlIteratePhdrCtx*)arg; + InternalScopedBuffer tmp(128); + if (ctx->is_first) { + internal_snprintf(tmp.data(), tmp.size(), "/proc/%d/exe", + (int)internal_getpid()); + info->dlpi_name = tmp.data(); + } + ctx->is_first = false; + if (info->dlpi_name == 0 || info->dlpi_name[0] == 0) + return 0; + ModuleDesc *m = (ModuleDesc*)internal_alloc(MBlockReportStack, + sizeof(ModuleDesc)); + m->fullname = internal_strdup(info->dlpi_name); + m->name = internal_strrchr(m->fullname, '/'); + if (m->name) + m->name += 1; + else + m->name = m->fullname; + m->base = (uptr)info->dlpi_addr; + m->inp_fd = -1; + m->out_fd = -1; + DPrintf2("Module %s %zx\n", m->name, m->base); + for (int i = 0; i < info->dlpi_phnum; i++) { + const Elf64_Phdr *s = &info->dlpi_phdr[i]; + DPrintf2(" Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx" + " p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n", + (uptr)s->p_type, (uptr)s->p_offset, (uptr)s->p_vaddr, + (uptr)s->p_paddr, (uptr)s->p_filesz, (uptr)s->p_memsz, + (uptr)s->p_flags, (uptr)s->p_align); + if (s->p_type != PT_LOAD) + continue; + SectionDesc *sec = (SectionDesc*)internal_alloc(MBlockReportStack, + sizeof(SectionDesc)); + sec->module = m; + sec->base = info->dlpi_addr + s->p_vaddr; + sec->end = sec->base + s->p_memsz; + sec->next = ctx->sections; + ctx->sections = sec; + DPrintf2(" Section %zx-%zx\n", sec->base, sec->end); + } + return 0; +} + +static SectionDesc *InitSections() { + DlIteratePhdrCtx ctx = {0, true}; + dl_iterate_phdr(dl_iterate_phdr_cb, &ctx); + return ctx.sections; +} + +static SectionDesc *GetSectionDesc(uptr addr) { + static SectionDesc *sections = 0; + if (sections == 0) + sections = InitSections(); + for (SectionDesc *s = sections; s; s = s->next) { + if (addr >= s->base && addr < s->end) { + if (s->module->inp_fd == -1) + InitModule(s->module); + return s; + } + } + return 0; +} + +ReportStack *SymbolizeCodeAddr2Line(uptr addr) { + SectionDesc *s = GetSectionDesc(addr); + if (s == 0) + return NewReportStackEntry(addr); + ModuleDesc *m = s->module; + uptr offset = addr - m->base; + char addrstr[32]; + internal_snprintf(addrstr, sizeof(addrstr), "%p\n", (void*)offset); + if (0 >= internal_write(m->out_fd, addrstr, internal_strlen(addrstr))) { + Printf("ThreadSanitizer: can't write from symbolizer (%d, %d)\n", + m->out_fd, errno); + Die(); + } + InternalScopedBuffer func(1024); + ssize_t len = internal_read(m->inp_fd, func.data(), func.size() - 1); + if (len <= 0) { + Printf("ThreadSanitizer: can't read from symbolizer (%d, %d)\n", + m->inp_fd, errno); + Die(); + } + func.data()[len] = 0; + ReportStack *res = NewReportStackEntry(addr); + res->module = internal_strdup(m->name); + res->offset = offset; + char *pos = (char*)internal_strchr(func.data(), '\n'); + if (pos && func[0] != '?') { + res->func = (char*)internal_alloc(MBlockReportStack, pos - func.data() + 1); + internal_memcpy(res->func, func.data(), pos - func.data()); + res->func[pos - func.data()] = 0; + char *pos2 = (char*)internal_strchr(pos, ':'); + if (pos2) { + res->file = (char*)internal_alloc(MBlockReportStack, pos2 - pos - 1 + 1); + internal_memcpy(res->file, pos + 1, pos2 - pos - 1); + res->file[pos2 - pos - 1] = 0; + res->line = atoi(pos2 + 1); + } + } + return res; +} + +ReportStack *SymbolizeDataAddr2Line(uptr addr) { + return 0; +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_sync.cc b/projects/compiler-rt/lib/tsan/rtl/tsan_sync.cc new file mode 100644 index 00000000..f8f3c40f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_sync.cc @@ -0,0 +1,306 @@ +//===-- tsan_sync.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_placement_new.h" +#include "tsan_sync.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" + +namespace __tsan { + +SyncVar::SyncVar(uptr addr, u64 uid) + : mtx(MutexTypeSyncVar, StatMtxSyncVar) + , addr(addr) + , uid(uid) + , owner_tid(kInvalidTid) + , last_lock() + , recursion() + , is_rw() + , is_recursive() + , is_broken() + , is_linker_init() { +} + +SyncTab::Part::Part() + : mtx(MutexTypeSyncTab, StatMtxSyncTab) + , val() { +} + +SyncTab::SyncTab() { +} + +SyncTab::~SyncTab() { + for (int i = 0; i < kPartCount; i++) { + while (tab_[i].val) { + SyncVar *tmp = tab_[i].val; + tab_[i].val = tmp->next; + DestroyAndFree(tmp); + } + } +} + +SyncVar* SyncTab::GetOrCreateAndLock(ThreadState *thr, uptr pc, + uptr addr, bool write_lock) { + return GetAndLock(thr, pc, addr, write_lock, true); +} + +SyncVar* SyncTab::GetIfExistsAndLock(uptr addr, bool write_lock) { + return GetAndLock(0, 0, addr, write_lock, false); +} + +SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) { + StatInc(thr, StatSyncCreated); + void *mem = internal_alloc(MBlockSync, sizeof(SyncVar)); + const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed); + SyncVar *res = new(mem) SyncVar(addr, uid); +#ifndef TSAN_GO + res->creation_stack_id = CurrentStackId(thr, pc); +#endif + return res; +} + +SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc, + uptr addr, bool write_lock, bool create) { +#ifndef TSAN_GO + { // NOLINT + SyncVar *res = GetJavaSync(thr, pc, addr, write_lock, create); + if (res) + return res; + } + + // Here we ask only PrimaryAllocator, because + // SecondaryAllocator::PointerIsMine() is slow and we have fallback on + // the hashmap anyway. + if (PrimaryAllocator::PointerIsMine((void*)addr)) { + MBlock *b = user_mblock(thr, (void*)addr); + CHECK_NE(b, 0); + MBlock::ScopedLock l(b); + SyncVar *res = 0; + for (res = b->ListHead(); res; res = res->next) { + if (res->addr == addr) + break; + } + if (res == 0) { + if (!create) + return 0; + res = Create(thr, pc, addr); + b->ListPush(res); + } + if (write_lock) + res->mtx.Lock(); + else + res->mtx.ReadLock(); + return res; + } +#endif + + Part *p = &tab_[PartIdx(addr)]; + { + ReadLock l(&p->mtx); + for (SyncVar *res = p->val; res; res = res->next) { + if (res->addr == addr) { + if (write_lock) + res->mtx.Lock(); + else + res->mtx.ReadLock(); + return res; + } + } + } + if (!create) + return 0; + { + Lock l(&p->mtx); + SyncVar *res = p->val; + for (; res; res = res->next) { + if (res->addr == addr) + break; + } + if (res == 0) { + res = Create(thr, pc, addr); + res->next = p->val; + p->val = res; + } + if (write_lock) + res->mtx.Lock(); + else + res->mtx.ReadLock(); + return res; + } +} + +SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) { +#ifndef TSAN_GO + { // NOLINT + SyncVar *res = GetAndRemoveJavaSync(thr, pc, addr); + if (res) + return res; + } + if (PrimaryAllocator::PointerIsMine((void*)addr)) { + MBlock *b = user_mblock(thr, (void*)addr); + CHECK_NE(b, 0); + SyncVar *res = 0; + { + MBlock::ScopedLock l(b); + res = b->ListHead(); + if (res) { + if (res->addr == addr) { + if (res->is_linker_init) + return 0; + b->ListPop(); + } else { + SyncVar **prev = &res->next; + res = *prev; + while (res) { + if (res->addr == addr) { + if (res->is_linker_init) + return 0; + *prev = res->next; + break; + } + prev = &res->next; + res = *prev; + } + } + if (res) { + StatInc(thr, StatSyncDestroyed); + res->mtx.Lock(); + res->mtx.Unlock(); + } + } + } + return res; + } +#endif + + Part *p = &tab_[PartIdx(addr)]; + SyncVar *res = 0; + { + Lock l(&p->mtx); + SyncVar **prev = &p->val; + res = *prev; + while (res) { + if (res->addr == addr) { + if (res->is_linker_init) + return 0; + *prev = res->next; + break; + } + prev = &res->next; + res = *prev; + } + } + if (res) { + StatInc(thr, StatSyncDestroyed); + res->mtx.Lock(); + res->mtx.Unlock(); + } + return res; +} + +int SyncTab::PartIdx(uptr addr) { + return (addr >> 3) % kPartCount; +} + +StackTrace::StackTrace() + : n_() + , s_() + , c_() { +} + +StackTrace::StackTrace(uptr *buf, uptr cnt) + : n_() + , s_(buf) + , c_(cnt) { + CHECK_NE(buf, 0); + CHECK_NE(cnt, 0); +} + +StackTrace::~StackTrace() { + Reset(); +} + +void StackTrace::Reset() { + if (s_ && !c_) { + CHECK_NE(n_, 0); + internal_free(s_); + s_ = 0; + } + n_ = 0; +} + +void StackTrace::Init(const uptr *pcs, uptr cnt) { + Reset(); + if (cnt == 0) + return; + if (c_) { + CHECK_NE(s_, 0); + CHECK_LE(cnt, c_); + } else { + s_ = (uptr*)internal_alloc(MBlockStackTrace, cnt * sizeof(s_[0])); + } + n_ = cnt; + internal_memcpy(s_, pcs, cnt * sizeof(s_[0])); +} + +void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) { + Reset(); + n_ = thr->shadow_stack_pos - thr->shadow_stack; + if (n_ + !!toppc == 0) + return; + uptr start = 0; + if (c_) { + CHECK_NE(s_, 0); + if (n_ + !!toppc > c_) { + start = n_ - c_ + !!toppc; + n_ = c_ - !!toppc; + } + } else { + // Cap potentially huge stacks. + if (n_ + !!toppc > kTraceStackSize) { + start = n_ - kTraceStackSize + !!toppc; + n_ = kTraceStackSize - !!toppc; + } + s_ = (uptr*)internal_alloc(MBlockStackTrace, + (n_ + !!toppc) * sizeof(s_[0])); + } + for (uptr i = 0; i < n_; i++) + s_[i] = thr->shadow_stack[start + i]; + if (toppc) { + s_[n_] = toppc; + n_++; + } +} + +void StackTrace::CopyFrom(const StackTrace& other) { + Reset(); + Init(other.Begin(), other.Size()); +} + +bool StackTrace::IsEmpty() const { + return n_ == 0; +} + +uptr StackTrace::Size() const { + return n_; +} + +uptr StackTrace::Get(uptr i) const { + CHECK_LT(i, n_); + return s_[i]; +} + +const uptr *StackTrace::Begin() const { + return s_; +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_sync.h b/projects/compiler-rt/lib/tsan/rtl/tsan_sync.h new file mode 100644 index 00000000..823af543 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_sync.h @@ -0,0 +1,127 @@ +//===-- tsan_sync.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_SYNC_H +#define TSAN_SYNC_H + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "tsan_clock.h" +#include "tsan_defs.h" +#include "tsan_mutex.h" + +namespace __tsan { + +class SlabCache; + +class StackTrace { + public: + StackTrace(); + // Initialized the object in "static mode", + // in this mode it never calls malloc/free but uses the provided buffer. + StackTrace(uptr *buf, uptr cnt); + ~StackTrace(); + void Reset(); + + void Init(const uptr *pcs, uptr cnt); + void ObtainCurrent(ThreadState *thr, uptr toppc); + bool IsEmpty() const; + uptr Size() const; + uptr Get(uptr i) const; + const uptr *Begin() const; + void CopyFrom(const StackTrace& other); + + private: + uptr n_; + uptr *s_; + const uptr c_; + + StackTrace(const StackTrace&); + void operator = (const StackTrace&); +}; + +struct SyncVar { + explicit SyncVar(uptr addr, u64 uid); + + static const int kInvalidTid = -1; + + Mutex mtx; + uptr addr; + const u64 uid; // Globally unique id. + SyncClock clock; + SyncClock read_clock; // Used for rw mutexes only. + u32 creation_stack_id; + int owner_tid; // Set only by exclusive owners. + u64 last_lock; + int recursion; + bool is_rw; + bool is_recursive; + bool is_broken; + bool is_linker_init; + SyncVar *next; // In SyncTab hashtable. + + uptr GetMemoryConsumption(); + u64 GetId() const { + // 47 lsb is addr, then 14 bits is low part of uid, then 3 zero bits. + return GetLsb((u64)addr | (uid << 47), 61); + } + bool CheckId(u64 uid) const { + CHECK_EQ(uid, GetLsb(uid, 14)); + return GetLsb(this->uid, 14) == uid; + } + static uptr SplitId(u64 id, u64 *uid) { + *uid = id >> 47; + return (uptr)GetLsb(id, 47); + } +}; + +class SyncTab { + public: + SyncTab(); + ~SyncTab(); + + SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc, + uptr addr, bool write_lock); + SyncVar* GetIfExistsAndLock(uptr addr, bool write_lock); + + // If the SyncVar does not exist, returns 0. + SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr); + + SyncVar* Create(ThreadState *thr, uptr pc, uptr addr); + + uptr GetMemoryConsumption(uptr *nsync); + + private: + struct Part { + Mutex mtx; + SyncVar *val; + char pad[kCacheLineSize - sizeof(Mutex) - sizeof(SyncVar*)]; // NOLINT + Part(); + }; + + // FIXME: Implement something more sane. + static const int kPartCount = 1009; + Part tab_[kPartCount]; + atomic_uint64_t uid_gen_; + + int PartIdx(uptr addr); + + SyncVar* GetAndLock(ThreadState *thr, uptr pc, + uptr addr, bool write_lock, bool create); + + SyncTab(const SyncTab&); // Not implemented. + void operator = (const SyncTab&); // Not implemented. +}; + +} // namespace __tsan + +#endif // TSAN_SYNC_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_trace.h b/projects/compiler-rt/lib/tsan/rtl/tsan_trace.h new file mode 100644 index 00000000..5ed0356e --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_trace.h @@ -0,0 +1,78 @@ +//===-- tsan_trace.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_TRACE_H +#define TSAN_TRACE_H + +#include "tsan_defs.h" +#include "tsan_mutex.h" +#include "tsan_sync.h" +#include "tsan_mutexset.h" + +namespace __tsan { + +const int kTracePartSizeBits = 14; +const int kTracePartSize = 1 << kTracePartSizeBits; +const int kTraceParts = 4 * 1024 * 1024 / kTracePartSize; +const int kTraceSize = kTracePartSize * kTraceParts; + +// Must fit into 3 bits. +enum EventType { + EventTypeMop, + EventTypeFuncEnter, + EventTypeFuncExit, + EventTypeLock, + EventTypeUnlock, + EventTypeRLock, + EventTypeRUnlock +}; + +// Represents a thread event (from most significant bit): +// u64 typ : 3; // EventType. +// u64 addr : 61; // Associated pc. +typedef u64 Event; + +struct TraceHeader { + StackTrace stack0; // Start stack for the trace. + u64 epoch0; // Start epoch for the trace. + MutexSet mset0; +#ifndef TSAN_GO + uptr stack0buf[kTraceStackSize]; +#endif + + TraceHeader() +#ifndef TSAN_GO + : stack0(stack0buf, kTraceStackSize) +#else + : stack0() +#endif + , epoch0() { + } +}; + +struct Trace { + TraceHeader headers[kTraceParts]; + Mutex mtx; +#ifndef TSAN_GO + // Must be last to catch overflow as paging fault. + // Go shadow stack is dynamically allocated. + uptr shadow_stack[kShadowStackSize]; +#endif + + Trace() + : mtx(MutexTypeTrace, StatMtxTrace) { + } +}; + +} // namespace __tsan + +#endif // TSAN_TRACE_H diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/projects/compiler-rt/lib/tsan/rtl/tsan_update_shadow_word_inl.h new file mode 100644 index 00000000..a11c9bc5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_update_shadow_word_inl.h @@ -0,0 +1,76 @@ +//===-- tsan_update_shadow_word_inl.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Body of the hottest inner loop. +// If we wrap this body into a function, compilers (both gcc and clang) +// produce sligtly less efficient code. +//===----------------------------------------------------------------------===// +do { + StatInc(thr, StatShadowProcessed); + const unsigned kAccessSize = 1 << kAccessSizeLog; + unsigned off = cur.ComputeSearchOffset(); + u64 *sp = &shadow_mem[(idx + off) % kShadowCnt]; + old = LoadShadow(sp); + if (old.IsZero()) { + StatInc(thr, StatShadowZero); + if (store_word) + StoreIfNotYetStored(sp, &store_word); + // The above StoreIfNotYetStored could be done unconditionally + // and it even shows 4% gain on synthetic benchmarks (r4307). + break; + } + // is the memory access equal to the previous? + if (Shadow::Addr0AndSizeAreEqual(cur, old)) { + StatInc(thr, StatShadowSameSize); + // same thread? + if (Shadow::TidsAreEqual(old, cur)) { + StatInc(thr, StatShadowSameThread); + if (OldIsInSameSynchEpoch(old, thr)) { + if (old.IsRWNotWeaker(kAccessIsWrite, kIsAtomic)) { + // found a slot that holds effectively the same info + // (that is, same tid, same sync epoch and same size) + StatInc(thr, StatMopSame); + return; + } + StoreIfNotYetStored(sp, &store_word); + break; + } + if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic)) + StoreIfNotYetStored(sp, &store_word); + break; + } + StatInc(thr, StatShadowAnotherThread); + if (HappensBefore(old, thr)) { + StoreIfNotYetStored(sp, &store_word); + break; + } + if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) + break; + goto RACE; + } + // Do the memory access intersect? + if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { + StatInc(thr, StatShadowIntersect); + if (Shadow::TidsAreEqual(old, cur)) { + StatInc(thr, StatShadowSameThread); + break; + } + StatInc(thr, StatShadowAnotherThread); + if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) + break; + if (HappensBefore(old, thr)) + break; + goto RACE; + } + // The accesses do not intersect. + StatInc(thr, StatShadowNotIntersect); + break; +} while (0); diff --git a/projects/compiler-rt/lib/tsan/rtl/tsan_vector.h b/projects/compiler-rt/lib/tsan/rtl/tsan_vector.h new file mode 100644 index 00000000..fa236b1f --- /dev/null +++ b/projects/compiler-rt/lib/tsan/rtl/tsan_vector.h @@ -0,0 +1,115 @@ +//===-- tsan_vector.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// + +// Low-fat STL-like vector container. + +#ifndef TSAN_VECTOR_H +#define TSAN_VECTOR_H + +#include "tsan_defs.h" +#include "tsan_mman.h" + +namespace __tsan { + +template +class Vector { + public: + explicit Vector(MBlockType typ) + : typ_(typ) + , begin_() + , end_() + , last_() { + } + + ~Vector() { + if (begin_) + internal_free(begin_); + } + + void Reset() { + if (begin_) + internal_free(begin_); + begin_ = 0; + end_ = 0; + last_ = 0; + } + + uptr Size() const { + return end_ - begin_; + } + + T &operator[](uptr i) { + DCHECK_LT(i, end_ - begin_); + return begin_[i]; + } + + const T &operator[](uptr i) const { + DCHECK_LT(i, end_ - begin_); + return begin_[i]; + } + + T *PushBack(T v = T()) { + EnsureSize(Size() + 1); + end_[-1] = v; + return &end_[-1]; + } + + void PopBack() { + DCHECK_GT(end_, begin_); + end_--; + } + + void Resize(uptr size) { + uptr old_size = Size(); + EnsureSize(size); + if (old_size < size) { + for (uptr i = old_size; i < size; i++) + begin_[i] = T(); + } + } + + private: + const MBlockType typ_; + T *begin_; + T *end_; + T *last_; + + void EnsureSize(uptr size) { + if (size <= Size()) + return; + if (size <= (uptr)(last_ - begin_)) { + end_ = begin_ + size; + return; + } + uptr cap0 = last_ - begin_; + uptr cap = 2 * cap0; + if (cap == 0) + cap = 16; + if (cap < size) + cap = size; + T *p = (T*)internal_alloc(typ_, cap * sizeof(T)); + if (cap0) { + internal_memcpy(p, begin_, cap0 * sizeof(T)); + internal_free(begin_); + } + begin_ = p; + end_ = begin_ + size; + last_ = begin_ + cap; + } + + Vector(const Vector&); + void operator=(const Vector&); +}; +} // namespace __tsan + +#endif // #ifndef TSAN_VECTOR_H diff --git a/projects/compiler-rt/lib/tsan/tests/CMakeLists.txt b/projects/compiler-rt/lib/tsan/tests/CMakeLists.txt new file mode 100644 index 00000000..f73a8924 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/CMakeLists.txt @@ -0,0 +1,46 @@ +include_directories(../rtl) + +add_custom_target(TsanUnitTests) +set_target_properties(TsanUnitTests PROPERTIES + FOLDER "TSan unittests") + +set(TSAN_UNITTEST_CFLAGS + ${TSAN_CFLAGS} + ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/tsan/rtl + -DGTEST_HAS_RTTI=0) + +# tsan_compile(obj_list, source, arch, {headers}) +macro(tsan_compile obj_list source arch) + get_filename_component(basename ${source} NAME) + set(output_obj "${basename}.${arch}.o") + get_target_flags_for_arch(${arch} TARGET_CFLAGS) + clang_compile(${output_obj} ${source} + CFLAGS ${TSAN_UNITTEST_CFLAGS} ${TARGET_CFLAGS} + DEPS gtest ${TSAN_RUNTIME_LIBRARIES} ${ARGN}) + list(APPEND ${obj_list} ${output_obj}) +endmacro() + +macro(add_tsan_unittest testname) + # Build unit tests only for 64-bit Linux. + if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64) + parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN}) + set(TEST_OBJECTS) + foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) + tsan_compile(TEST_OBJECTS ${SOURCE} x86_64 ${TEST_HEADERS}) + endforeach() + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(TsanUnitTests ${testname} + OBJECTS ${TEST_OBJECTS} + DEPS ${TSAN_RUNTIME_LIBRARIES} ${TEST_OBJECTS} + LINK_FLAGS ${TARGET_LINK_FLAGS} + -fsanitize=thread + -lstdc++ -lm) + endif() +endmacro() + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + add_subdirectory(rtl) + add_subdirectory(unit) +endif() diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt b/projects/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt new file mode 100644 index 00000000..989566d9 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt @@ -0,0 +1,19 @@ +set(TSAN_RTL_TEST_SOURCES + tsan_bench.cc + tsan_mop.cc + tsan_mutex.cc + tsan_posix.cc + tsan_string.cc + tsan_test.cc + tsan_thread.cc) + +if(UNIX AND NOT APPLE) + list(APPEND TSAN_RTL_TEST_SOURCES tsan_test_util_linux.cc) +endif() + +set(TSAN_RTL_TEST_HEADERS + tsan_test_util.h) + +add_tsan_unittest(TsanRtlTest + SOURCES ${TSAN_RTL_TEST_SOURCES} + HEADERS ${TSAN_RTL_TEST_HEADERS}) diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc new file mode 100644 index 00000000..a3cf22f2 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_bench.cc @@ -0,0 +1,105 @@ +//===-- tsan_bench.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "tsan_interface.h" +#include "tsan_defs.h" +#include "gtest/gtest.h" +#include + +const int kSize = 128; +const int kRepeat = 2*1024*1024; + +void noinstr(void *p) {} + +template +static void Benchmark() { + volatile T data[kSize]; + for (int i = 0; i < kRepeat; i++) { + for (int j = 0; j < kSize; j++) { + __tsan_mop((void*)&data[j]); + data[j]++; + } + } +} + +TEST(DISABLED_BENCH, Mop1) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop1Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop1Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop2) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop2Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop2Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop4) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop4Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop4Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop8) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop8Read) { + Benchmark(); +} + +TEST(DISABLED_BENCH, Mop8Write) { + Benchmark(); +} + +TEST(DISABLED_BENCH, FuncCall) { + for (int i = 0; i < kRepeat; i++) { + for (int j = 0; j < kSize; j++) + __tsan_func_entry((void*)(uintptr_t)j); + for (int j = 0; j < kSize; j++) + __tsan_func_exit(); + } +} + +TEST(DISABLED_BENCH, MutexLocal) { + Mutex m; + ScopedThread().Create(m); + for (int i = 0; i < 50; i++) { + ScopedThread t; + t.Lock(m); + t.Unlock(m); + } + for (int i = 0; i < 16*1024*1024; i++) { + m.Lock(); + m.Unlock(); + } + ScopedThread().Destroy(m); +} diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc new file mode 100644 index 00000000..f2174282 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_mop.cc @@ -0,0 +1,233 @@ +//===-- tsan_mop.cc -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include +#include + +TEST(ThreadSanitizer, SimpleWrite) { + ScopedThread t; + MemLoc l; + t.Write1(l); +} + +TEST(ThreadSanitizer, SimpleWriteWrite) { + ScopedThread t1, t2; + MemLoc l1, l2; + t1.Write1(l1); + t2.Write1(l2); +} + +TEST(ThreadSanitizer, WriteWriteRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Write1(l, true); +} + +TEST(ThreadSanitizer, ReadWriteRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Read1(l); + t2.Write1(l, true); +} + +TEST(ThreadSanitizer, WriteReadRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Read1(l, true); +} + +TEST(ThreadSanitizer, ReadReadNoRace) { + ScopedThread t1, t2; + MemLoc l; + t1.Read1(l); + t2.Read1(l); +} + +TEST(ThreadSanitizer, WriteThenRead) { + MemLoc l; + ScopedThread t1, t2; + t1.Write1(l); + t1.Read1(l); + t2.Read1(l, true); +} + +TEST(ThreadSanitizer, WriteThenLockedRead) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + MemLoc l; + { + ScopedThread t1, t2; + + t1.Write8(l); + + t1.Lock(m); + t1.Read8(l); + t1.Unlock(m); + + t2.Read8(l, true); + } + t0.Destroy(m); +} + +TEST(ThreadSanitizer, LockedWriteThenRead) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + MemLoc l; + { + ScopedThread t1, t2; + + t1.Lock(m); + t1.Write8(l); + t1.Unlock(m); + + t1.Read8(l); + + t2.Read8(l, true); + } + t0.Destroy(m); +} + + +TEST(ThreadSanitizer, RaceWithOffset) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access(l.loc(), true, 8, false); + t2.Access((char*)l.loc() + 4, true, 4, true); + } + { + MemLoc l; + t1.Access(l.loc(), true, 8, false); + t2.Access((char*)l.loc() + 7, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 4, true, 4, false); + t2.Access((char*)l.loc() + 4, true, 2, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 4, true, 4, false); + t2.Access((char*)l.loc() + 6, true, 2, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 3, true, 2, false); + t2.Access((char*)l.loc() + 4, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 1, true, 8, false); + t2.Access((char*)l.loc() + 3, true, 1, true); + } +} + +TEST(ThreadSanitizer, RaceWithOffset2) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access((char*)l.loc(), true, 4, false); + t2.Access((char*)l.loc() + 2, true, 1, true); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 2, true, 1, false); + t2.Access((char*)l.loc(), true, 4, true); + } +} + +TEST(ThreadSanitizer, NoRaceWithOffset) { + ScopedThread t1, t2; + { + MemLoc l; + t1.Access(l.loc(), true, 4, false); + t2.Access((char*)l.loc() + 4, true, 4, false); + } + { + MemLoc l; + t1.Access((char*)l.loc() + 3, true, 2, false); + t2.Access((char*)l.loc() + 1, true, 2, false); + t2.Access((char*)l.loc() + 5, true, 2, false); + } +} + +TEST(ThreadSanitizer, RaceWithDeadThread) { + MemLoc l; + ScopedThread t; + ScopedThread().Write1(l); + t.Write1(l, true); +} + +TEST(ThreadSanitizer, BenignRaceOnVptr) { + void *vptr_storage; + MemLoc vptr(&vptr_storage), val; + vptr_storage = val.loc(); + ScopedThread t1, t2; + t1.VptrUpdate(vptr, val); + t2.Read8(vptr); +} + +TEST(ThreadSanitizer, HarmfulRaceOnVptr) { + void *vptr_storage; + MemLoc vptr(&vptr_storage), val1, val2; + vptr_storage = val1.loc(); + ScopedThread t1, t2; + t1.VptrUpdate(vptr, val2); + t2.Read8(vptr, true); +} + +static void foo() { + volatile int x = 42; + int x2 = x; + (void)x2; +} + +static void bar() { + volatile int x = 43; + int x2 = x; + (void)x2; +} + +TEST(ThreadSanitizer, ReportDeadThread) { + MemLoc l; + ScopedThread t1; + { + ScopedThread t2; + t2.Call(&foo); + t2.Call(&bar); + t2.Write1(l); + } + t1.Write1(l, true); +} + +struct ClassWithStatic { + static int Data[4]; +}; + +int ClassWithStatic::Data[4]; + +static void foobarbaz() {} + +TEST(ThreadSanitizer, ReportRace) { + ScopedThread t1; + MainThread().Access(&ClassWithStatic::Data, true, 4, false); + t1.Call(&foobarbaz); + t1.Access(&ClassWithStatic::Data, true, 2, true); + t1.Return(); +} diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc new file mode 100644 index 00000000..4d9c7796 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_mutex.cc @@ -0,0 +1,221 @@ +//===-- tsan_mutex.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_atomic.h" +#include "tsan_interface.h" +#include "tsan_interface_ann.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include + +namespace __tsan { + +TEST(ThreadSanitizer, BasicMutex) { + ScopedThread t; + Mutex m; + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, BasicSpinMutex) { + ScopedThread t; + Mutex m(Mutex::Spin); + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, BasicRwMutex) { + ScopedThread t; + Mutex m(Mutex::RW); + t.Create(m); + + t.Lock(m); + t.Unlock(m); + + CHECK(t.TryLock(m)); + t.Unlock(m); + + t.Lock(m); + CHECK(!t.TryLock(m)); + t.Unlock(m); + + t.ReadLock(m); + t.ReadUnlock(m); + + CHECK(t.TryReadLock(m)); + t.ReadUnlock(m); + + t.Lock(m); + CHECK(!t.TryReadLock(m)); + t.Unlock(m); + + t.ReadLock(m); + CHECK(!t.TryLock(m)); + t.ReadUnlock(m); + + t.ReadLock(m); + CHECK(t.TryReadLock(m)); + t.ReadUnlock(m); + t.ReadUnlock(m); + + t.Destroy(m); +} + +TEST(ThreadSanitizer, Mutex) { + Mutex m; + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, SpinMutex) { + Mutex m(Mutex::Spin); + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, RwMutex) { + Mutex m(Mutex::RW); + MainThread t0; + t0.Create(m); + + ScopedThread t1, t2, t3; + MemLoc l; + t1.Lock(m); + t1.Write1(l); + t1.Unlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t1.ReadLock(m); + t3.ReadLock(m); + t1.Read1(l); + t3.Read1(l); + t1.ReadUnlock(m); + t3.ReadUnlock(m); + t2.Lock(m); + t2.Write1(l); + t2.Unlock(m); + t2.Destroy(m); +} + +TEST(ThreadSanitizer, StaticMutex) { + // Emulates statically initialized mutex. + Mutex m; + m.StaticInit(); + { + ScopedThread t1, t2; + t1.Lock(m); + t1.Unlock(m); + t2.Lock(m); + t2.Unlock(m); + } + MainThread().Destroy(m); +} + +static void *singleton_thread(void *param) { + atomic_uintptr_t *singleton = (atomic_uintptr_t *)param; + for (int i = 0; i < 4*1024*1024; i++) { + int *val = (int *)atomic_load(singleton, memory_order_acquire); + __tsan_acquire(singleton); + __tsan_read4(val); + CHECK_EQ(*val, 42); + } + return 0; +} + +TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) { + const int kClockSize = 100; + const int kThreadCount = 8; + + // Puff off thread's clock. + for (int i = 0; i < kClockSize; i++) { + ScopedThread t1; + (void)t1; + } + // Create the singleton. + int val = 42; + __tsan_write4(&val); + atomic_uintptr_t singleton; + __tsan_release(&singleton); + atomic_store(&singleton, (uintptr_t)&val, memory_order_release); + // Create reader threads. + pthread_t threads[kThreadCount]; + for (int t = 0; t < kThreadCount; t++) + pthread_create(&threads[t], 0, singleton_thread, &singleton); + for (int t = 0; t < kThreadCount; t++) + pthread_join(threads[t], 0); +} + +TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) { + const int kClockSize = 100; + const int kIters = 16*1024*1024; + + // Puff off thread's clock. + for (int i = 0; i < kClockSize; i++) { + ScopedThread t1; + (void)t1; + } + // Create the stop flag. + atomic_uintptr_t flag; + __tsan_release(&flag); + atomic_store(&flag, 0, memory_order_release); + // Read it a lot. + for (int i = 0; i < kIters; i++) { + uptr v = atomic_load(&flag, memory_order_acquire); + __tsan_acquire(&flag); + CHECK_EQ(v, 0); + } +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc new file mode 100644 index 00000000..0caedd72 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc @@ -0,0 +1,146 @@ +//===-- tsan_posix.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include + +struct thread_key { + pthread_key_t key; + pthread_mutex_t *mtx; + int val; + int *cnt; + thread_key(pthread_key_t key, pthread_mutex_t *mtx, int val, int *cnt) + : key(key) + , mtx(mtx) + , val(val) + , cnt(cnt) { + } +}; + +static void thread_secific_dtor(void *v) { + thread_key *k = (thread_key *)v; + EXPECT_EQ(pthread_mutex_lock(k->mtx), 0); + (*k->cnt)++; + __tsan_write4(&k->cnt); + EXPECT_EQ(pthread_mutex_unlock(k->mtx), 0); + if (k->val == 42) { + delete k; + } else if (k->val == 43 || k->val == 44) { + k->val--; + EXPECT_EQ(pthread_setspecific(k->key, k), 0); + } else { + ASSERT_TRUE(false); + } +} + +static void *dtors_thread(void *p) { + thread_key *k = (thread_key *)p; + EXPECT_EQ(pthread_setspecific(k->key, k), 0); + return 0; +} + +TEST(Posix, ThreadSpecificDtors) { + int cnt = 0; + pthread_key_t key; + EXPECT_EQ(pthread_key_create(&key, thread_secific_dtor), 0); + pthread_mutex_t mtx; + EXPECT_EQ(pthread_mutex_init(&mtx, 0), 0); + pthread_t th[3]; + thread_key *k[3]; + k[0] = new thread_key(key, &mtx, 42, &cnt); + k[1] = new thread_key(key, &mtx, 43, &cnt); + k[2] = new thread_key(key, &mtx, 44, &cnt); + EXPECT_EQ(pthread_create(&th[0], 0, dtors_thread, k[0]), 0); + EXPECT_EQ(pthread_create(&th[1], 0, dtors_thread, k[1]), 0); + EXPECT_EQ(pthread_join(th[0], 0), 0); + EXPECT_EQ(pthread_create(&th[2], 0, dtors_thread, k[2]), 0); + EXPECT_EQ(pthread_join(th[1], 0), 0); + EXPECT_EQ(pthread_join(th[2], 0), 0); + EXPECT_EQ(pthread_key_delete(key), 0); + EXPECT_EQ(6, cnt); +} + +static __thread int local_var; + +static void *local_thread(void *p) { + __tsan_write1(&local_var); + __tsan_write1(&p); + if (p == 0) + return 0; + const int kThreads = 4; + pthread_t th[kThreads]; + for (int i = 0; i < kThreads; i++) + EXPECT_EQ(pthread_create(&th[i], 0, local_thread, + (void*)((long)p - 1)), 0); // NOLINT + for (int i = 0; i < kThreads; i++) + EXPECT_EQ(pthread_join(th[i], 0), 0); + return 0; +} + +TEST(Posix, ThreadLocalAccesses) { + local_thread((void*)2); +} + +struct CondContext { + pthread_mutex_t m; + pthread_cond_t c; + int data; +}; + +static void *cond_thread(void *p) { + CondContext &ctx = *static_cast(p); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + EXPECT_EQ(ctx.data, 0); + ctx.data = 1; + EXPECT_EQ(pthread_cond_signal(&ctx.c), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 2) + EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + ctx.data = 3; + EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + return 0; +} + +TEST(Posix, CondBasic) { + CondContext ctx; + EXPECT_EQ(pthread_mutex_init(&ctx.m, 0), 0); + EXPECT_EQ(pthread_cond_init(&ctx.c, 0), 0); + ctx.data = 0; + pthread_t th; + EXPECT_EQ(pthread_create(&th, 0, cond_thread, &ctx), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 1) + EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); + ctx.data = 2; + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + EXPECT_EQ(pthread_cond_broadcast(&ctx.c), 0); + + EXPECT_EQ(pthread_mutex_lock(&ctx.m), 0); + while (ctx.data != 3) + EXPECT_EQ(pthread_cond_wait(&ctx.c, &ctx.m), 0); + EXPECT_EQ(pthread_mutex_unlock(&ctx.m), 0); + + EXPECT_EQ(pthread_join(th, 0), 0); + EXPECT_EQ(pthread_cond_destroy(&ctx.c), 0); + EXPECT_EQ(pthread_mutex_destroy(&ctx.m), 0); +} diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc new file mode 100644 index 00000000..c402f7cb --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc @@ -0,0 +1,86 @@ +//===-- tsan_string.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "gtest/gtest.h" +#include + +namespace __tsan { + +TEST(ThreadSanitizer, Memcpy) { + char data0[7] = {1, 2, 3, 4, 5, 6, 7}; + char data[7] = {42, 42, 42, 42, 42, 42, 42}; + MainThread().Memcpy(data+1, data0+1, 5); + EXPECT_EQ(data[0], 42); + EXPECT_EQ(data[1], 2); + EXPECT_EQ(data[2], 3); + EXPECT_EQ(data[3], 4); + EXPECT_EQ(data[4], 5); + EXPECT_EQ(data[5], 6); + EXPECT_EQ(data[6], 42); + MainThread().Memset(data+1, 13, 5); + EXPECT_EQ(data[0], 42); + EXPECT_EQ(data[1], 13); + EXPECT_EQ(data[2], 13); + EXPECT_EQ(data[3], 13); + EXPECT_EQ(data[4], 13); + EXPECT_EQ(data[5], 13); + EXPECT_EQ(data[6], 42); +} + +TEST(ThreadSanitizer, MemcpyRace1) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data, data2, 10, true); +} + +// The test fails with TSAN_SHADOW_COUNT=2, +// because the old racy access is evicted. +#if defined(TSAN_SHADOW_COUNT) && TSAN_SHADOW_COUNT >= 4 +TEST(ThreadSanitizer, MemcpyRace2) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data+5, data1, 1); + t2.Memcpy(data+3, data2, 4, true); +} +#endif + +TEST(ThreadSanitizer, MemcpyRace3) { + char *data = new char[10]; + char *data1 = new char[10]; + char *data2 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data1, data2, 10, true); +} + +TEST(ThreadSanitizer, MemcpyStack) { + char *data = new char[10]; + char *data1 = new char[10]; + ScopedThread t1, t2; + t1.Memcpy(data, data1, 10); + t2.Memcpy(data, data1, 10, true); +} + +TEST(ThreadSanitizer, MemsetRace1) { + char *data = new char[10]; + ScopedThread t1, t2; + t1.Memset(data, 1, 10); + t2.Memset(data, 2, 10, true); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc new file mode 100644 index 00000000..2184284d --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc @@ -0,0 +1,50 @@ +//===-- tsan_test.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "gtest/gtest.h" + +static void foo() {} +static void bar() {} + +TEST(ThreadSanitizer, FuncCall) { + ScopedThread t1, t2; + MemLoc l; + t1.Write1(l); + t2.Call(foo); + t2.Call(bar); + t2.Write1(l, true); + t2.Return(); + t2.Return(); +} + +// We use this function instead of main, as ISO C++ forbids taking the address +// of main, which we need to pass inside __tsan_func_entry. +int run_tests(int argc, char **argv) { + TestMutexBeforeInit(); // Mutexes must be usable before __tsan_init(); + __tsan_init(); + __tsan_func_entry(__builtin_return_address(0)); + __tsan_func_entry((void*)((intptr_t)&run_tests + 1)); + + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + int res = RUN_ALL_TESTS(); + + __tsan_func_exit(); + __tsan_func_exit(); + return res; +} + +int main(int argc, char **argv) { + return run_tests(argc, argv); +} diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test_util.h b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test_util.h new file mode 100644 index 00000000..483a564c --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test_util.h @@ -0,0 +1,122 @@ +//===-- tsan_test_util.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Test utils. +//===----------------------------------------------------------------------===// +#ifndef TSAN_TEST_UTIL_H +#define TSAN_TEST_UTIL_H + +void TestMutexBeforeInit(); + +// A location of memory on which a race may be detected. +class MemLoc { + public: + explicit MemLoc(int offset_from_aligned = 0); + explicit MemLoc(void *const real_addr) : loc_(real_addr) { } + ~MemLoc(); + void *loc() const { return loc_; } + private: + void *const loc_; + MemLoc(const MemLoc&); + void operator = (const MemLoc&); +}; + +class Mutex { + public: + enum Type { Normal, Spin, RW }; + + explicit Mutex(Type type = Normal); + ~Mutex(); + + void Init(); + void StaticInit(); // Emulates static initalization (tsan invisible). + void Destroy(); + void Lock(); + bool TryLock(); + void Unlock(); + void ReadLock(); + bool TryReadLock(); + void ReadUnlock(); + + private: + // Placeholder for pthread_mutex_t, CRITICAL_SECTION or whatever. + void *mtx_[128]; + bool alive_; + const Type type_; + + Mutex(const Mutex&); + void operator = (const Mutex&); +}; + +// A thread is started in CTOR and joined in DTOR. +class ScopedThread { + public: + explicit ScopedThread(bool detached = false, bool main = false); + ~ScopedThread(); + void Detach(); + + void Access(void *addr, bool is_write, int size, bool expect_race); + void Read(const MemLoc &ml, int size, bool expect_race = false) { + Access(ml.loc(), false, size, expect_race); + } + void Write(const MemLoc &ml, int size, bool expect_race = false) { + Access(ml.loc(), true, size, expect_race); + } + void Read1(const MemLoc &ml, bool expect_race = false) { + Read(ml, 1, expect_race); } + void Read2(const MemLoc &ml, bool expect_race = false) { + Read(ml, 2, expect_race); } + void Read4(const MemLoc &ml, bool expect_race = false) { + Read(ml, 4, expect_race); } + void Read8(const MemLoc &ml, bool expect_race = false) { + Read(ml, 8, expect_race); } + void Write1(const MemLoc &ml, bool expect_race = false) { + Write(ml, 1, expect_race); } + void Write2(const MemLoc &ml, bool expect_race = false) { + Write(ml, 2, expect_race); } + void Write4(const MemLoc &ml, bool expect_race = false) { + Write(ml, 4, expect_race); } + void Write8(const MemLoc &ml, bool expect_race = false) { + Write(ml, 8, expect_race); } + + void VptrUpdate(const MemLoc &vptr, const MemLoc &new_val, + bool expect_race = false); + + void Call(void(*pc)()); + void Return(); + + void Create(const Mutex &m); + void Destroy(const Mutex &m); + void Lock(const Mutex &m); + bool TryLock(const Mutex &m); + void Unlock(const Mutex &m); + void ReadLock(const Mutex &m); + bool TryReadLock(const Mutex &m); + void ReadUnlock(const Mutex &m); + + void Memcpy(void *dst, const void *src, int size, bool expect_race = false); + void Memset(void *dst, int val, int size, bool expect_race = false); + + private: + struct Impl; + Impl *impl_; + ScopedThread(const ScopedThread&); // Not implemented. + void operator = (const ScopedThread&); // Not implemented. +}; + +class MainThread : public ScopedThread { + public: + MainThread() + : ScopedThread(false, true) { + } +}; + +#endif // #ifndef TSAN_TEST_UTIL_H diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc new file mode 100644 index 00000000..a2601486 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc @@ -0,0 +1,465 @@ + +//===-- tsan_test_util_linux.cc -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Test utils, linux implementation. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_atomic.h" +#include "tsan_interface.h" +#include "tsan_test_util.h" +#include "tsan_report.h" + +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace __tsan; // NOLINT + +static __thread bool expect_report; +static __thread bool expect_report_reported; +static __thread ReportType expect_report_type; + +extern "C" void *__interceptor_memcpy(void*, const void*, uptr); +extern "C" void *__interceptor_memset(void*, int, uptr); + +static void *BeforeInitThread(void *param) { + (void)param; + return 0; +} + +static void AtExit() { +} + +void TestMutexBeforeInit() { + // Mutexes must be usable before __tsan_init(); + pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&mtx); + pthread_mutex_unlock(&mtx); + pthread_mutex_destroy(&mtx); + pthread_t thr; + pthread_create(&thr, 0, BeforeInitThread, 0); + pthread_join(thr, 0); + atexit(AtExit); +} + +namespace __tsan { +bool OnReport(const ReportDesc *rep, bool suppressed) { + if (expect_report) { + if (rep->typ != expect_report_type) { + printf("Expected report of type %d, got type %d\n", + (int)expect_report_type, (int)rep->typ); + EXPECT_FALSE("Wrong report type"); + return false; + } + } else { + EXPECT_FALSE("Unexpected report"); + return false; + } + expect_report_reported = true; + return true; +} +} // namespace __tsan + +static void* allocate_addr(int size, int offset_from_aligned = 0) { + static uintptr_t foo; + static atomic_uintptr_t uniq = {(uintptr_t)&foo}; // Some real address. + const int kAlign = 16; + CHECK(offset_from_aligned < kAlign); + size = (size + 2 * kAlign) & ~(kAlign - 1); + uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed); + return (void*)(addr + offset_from_aligned); +} + +MemLoc::MemLoc(int offset_from_aligned) + : loc_(allocate_addr(16, offset_from_aligned)) { +} + +MemLoc::~MemLoc() { +} + +Mutex::Mutex(Type type) + : alive_() + , type_(type) { +} + +Mutex::~Mutex() { + CHECK(!alive_); +} + +void Mutex::Init() { + CHECK(!alive_); + alive_ = true; + if (type_ == Normal) + CHECK_EQ(pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0); + else + CHECK(0); +} + +void Mutex::StaticInit() { + CHECK(!alive_); + CHECK(type_ == Normal); + alive_ = true; + pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER; + memcpy(mtx_, &tmp, sizeof(tmp)); +} + +void Mutex::Destroy() { + CHECK(alive_); + alive_ = false; + if (type_ == Normal) + CHECK_EQ(pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0); +} + +void Mutex::Lock() { + CHECK(alive_); + if (type_ == Normal) + CHECK_EQ(pthread_mutex_lock((pthread_mutex_t*)mtx_), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0); +} + +bool Mutex::TryLock() { + CHECK(alive_); + if (type_ == Normal) + return pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0; + else if (type_ == Spin) + return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0; + else if (type_ == RW) + return pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0; + return false; +} + +void Mutex::Unlock() { + CHECK(alive_); + if (type_ == Normal) + CHECK_EQ(pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0); + else if (type_ == Spin) + CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0); + else if (type_ == RW) + CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); +} + +void Mutex::ReadLock() { + CHECK(alive_); + CHECK(type_ == RW); + CHECK_EQ(pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0); +} + +bool Mutex::TryReadLock() { + CHECK(alive_); + CHECK(type_ == RW); + return pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) == 0; +} + +void Mutex::ReadUnlock() { + CHECK(alive_); + CHECK(type_ == RW); + CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0); +} + +struct Event { + enum Type { + SHUTDOWN, + READ, + WRITE, + VPTR_UPDATE, + CALL, + RETURN, + MUTEX_CREATE, + MUTEX_DESTROY, + MUTEX_LOCK, + MUTEX_TRYLOCK, + MUTEX_UNLOCK, + MUTEX_READLOCK, + MUTEX_TRYREADLOCK, + MUTEX_READUNLOCK, + MEMCPY, + MEMSET + }; + Type type; + void *ptr; + uptr arg; + uptr arg2; + bool res; + bool expect_report; + ReportType report_type; + + Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0) + : type(type) + , ptr(const_cast(ptr)) + , arg(arg) + , arg2(arg2) + , res() + , expect_report() + , report_type() { + } + + void ExpectReport(ReportType type) { + expect_report = true; + report_type = type; + } +}; + +struct ScopedThread::Impl { + pthread_t thread; + bool main; + bool detached; + atomic_uintptr_t event; // Event* + + static void *ScopedThreadCallback(void *arg); + void send(Event *ev); + void HandleEvent(Event *ev); +}; + +void ScopedThread::Impl::HandleEvent(Event *ev) { + CHECK_EQ(expect_report, false); + expect_report = ev->expect_report; + expect_report_reported = false; + expect_report_type = ev->report_type; + switch (ev->type) { + case Event::READ: + case Event::WRITE: { + void (*tsan_mop)(void *addr) = 0; + if (ev->type == Event::READ) { + switch (ev->arg /*size*/) { + case 1: tsan_mop = __tsan_read1; break; + case 2: tsan_mop = __tsan_read2; break; + case 4: tsan_mop = __tsan_read4; break; + case 8: tsan_mop = __tsan_read8; break; + case 16: tsan_mop = __tsan_read16; break; + } + } else { + switch (ev->arg /*size*/) { + case 1: tsan_mop = __tsan_write1; break; + case 2: tsan_mop = __tsan_write2; break; + case 4: tsan_mop = __tsan_write4; break; + case 8: tsan_mop = __tsan_write8; break; + case 16: tsan_mop = __tsan_write16; break; + } + } + CHECK_NE(tsan_mop, 0); + errno = ECHRNG; + tsan_mop(ev->ptr); + CHECK_EQ(errno, ECHRNG); // In no case must errno be changed. + break; + } + case Event::VPTR_UPDATE: + __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg); + break; + case Event::CALL: + __tsan_func_entry((void*)((uptr)ev->ptr)); + break; + case Event::RETURN: + __tsan_func_exit(); + break; + case Event::MUTEX_CREATE: + static_cast(ev->ptr)->Init(); + break; + case Event::MUTEX_DESTROY: + static_cast(ev->ptr)->Destroy(); + break; + case Event::MUTEX_LOCK: + static_cast(ev->ptr)->Lock(); + break; + case Event::MUTEX_TRYLOCK: + ev->res = static_cast(ev->ptr)->TryLock(); + break; + case Event::MUTEX_UNLOCK: + static_cast(ev->ptr)->Unlock(); + break; + case Event::MUTEX_READLOCK: + static_cast(ev->ptr)->ReadLock(); + break; + case Event::MUTEX_TRYREADLOCK: + ev->res = static_cast(ev->ptr)->TryReadLock(); + break; + case Event::MUTEX_READUNLOCK: + static_cast(ev->ptr)->ReadUnlock(); + break; + case Event::MEMCPY: + __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2); + break; + case Event::MEMSET: + __interceptor_memset(ev->ptr, ev->arg, ev->arg2); + break; + default: CHECK(0); + } + if (expect_report && !expect_report_reported) { + printf("Missed expected report of type %d\n", (int)ev->report_type); + EXPECT_FALSE("Missed expected race"); + } + expect_report = false; +} + +void *ScopedThread::Impl::ScopedThreadCallback(void *arg) { + __tsan_func_entry(__builtin_return_address(0)); + Impl *impl = (Impl*)arg; + for (;;) { + Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire); + if (ev == 0) { + pthread_yield(); + continue; + } + if (ev->type == Event::SHUTDOWN) { + atomic_store(&impl->event, 0, memory_order_release); + break; + } + impl->HandleEvent(ev); + atomic_store(&impl->event, 0, memory_order_release); + } + __tsan_func_exit(); + return 0; +} + +void ScopedThread::Impl::send(Event *e) { + if (main) { + HandleEvent(e); + } else { + CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0); + atomic_store(&event, (uintptr_t)e, memory_order_release); + while (atomic_load(&event, memory_order_acquire) != 0) + pthread_yield(); + } +} + +ScopedThread::ScopedThread(bool detached, bool main) { + impl_ = new Impl; + impl_->main = main; + impl_->detached = detached; + atomic_store(&impl_->event, 0, memory_order_relaxed); + if (!main) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, detached); + pthread_attr_setstacksize(&attr, 64*1024); + pthread_create(&impl_->thread, &attr, + ScopedThread::Impl::ScopedThreadCallback, impl_); + } +} + +ScopedThread::~ScopedThread() { + if (!impl_->main) { + Event event(Event::SHUTDOWN); + impl_->send(&event); + if (!impl_->detached) + pthread_join(impl_->thread, 0); + } + delete impl_; +} + +void ScopedThread::Detach() { + CHECK(!impl_->main); + CHECK(!impl_->detached); + impl_->detached = true; + pthread_detach(impl_->thread); +} + +void ScopedThread::Access(void *addr, bool is_write, + int size, bool expect_race) { + Event event(is_write ? Event::WRITE : Event::READ, addr, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::VptrUpdate(const MemLoc &vptr, + const MemLoc &new_val, + bool expect_race) { + Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc()); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::Call(void(*pc)()) { + Event event(Event::CALL, (void*)((uintptr_t)pc)); + impl_->send(&event); +} + +void ScopedThread::Return() { + Event event(Event::RETURN); + impl_->send(&event); +} + +void ScopedThread::Create(const Mutex &m) { + Event event(Event::MUTEX_CREATE, &m); + impl_->send(&event); +} + +void ScopedThread::Destroy(const Mutex &m) { + Event event(Event::MUTEX_DESTROY, &m); + impl_->send(&event); +} + +void ScopedThread::Lock(const Mutex &m) { + Event event(Event::MUTEX_LOCK, &m); + impl_->send(&event); +} + +bool ScopedThread::TryLock(const Mutex &m) { + Event event(Event::MUTEX_TRYLOCK, &m); + impl_->send(&event); + return event.res; +} + +void ScopedThread::Unlock(const Mutex &m) { + Event event(Event::MUTEX_UNLOCK, &m); + impl_->send(&event); +} + +void ScopedThread::ReadLock(const Mutex &m) { + Event event(Event::MUTEX_READLOCK, &m); + impl_->send(&event); +} + +bool ScopedThread::TryReadLock(const Mutex &m) { + Event event(Event::MUTEX_TRYREADLOCK, &m); + impl_->send(&event); + return event.res; +} + +void ScopedThread::ReadUnlock(const Mutex &m) { + Event event(Event::MUTEX_READUNLOCK, &m); + impl_->send(&event); +} + +void ScopedThread::Memcpy(void *dst, const void *src, int size, + bool expect_race) { + Event event(Event::MEMCPY, dst, (uptr)src, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} + +void ScopedThread::Memset(void *dst, int val, int size, + bool expect_race) { + Event event(Event::MEMSET, dst, val, size); + if (expect_race) + event.ExpectReport(ReportTypeRace); + impl_->send(&event); +} diff --git a/projects/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc new file mode 100644 index 00000000..5646415a --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/rtl/tsan_thread.cc @@ -0,0 +1,59 @@ +//===-- tsan_thread.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_test_util.h" +#include "gtest/gtest.h" + +TEST(ThreadSanitizer, ThreadSync) { + MainThread t0; + MemLoc l; + t0.Write1(l); + { + ScopedThread t1; + t1.Write1(l); + } + t0.Write1(l); +} + +TEST(ThreadSanitizer, ThreadDetach1) { + ScopedThread t1(true); + MemLoc l; + t1.Write1(l); +} + +TEST(ThreadSanitizer, ThreadDetach2) { + ScopedThread t1; + MemLoc l; + t1.Write1(l); + t1.Detach(); +} + +static void *thread_alot_func(void *arg) { + (void)arg; + int usleep(unsigned); + usleep(50); + return 0; +} + +TEST(DISABLED_SLOW_ThreadSanitizer, ThreadALot) { + const int kThreads = 70000; + const int kAlive = 1000; + pthread_t threads[kAlive] = {}; + for (int i = 0; i < kThreads; i++) { + if (threads[i % kAlive]) + pthread_join(threads[i % kAlive], 0); + pthread_create(&threads[i % kAlive], 0, thread_alot_func, 0); + } + for (int i = 0; i < kAlive; i++) { + pthread_join(threads[i], 0); + } +} diff --git a/projects/compiler-rt/lib/tsan/tests/unit/CMakeLists.txt b/projects/compiler-rt/lib/tsan/tests/unit/CMakeLists.txt new file mode 100644 index 00000000..6898f641 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/CMakeLists.txt @@ -0,0 +1,13 @@ +set(TSAN_UNIT_TEST_SOURCES + tsan_clock_test.cc + tsan_flags_test.cc + tsan_mman_test.cc + tsan_mutex_test.cc + tsan_shadow_test.cc + tsan_stack_test.cc + tsan_sync_test.cc + tsan_unit_test_main.cc + tsan_vector_test.cc) + +add_tsan_unittest(TsanUnitTest + SOURCES ${TSAN_UNIT_TEST_SOURCES}) diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc new file mode 100644 index 00000000..fe886e10 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc @@ -0,0 +1,123 @@ +//===-- tsan_clock_test.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_clock.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Clock, VectorBasic) { + ScopedInRtl in_rtl; + ThreadClock clk; + CHECK_EQ(clk.size(), 0); + clk.tick(0); + CHECK_EQ(clk.size(), 1); + CHECK_EQ(clk.get(0), 1); + clk.tick(3); + CHECK_EQ(clk.size(), 4); + CHECK_EQ(clk.get(0), 1); + CHECK_EQ(clk.get(1), 0); + CHECK_EQ(clk.get(2), 0); + CHECK_EQ(clk.get(3), 1); + clk.tick(3); + CHECK_EQ(clk.get(3), 2); +} + +TEST(Clock, ChunkedBasic) { + ScopedInRtl in_rtl; + ThreadClock vector; + SyncClock chunked; + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); + vector.acquire(&chunked); + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); + vector.release(&chunked); + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); + vector.acq_rel(&chunked); + CHECK_EQ(vector.size(), 0); + CHECK_EQ(chunked.size(), 0); +} + +TEST(Clock, AcquireRelease) { + ScopedInRtl in_rtl; + ThreadClock vector1; + vector1.tick(100); + SyncClock chunked; + vector1.release(&chunked); + CHECK_EQ(chunked.size(), 101); + ThreadClock vector2; + vector2.acquire(&chunked); + CHECK_EQ(vector2.size(), 101); + CHECK_EQ(vector2.get(0), 0); + CHECK_EQ(vector2.get(1), 0); + CHECK_EQ(vector2.get(99), 0); + CHECK_EQ(vector2.get(100), 1); +} + +TEST(Clock, ManyThreads) { + ScopedInRtl in_rtl; + SyncClock chunked; + for (int i = 0; i < 100; i++) { + ThreadClock vector; + vector.tick(i); + vector.release(&chunked); + CHECK_EQ(chunked.size(), i + 1); + vector.acquire(&chunked); + CHECK_EQ(vector.size(), i + 1); + } + ThreadClock vector; + vector.acquire(&chunked); + CHECK_EQ(vector.size(), 100); + for (int i = 0; i < 100; i++) + CHECK_EQ(vector.get(i), 1); +} + +TEST(Clock, DifferentSizes) { + ScopedInRtl in_rtl; + { + ThreadClock vector1; + vector1.tick(10); + ThreadClock vector2; + vector2.tick(20); + { + SyncClock chunked; + vector1.release(&chunked); + CHECK_EQ(chunked.size(), 11); + vector2.release(&chunked); + CHECK_EQ(chunked.size(), 21); + } + { + SyncClock chunked; + vector2.release(&chunked); + CHECK_EQ(chunked.size(), 21); + vector1.release(&chunked); + CHECK_EQ(chunked.size(), 21); + } + { + SyncClock chunked; + vector1.release(&chunked); + vector2.acquire(&chunked); + CHECK_EQ(vector2.size(), 21); + } + { + SyncClock chunked; + vector2.release(&chunked); + vector1.acquire(&chunked); + CHECK_EQ(vector1.size(), 21); + } + } +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc new file mode 100644 index 00000000..ffb9c55b --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_flags_test.cc @@ -0,0 +1,38 @@ +//===-- tsan_flags_test.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_flags.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Flags, Basic) { + ScopedInRtl in_rtl; + // At least should not crash. + Flags f; + InitializeFlags(&f, 0); + InitializeFlags(&f, ""); +} + +TEST(Flags, DefaultValues) { + ScopedInRtl in_rtl; + Flags f; + + f.enable_annotations = false; + f.exitcode = -11; + InitializeFlags(&f, ""); + EXPECT_EQ(66, f.exitcode); + EXPECT_EQ(true, f.enable_annotations); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc new file mode 100644 index 00000000..e1ad7ac5 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc @@ -0,0 +1,173 @@ +//===-- tsan_mman_test.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include +#include "tsan_mman.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +extern "C" { +uptr __tsan_get_current_allocated_bytes(); +uptr __tsan_get_heap_size(); +uptr __tsan_get_free_bytes(); +uptr __tsan_get_unmapped_bytes(); +uptr __tsan_get_estimated_allocated_size(uptr size); +bool __tsan_get_ownership(void *p); +uptr __tsan_get_allocated_size(void *p); +} + +namespace __tsan { + +TEST(Mman, Internal) { + ScopedInRtl in_rtl; + char *p = (char*)internal_alloc(MBlockScopedBuf, 10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + for (int i = 0; i < 10; i++) { + p[i] = 42; + } + for (int i = 0; i < 20; i++) { + ((char*)p2)[i] = 42; + } + internal_free(p); + internal_free(p2); +} + +TEST(Mman, User) { + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + uptr pc = 0; + char *p = (char*)user_alloc(thr, pc, 10); + EXPECT_NE(p, (char*)0); + char *p2 = (char*)user_alloc(thr, pc, 20); + EXPECT_NE(p2, (char*)0); + EXPECT_NE(p2, p); + MBlock *b = user_mblock(thr, p); + EXPECT_NE(b, (MBlock*)0); + EXPECT_EQ(b->Size(), (uptr)10); + MBlock *b2 = user_mblock(thr, p2); + EXPECT_NE(b2, (MBlock*)0); + EXPECT_EQ(b2->Size(), (uptr)20); + for (int i = 0; i < 10; i++) { + p[i] = 42; + EXPECT_EQ(b, user_mblock(thr, p + i)); + } + for (int i = 0; i < 20; i++) { + ((char*)p2)[i] = 42; + EXPECT_EQ(b2, user_mblock(thr, p2 + i)); + } + user_free(thr, pc, p); + user_free(thr, pc, p2); +} + +TEST(Mman, UserRealloc) { + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + uptr pc = 0; + { + void *p = user_realloc(thr, pc, 0, 0); + // Strictly saying this is incorrect, realloc(NULL, N) is equivalent to + // malloc(N), thus must return non-NULL pointer. + EXPECT_EQ(p, (void*)0); + } + { + void *p = user_realloc(thr, pc, 0, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + user_free(thr, pc, p); + } + { + void *p = user_alloc(thr, pc, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + void *p2 = user_realloc(thr, pc, p, 0); + EXPECT_EQ(p2, (void*)0); + } + { + void *p = user_realloc(thr, pc, 0, 100); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 100); + void *p2 = user_realloc(thr, pc, p, 10000); + EXPECT_NE(p2, (void*)0); + for (int i = 0; i < 100; i++) + EXPECT_EQ(((char*)p2)[i], (char)0xde); + memset(p2, 0xde, 10000); + user_free(thr, pc, p2); + } + { + void *p = user_realloc(thr, pc, 0, 10000); + EXPECT_NE(p, (void*)0); + memset(p, 0xde, 10000); + void *p2 = user_realloc(thr, pc, p, 10); + EXPECT_NE(p2, (void*)0); + for (int i = 0; i < 10; i++) + EXPECT_EQ(((char*)p2)[i], (char)0xde); + user_free(thr, pc, p2); + } +} + +TEST(Mman, UsableSize) { + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + uptr pc = 0; + char *p = (char*)user_alloc(thr, pc, 10); + char *p2 = (char*)user_alloc(thr, pc, 20); + EXPECT_EQ(0U, user_alloc_usable_size(thr, pc, NULL)); + EXPECT_EQ(10U, user_alloc_usable_size(thr, pc, p)); + EXPECT_EQ(20U, user_alloc_usable_size(thr, pc, p2)); + user_free(thr, pc, p); + user_free(thr, pc, p2); +} + +TEST(Mman, Stats) { + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + + uptr alloc0 = __tsan_get_current_allocated_bytes(); + uptr heap0 = __tsan_get_heap_size(); + uptr free0 = __tsan_get_free_bytes(); + uptr unmapped0 = __tsan_get_unmapped_bytes(); + + EXPECT_EQ(__tsan_get_estimated_allocated_size(10), (uptr)10); + EXPECT_EQ(__tsan_get_estimated_allocated_size(20), (uptr)20); + EXPECT_EQ(__tsan_get_estimated_allocated_size(100), (uptr)100); + + char *p = (char*)user_alloc(thr, 0, 10); + EXPECT_EQ(__tsan_get_ownership(p), true); + EXPECT_EQ(__tsan_get_allocated_size(p), (uptr)10); + + EXPECT_EQ(__tsan_get_current_allocated_bytes(), alloc0 + 16); + EXPECT_GE(__tsan_get_heap_size(), heap0); + EXPECT_EQ(__tsan_get_free_bytes(), free0); + EXPECT_EQ(__tsan_get_unmapped_bytes(), unmapped0); + + user_free(thr, 0, p); + + EXPECT_EQ(__tsan_get_current_allocated_bytes(), alloc0); + EXPECT_GE(__tsan_get_heap_size(), heap0); + EXPECT_EQ(__tsan_get_free_bytes(), free0); + EXPECT_EQ(__tsan_get_unmapped_bytes(), unmapped0); +} + +TEST(Mman, CallocOverflow) { + size_t kArraySize = 4096; + volatile size_t kMaxSizeT = std::numeric_limits::max(); + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + volatile void *p = NULL; + EXPECT_DEATH(p = calloc(kArraySize, kArraySize2), + "allocator is terminating the process instead of returning 0"); + EXPECT_EQ(0L, p); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc new file mode 100644 index 00000000..c39841dd --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc @@ -0,0 +1,126 @@ +//===-- tsan_mutex_test.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "tsan_mutex.h" +#include "gtest/gtest.h" + +namespace __tsan { + +template +class TestData { + public: + explicit TestData(MutexType *mtx) + : mtx_(mtx) { + for (int i = 0; i < kSize; i++) + data_[i] = 0; + } + + void Write() { + Lock l(mtx_); + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + data_[i]++; + } + } + + void Read() { + ReadLock l(mtx_); + T v0 = data_[0]; + for (int i = 0; i < kSize; i++) { + CHECK_EQ(data_[i], v0); + } + } + + void Backoff() { + volatile T data[kSize] = {}; + for (int i = 0; i < kSize; i++) { + data[i]++; + CHECK_EQ(data[i], 1); + } + } + + private: + typedef GenericScopedLock Lock; + static const int kSize = 64; + typedef u64 T; + MutexType *mtx_; + char pad_[kCacheLineSize]; + T data_[kSize]; +}; + +const int kThreads = 8; +const int kWriteRate = 1024; +#if TSAN_DEBUG +const int kIters = 16*1024; +#else +const int kIters = 64*1024; +#endif + +template +static void *write_mutex_thread(void *param) { + TestData *data = (TestData*)param; + for (int i = 0; i < kIters; i++) { + data->Write(); + data->Backoff(); + } + return 0; +} + +template +static void *read_mutex_thread(void *param) { + TestData *data = (TestData*)param; + for (int i = 0; i < kIters; i++) { + if ((i % kWriteRate) == 0) + data->Write(); + else + data->Read(); + data->Backoff(); + } + return 0; +} + +TEST(Mutex, Write) { + Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, write_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +TEST(Mutex, ReadWrite) { + Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, read_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +TEST(Mutex, SpinWrite) { + SpinMutex mtx; + TestData data(&mtx); + pthread_t threads[kThreads]; + for (int i = 0; i < kThreads; i++) + pthread_create(&threads[i], 0, write_mutex_thread, &data); + for (int i = 0; i < kThreads; i++) + pthread_join(threads[i], 0); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc new file mode 100644 index 00000000..335a7748 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_mutexset_test.cc @@ -0,0 +1,127 @@ +//===-- tsan_mutexset_test.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_mutexset.h" +#include "gtest/gtest.h" + +namespace __tsan { + +static void Expect(const MutexSet &mset, uptr i, u64 id, bool write, u64 epoch, + int count) { + MutexSet::Desc d = mset.Get(i); + EXPECT_EQ(id, d.id); + EXPECT_EQ(write, d.write); + EXPECT_EQ(epoch, d.epoch); + EXPECT_EQ(count, d.count); +} + +TEST(MutexSet, Basic) { + MutexSet mset; + EXPECT_EQ(mset.Size(), (uptr)0); + + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 1); + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); + + mset.Add(3, true, 4); + mset.Add(5, false, 6); + EXPECT_EQ(mset.Size(), (uptr)2); + Expect(mset, 0, 3, true, 4, 1); + Expect(mset, 1, 5, false, 6, 1); + mset.Del(3, true); + EXPECT_EQ(mset.Size(), (uptr)1); + mset.Del(5, false); + EXPECT_EQ(mset.Size(), (uptr)0); +} + +TEST(MutexSet, DoubleAdd) { + MutexSet mset; + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 1); + + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 2); + + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 1, true, 2, 1); + + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); +} + +TEST(MutexSet, DoubleDel) { + MutexSet mset; + mset.Add(1, true, 2); + EXPECT_EQ(mset.Size(), (uptr)1); + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); + mset.Del(1, true); + EXPECT_EQ(mset.Size(), (uptr)0); +} + +TEST(MutexSet, Remove) { + MutexSet mset; + mset.Add(1, true, 2); + mset.Add(1, true, 2); + mset.Add(3, true, 4); + mset.Add(3, true, 4); + EXPECT_EQ(mset.Size(), (uptr)2); + + mset.Remove(1); + EXPECT_EQ(mset.Size(), (uptr)1); + Expect(mset, 0, 3, true, 4, 2); +} + +TEST(MutexSet, Full) { + MutexSet mset; + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + mset.Add(i, true, i + 1); + } + EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + Expect(mset, i, i, true, i + 1, 1); + } + + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + mset.Add(i, true, i + 1); + } + EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + Expect(mset, i, i, true, i + 1, 2); + } +} + +TEST(MutexSet, Overflow) { + MutexSet mset; + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + mset.Add(i, true, i + 1); + mset.Add(i, true, i + 1); + } + mset.Add(100, true, 200); + EXPECT_EQ(mset.Size(), MutexSet::kMaxSize); + for (uptr i = 0; i < MutexSet::kMaxSize; i++) { + if (i == 0) + Expect(mset, i, MutexSet::kMaxSize - 1, + true, MutexSet::kMaxSize, 2); + else if (i == MutexSet::kMaxSize - 1) + Expect(mset, i, 100, true, 200, 1); + else + Expect(mset, i, i, true, i + 1, 2); + } +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc new file mode 100644 index 00000000..17b17977 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_shadow_test.cc @@ -0,0 +1,78 @@ +//===-- tsan_shadow_test.cc -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_platform.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Shadow, FastState) { + Shadow s(FastState(11, 22)); + EXPECT_EQ(s.tid(), (u64)11); + EXPECT_EQ(s.epoch(), (u64)22); + EXPECT_EQ(s.GetIgnoreBit(), false); + EXPECT_EQ(s.GetFreedAndReset(), false); + EXPECT_EQ(s.GetHistorySize(), 0); + EXPECT_EQ(s.addr0(), (u64)0); + EXPECT_EQ(s.size(), (u64)1); + EXPECT_EQ(s.IsWrite(), true); + + s.IncrementEpoch(); + EXPECT_EQ(s.epoch(), (u64)23); + s.IncrementEpoch(); + EXPECT_EQ(s.epoch(), (u64)24); + + s.SetIgnoreBit(); + EXPECT_EQ(s.GetIgnoreBit(), true); + s.ClearIgnoreBit(); + EXPECT_EQ(s.GetIgnoreBit(), false); + + for (int i = 0; i < 8; i++) { + s.SetHistorySize(i); + EXPECT_EQ(s.GetHistorySize(), i); + } + s.SetHistorySize(2); + s.ClearHistorySize(); + EXPECT_EQ(s.GetHistorySize(), 0); +} + +TEST(Shadow, Mapping) { + static int global; + int stack; + void *heap = malloc(0); + free(heap); + + CHECK(IsAppMem((uptr)&global)); + CHECK(IsAppMem((uptr)&stack)); + CHECK(IsAppMem((uptr)heap)); + + CHECK(IsShadowMem(MemToShadow((uptr)&global))); + CHECK(IsShadowMem(MemToShadow((uptr)&stack))); + CHECK(IsShadowMem(MemToShadow((uptr)heap))); +} + +TEST(Shadow, Celling) { + u64 aligned_data[4]; + char *data = (char*)aligned_data; + CHECK_EQ((uptr)data % kShadowSize, 0); + uptr s0 = MemToShadow((uptr)&data[0]); + CHECK_EQ(s0 % kShadowSize, 0); + for (unsigned i = 1; i < kShadowCell; i++) + CHECK_EQ(s0, MemToShadow((uptr)&data[i])); + for (unsigned i = kShadowCell; i < 2*kShadowCell; i++) + CHECK_EQ(s0 + kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); + for (unsigned i = 2*kShadowCell; i < 3*kShadowCell; i++) + CHECK_EQ(s0 + 2*kShadowSize*kShadowCnt, MemToShadow((uptr)&data[i])); +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc new file mode 100644 index 00000000..9aa29676 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_stack_test.cc @@ -0,0 +1,89 @@ +//===-- tsan_stack_test.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_sync.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" +#include + +namespace __tsan { + +static void TestStackTrace(StackTrace *trace) { + ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0); + uptr stack[128]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[128]; + + trace->ObtainCurrent(&thr, 0); + EXPECT_EQ(trace->Size(), (uptr)0); + + trace->ObtainCurrent(&thr, 42); + EXPECT_EQ(trace->Size(), (uptr)1); + EXPECT_EQ(trace->Get(0), (uptr)42); + + *thr.shadow_stack_pos++ = 100; + *thr.shadow_stack_pos++ = 101; + trace->ObtainCurrent(&thr, 0); + EXPECT_EQ(trace->Size(), (uptr)2); + EXPECT_EQ(trace->Get(0), (uptr)100); + EXPECT_EQ(trace->Get(1), (uptr)101); + + trace->ObtainCurrent(&thr, 42); + EXPECT_EQ(trace->Size(), (uptr)3); + EXPECT_EQ(trace->Get(0), (uptr)100); + EXPECT_EQ(trace->Get(1), (uptr)101); + EXPECT_EQ(trace->Get(2), (uptr)42); +} + +TEST(StackTrace, Basic) { + ScopedInRtl in_rtl; + StackTrace trace; + TestStackTrace(&trace); +} + +TEST(StackTrace, StaticBasic) { + ScopedInRtl in_rtl; + uptr buf[10]; + StackTrace trace1(buf, 10); + TestStackTrace(&trace1); + StackTrace trace2(buf, 3); + TestStackTrace(&trace2); +} + +TEST(StackTrace, StaticTrim) { + ScopedInRtl in_rtl; + uptr buf[2]; + StackTrace trace(buf, 2); + + ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0); + uptr stack[128]; + thr.shadow_stack = &stack[0]; + thr.shadow_stack_pos = &stack[0]; + thr.shadow_stack_end = &stack[128]; + + *thr.shadow_stack_pos++ = 100; + *thr.shadow_stack_pos++ = 101; + *thr.shadow_stack_pos++ = 102; + trace.ObtainCurrent(&thr, 0); + EXPECT_EQ(trace.Size(), (uptr)2); + EXPECT_EQ(trace.Get(0), (uptr)101); + EXPECT_EQ(trace.Get(1), (uptr)102); + + trace.ObtainCurrent(&thr, 42); + EXPECT_EQ(trace.Size(), (uptr)2); + EXPECT_EQ(trace.Get(0), (uptr)102); + EXPECT_EQ(trace.Get(1), (uptr)42); +} + + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc new file mode 100644 index 00000000..dddf0b29 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_sync_test.cc @@ -0,0 +1,65 @@ +//===-- tsan_sync_test.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_sync.h" +#include "tsan_rtl.h" +#include "tsan_mman.h" +#include "gtest/gtest.h" + +#include +#include +#include + +namespace __tsan { + +TEST(Sync, Table) { + const uintptr_t kIters = 512*1024; + const uintptr_t kRange = 10000; + + ScopedInRtl in_rtl; + ThreadState *thr = cur_thread(); + uptr pc = 0; + + SyncTab tab; + SyncVar *golden[kRange] = {}; + unsigned seed = 0; + for (uintptr_t i = 0; i < kIters; i++) { + uintptr_t addr = rand_r(&seed) % (kRange - 1) + 1; + if (rand_r(&seed) % 2) { + // Get or add. + SyncVar *v = tab.GetOrCreateAndLock(thr, pc, addr, true); + EXPECT_TRUE(golden[addr] == 0 || golden[addr] == v); + EXPECT_EQ(v->addr, addr); + golden[addr] = v; + v->mtx.Unlock(); + } else { + // Remove. + SyncVar *v = tab.GetAndRemove(thr, pc, addr); + EXPECT_EQ(golden[addr], v); + if (v) { + EXPECT_EQ(v->addr, addr); + golden[addr] = 0; + DestroyAndFree(v); + } + } + } + for (uintptr_t addr = 0; addr < kRange; addr++) { + if (golden[addr] == 0) + continue; + SyncVar *v = tab.GetAndRemove(thr, pc, addr); + EXPECT_EQ(v, golden[addr]); + EXPECT_EQ(v->addr, addr); + DestroyAndFree(v); + } +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc new file mode 100644 index 00000000..84d94dd0 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_unit_test_main.cc @@ -0,0 +1,19 @@ +//===-- tsan_unit_test_main.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/projects/compiler-rt/lib/tsan/tests/unit/tsan_vector_test.cc b/projects/compiler-rt/lib/tsan/tests/unit/tsan_vector_test.cc new file mode 100644 index 00000000..cfef6e52 --- /dev/null +++ b/projects/compiler-rt/lib/tsan/tests/unit/tsan_vector_test.cc @@ -0,0 +1,45 @@ +//===-- tsan_vector_test.cc -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_vector.h" +#include "tsan_rtl.h" +#include "gtest/gtest.h" + +namespace __tsan { + +TEST(Vector, Basic) { + ScopedInRtl in_rtl; + Vector v(MBlockScopedBuf); + EXPECT_EQ(v.Size(), (uptr)0); + v.PushBack(42); + EXPECT_EQ(v.Size(), (uptr)1); + EXPECT_EQ(v[0], 42); + v.PushBack(43); + EXPECT_EQ(v.Size(), (uptr)2); + EXPECT_EQ(v[0], 42); + EXPECT_EQ(v[1], 43); +} + +TEST(Vector, Stride) { + ScopedInRtl in_rtl; + Vector v(MBlockScopedBuf); + for (int i = 0; i < 1000; i++) { + v.PushBack(i); + EXPECT_EQ(v.Size(), (uptr)(i + 1)); + EXPECT_EQ(v[i], i); + } + for (int i = 0; i < 1000; i++) { + EXPECT_EQ(v[i], i); + } +} + +} // namespace __tsan diff --git a/projects/compiler-rt/lib/ubsan/CMakeLists.txt b/projects/compiler-rt/lib/ubsan/CMakeLists.txt new file mode 100644 index 00000000..675c47f6 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/CMakeLists.txt @@ -0,0 +1,56 @@ +# Build for the undefined behavior sanitizer runtime support library. + +set(UBSAN_SOURCES + ubsan_diag.cc + ubsan_handlers.cc + ubsan_value.cc + ) + +set(UBSAN_CXX_SOURCES + ubsan_handlers_cxx.cc + ubsan_type_hash.cc + ) + +include_directories(..) + +set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) + +filter_available_targets(UBSAN_SUPPORTED_ARCH + x86_64 i386) + +set(UBSAN_RUNTIME_LIBRARIES) + +if(APPLE) + # Build universal binary on APPLE. + add_compiler_rt_osx_static_runtime(clang_rt.ubsan_osx + ARCH ${UBSAN_SUPPORTED_ARCH} + SOURCES ${UBSAN_SOURCES} ${UBSAN_CXX_SOURCES} + $ + CFLAGS ${UBSAN_CFLAGS}) + list(APPEND UBSAN_RUNTIME_LIBRARIES clang_rt.ubsan_osx) +else() + # Build separate libraries for each target. + foreach(arch ${UBSAN_SUPPORTED_ARCH}) + # Main UBSan runtime. + add_compiler_rt_static_runtime(clang_rt.ubsan-${arch} ${arch} + SOURCES ${UBSAN_SOURCES} + CFLAGS ${UBSAN_CFLAGS}) + # C++-specific parts of UBSan runtime. Requires a C++ ABI library. + add_compiler_rt_static_runtime(clang_rt.ubsan_cxx-${arch} ${arch} + SOURCES ${UBSAN_CXX_SOURCES} + CFLAGS ${UBSAN_CFLAGS}) + list(APPEND UBSAN_RUNTIME_LIBRARIES + clang_rt.san-${arch} + clang_rt.ubsan-${arch} + clang_rt.ubsan_cxx-${arch}) + if (UNIX AND NOT ${arch} STREQUAL "i386") + add_sanitizer_rt_symbols(clang_rt.ubsan-${arch} ubsan.syms.extra) + add_sanitizer_rt_symbols(clang_rt.ubsan_cxx-${arch} ubsan.syms.extra) + list(APPEND UBSAN_RUNTIME_LIBRARIES + clang_rt.ubsan-${arch}-symbols + clang_rt.ubsan_cxx-${arch}-symbols) + endif() + endforeach() +endif() + +add_subdirectory(lit_tests) diff --git a/projects/compiler-rt/lib/ubsan/Makefile.mk b/projects/compiler-rt/lib/ubsan/Makefile.mk new file mode 100644 index 00000000..d5561f41 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/Makefile.mk @@ -0,0 +1,26 @@ +#===- lib/ubsan/Makefile.mk ---------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := ubsan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +CXXSources := ubsan_type_hash.cc ubsan_handlers_cxx.cc +CSources := $(filter-out $(CXXSources),$(Sources)) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the ubsan functions. +UbsanFunctions := $(CSources:%.cc=%) +UbsanCXXFunctions := $(CXXSources:%.cc=%) diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/AsanConfig/lit.cfg b/projects/compiler-rt/lib/ubsan/lit_tests/AsanConfig/lit.cfg new file mode 100644 index 00000000..407e5ec3 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/AsanConfig/lit.cfg @@ -0,0 +1,25 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +ubsan_lit_tests_dir = get_required_attr(config, "ubsan_lit_tests_dir") +ubsan_lit_cfg = os.path.join(ubsan_lit_tests_dir, "lit.common.cfg") +lit_config.load_config(config, ubsan_lit_cfg) + +config.name = 'UndefinedBehaviorSanitizer-AddressSanitizer' + +# Define %clang and %clangxx substitutions to use in test RUN lines. +config.substitutions.append( ("%clang ", (" " + config.clang + + " -fsanitize=address ")) ) +config.substitutions.append( ("%clangxx ", (" " + config.clang + + " -fsanitize=address" + + " --driver-mode=g++ ")) ) diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/AsanConfig/lit.site.cfg.in b/projects/compiler-rt/lib/ubsan/lit_tests/AsanConfig/lit.site.cfg.in new file mode 100644 index 00000000..f7574183 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/AsanConfig/lit.site.cfg.in @@ -0,0 +1,9 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.ubsan_lit_tests_dir = "@UBSAN_LIT_TESTS_DIR@" + +# Load tool-specific config that would do the real work. +print config.ubsan_lit_tests_dir +lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/AsanConfig/lit.cfg") diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/CMakeLists.txt b/projects/compiler-rt/lib/ubsan/lit_tests/CMakeLists.txt new file mode 100644 index 00000000..36d8dc1f --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/CMakeLists.txt @@ -0,0 +1,23 @@ +set(UBSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/UbsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig/lit.site.cfg) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/AsanConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg) + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + # Run UBSan output tests only if we're sure that clang would produce + # working binaries. + set(UBSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + ${UBSAN_RUNTIME_LIBRARIES} + asan_runtime_libraries) + add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig + DEPENDS ${UBSAN_TEST_DEPS}) + set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan unittests") +endif() diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Float/cast-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Float/cast-overflow.cpp new file mode 100644 index 00000000..35f9336c --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Float/cast-overflow.cpp @@ -0,0 +1,99 @@ +// RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t +// RUN: %t _ +// RUN: %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 +// RUN: %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 +// RUN: %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2 +// RUN: %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3 +// RUN: %t 4 2>&1 | FileCheck %s --check-prefix=CHECK-4 +// RUN: %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5 +// RUN: %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6 +// FIXME: %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7 +// RUN: %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8 + +// This test assumes float and double are IEEE-754 single- and double-precision. + +#include +#include +#include + +float Inf; +float NaN; + +int main(int argc, char **argv) { + float MaxFloatRepresentableAsInt = 0x7fffff80; + (int)MaxFloatRepresentableAsInt; // ok + (int)-MaxFloatRepresentableAsInt; // ok + + float MinFloatRepresentableAsInt = -0x7fffffff - 1; + (int)MinFloatRepresentableAsInt; // ok + + float MaxFloatRepresentableAsUInt = 0xffffff00u; + (unsigned int)MaxFloatRepresentableAsUInt; // ok + +#ifdef __SIZEOF_INT128__ + unsigned __int128 FloatMaxAsUInt128 = -((unsigned __int128)1 << 104); + (void)(float)FloatMaxAsUInt128; // ok +#endif + + float NearlyMinusOne = -0.99999; + unsigned Zero = NearlyMinusOne; // ok + + // Build a '+Inf'. + char InfVal[] = { 0x00, 0x00, 0x80, 0x7f }; + float Inf; + memcpy(&Inf, InfVal, 4); + + // Build a 'NaN'. + char NaNVal[] = { 0x01, 0x00, 0x80, 0x7f }; + float NaN; + memcpy(&NaN, NaNVal, 4); + + double DblInf = (double)Inf; // ok + + switch (argv[1][0]) { + // FIXME: Produce a source location for these checks and test for it here. + + // Floating point -> integer overflow. + case '0': + // Note that values between 0x7ffffe00 and 0x80000000 may or may not + // successfully round-trip, depending on the rounding mode. + // CHECK-0: runtime error: value 2.14748{{.*}} is outside the range of representable values of type 'int' + return MaxFloatRepresentableAsInt + 0x80; + case '1': + // CHECK-1: runtime error: value -2.14748{{.*}} is outside the range of representable values of type 'int' + return MinFloatRepresentableAsInt - 0x100; + case '2': + // CHECK-2: runtime error: value -1 is outside the range of representable values of type 'unsigned int' + return (unsigned)-1.0; + case '3': + // CHECK-3: runtime error: value 4.2949{{.*}} is outside the range of representable values of type 'unsigned int' + return (unsigned)(MaxFloatRepresentableAsUInt + 0x100); + + case '4': + // CHECK-4: runtime error: value {{.*}} is outside the range of representable values of type 'int' + return Inf; + case '5': + // CHECK-5: runtime error: value {{.*}} is outside the range of representable values of type 'int' + return NaN; + + // Integer -> floating point overflow. + case '6': + // CHECK-6: {{runtime error: value 0xffffff00000000000000000000000001 is outside the range of representable values of type 'float'|__int128 not supported}} +#ifdef __SIZEOF_INT128__ + return (float)(FloatMaxAsUInt128 + 1); +#else + puts("__int128 not supported"); + return 0; +#endif + // FIXME: The backend cannot lower __fp16 operations on x86 yet. + //case '7': + // (__fp16)65504; // ok + // // CHECK-7: runtime error: value 65505 is outside the range of representable values of type '__fp16' + // return (__fp16)65505; + + // Floating point -> floating point overflow. + case '8': + // CHECK-8: runtime error: value 1e+39 is outside the range of representable values of type 'float' + return (float)1e39; + } +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/add-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/add-overflow.cpp new file mode 100644 index 00000000..412eb762 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/add-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include +#include + +int main() { + // These promote to 'int'. + (void)(int8_t(0x7f) + int8_t(0x7f)); + (void)(int16_t(0x3fff) + int16_t(0x4000)); + +#ifdef ADD_I32 + int32_t k = 0x12345678; + k += 0x789abcde; + // CHECK-ADD_I32: add-overflow.cpp:[[@LINE-1]]:5: runtime error: signed integer overflow: 305419896 + 2023406814 cannot be represented in type 'int' +#endif + +#ifdef ADD_I64 + (void)(int64_t(8000000000000000000ll) + int64_t(2000000000000000000ll)); + // CHECK-ADD_I64: 8000000000000000000 + 2000000000000000000 cannot be represented in type '{{long( long)?}}' +#endif + +#ifdef ADD_I128 +# ifdef __SIZEOF_INT128__ + (void)((__int128_t(1) << 126) + (__int128_t(1) << 126)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x40000000000000000000000000000000 \+ 0x40000000000000000000000000000000 cannot be represented in type '__int128'|__int128 not supported}} +#endif +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/div-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/div-overflow.cpp new file mode 100644 index 00000000..83aa8544 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/div-overflow.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s + +#include + +int main() { + unsigned(0x80000000) / -1; + + // CHECK: div-overflow.cpp:9:23: runtime error: division of -2147483648 by -1 cannot be represented in type 'int' + int32_t(0x80000000) / -1; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/div-zero.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/div-zero.cpp new file mode 100644 index 00000000..6b8aadfe --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/div-zero.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %t 2>&1 | FileCheck %s + +#ifdef __SIZEOF_INT128__ +typedef __int128 intmax; +#else +typedef long long intmax; +#endif + +int main() { + // CHECK: div-zero.cpp:[[@LINE+1]]:12: runtime error: division by zero + DIVIDEND / 0; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/incdec-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/incdec-overflow.cpp new file mode 100644 index 00000000..904250a7 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/incdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s + +#include + +int main() { + int n = 0x7ffffffd; + n++; + n++; + int m = -n - 1; + // CHECK: incdec-overflow.cpp:15:3: runtime error: signed integer overflow: [[MINUS:-?]]214748364 + // CHECK: + [[MINUS]]1 cannot be represented in type 'int' + OP; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/mul-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/mul-overflow.cpp new file mode 100644 index 00000000..1cfe23f5 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/mul-overflow.cpp @@ -0,0 +1,14 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s + +#include + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) * int8_t(0x7f)); + (void)(int16_t(0x7fff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * uint16_t(0x8000)); + + // CHECK: mul-overflow.cpp:13:27: runtime error: signed integer overflow: 65535 * 32769 cannot be represented in type 'int' + (void)(uint16_t(0xffff) * uint16_t(0x8001)); +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/negate-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/negate-overflow.cpp new file mode 100644 index 00000000..6bee3eea --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/negate-overflow.cpp @@ -0,0 +1,12 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKS +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKU + +int main() { + // CHECKS-NOT: runtime error + // CHECKU: negate-overflow.cpp:[[@LINE+2]]:3: runtime error: negation of 2147483648 cannot be represented in type 'unsigned int' + // CHECKU-NOT: cast to an unsigned + -unsigned(-0x7fffffff - 1); // ok + // CHECKS: negate-overflow.cpp:[[@LINE+2]]:10: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself + // CHECKU-NOT: runtime error + return -(-0x7fffffff - 1); +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/no-recover.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/no-recover.cpp new file mode 100644 index 00000000..64787b7c --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/no-recover.cpp @@ -0,0 +1,22 @@ +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ABORT + +#include + +int main() { + // These promote to 'int'. + (void)(uint8_t(0xff) + uint8_t(0xff)); + (void)(uint16_t(0xf0fff) + uint16_t(0x0fff)); + // RECOVER-NOT: runtime error + // ABORT-NOT: runtime error + + uint32_t k = 0x87654321; + k += 0xedcba987; + // RECOVER: no-recover.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' + // ABORT: no-recover.cpp:[[@LINE-2]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' + + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // RECOVER: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' + // ABORT-NOT: runtime error +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/shift.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/shift.cpp new file mode 100644 index 00000000..f35fa1f9 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/shift.cpp @@ -0,0 +1,37 @@ +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH + +#include + +int main() { + int a = 1; + unsigned b = 1; + + a <<= 31; // ok in C++11, not ok in C99/C11 + b <<= 31; // ok + b <<= 1; // still ok, unsigned + +#ifdef LSH_OVERFLOW + // CHECK-LSH_OVERFLOW: shift.cpp:24:5: runtime error: left shift of negative value -2147483648 + a OP 1; +#endif + +#ifdef TOO_LOW + // CHECK-TOO_LOW: shift.cpp:29:5: runtime error: shift exponent -3 is negative + a OP (-3); +#endif + +#ifdef TOO_HIGH + a = 0; + // CHECK-TOO_HIGH: shift.cpp:35:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' + a OP 32; +#endif +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/sub-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/sub-overflow.cpp new file mode 100644 index 00000000..bf33d293 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/sub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 + +#include +#include + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) - int8_t(0x7f)); + (void)(int16_t(-2) - int16_t(0x7fff)); + +#ifdef SUB_I32 + (void)(int32_t(-2) - int32_t(0x7fffffff)); + // CHECK-SUB_I32: sub-overflow.cpp:[[@LINE-1]]:22: runtime error: signed integer overflow: -2 - 2147483647 cannot be represented in type 'int' +#endif + +#ifdef SUB_I64 + (void)(int64_t(-8000000000000000000ll) - int64_t(2000000000000000000ll)); + // CHECK-SUB_I64: -8000000000000000000 - 2000000000000000000 cannot be represented in type '{{long( long)?}}' +#endif + +#ifdef SUB_I128 +# ifdef __SIZEOF_INT128__ + (void)(-(__int128_t(1) << 126) - (__int128_t(1) << 126) - 1); +# else + puts("__int128 not supported"); +# endif + // CHECK-SUB_I128: {{0x80000000000000000000000000000000 - 1 cannot be represented in type '__int128'|__int128 not supported}} +#endif +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/uadd-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/uadd-overflow.cpp new file mode 100644 index 00000000..2ef31c06 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/uadd-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include +#include + +int main() { + // These promote to 'int'. + (void)(uint8_t(0xff) + uint8_t(0xff)); + (void)(uint16_t(0xf0fff) + uint16_t(0x0fff)); + +#ifdef ADD_I32 + uint32_t k = 0x87654321; + k += 0xedcba987; + // CHECK-ADD_I32: uadd-overflow.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' +#endif + +#ifdef ADD_I64 + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // CHECK-ADD_I64: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' +#endif + +#ifdef ADD_I128 +# ifdef __SIZEOF_INT128__ + (void)((__uint128_t(1) << 127) + (__uint128_t(1) << 127)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x80000000000000000000000000000000 \+ 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}} +#endif +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/uincdec-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/uincdec-overflow.cpp new file mode 100644 index 00000000..a14bd6a7 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/uincdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s +// RUN: %clangxx -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s + +#include + +int main() { + unsigned n = 0xfffffffd; + n++; + n++; + unsigned m = 0; + // CHECK-INC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 4294967295 + 1 cannot be represented in type 'unsigned int' + // CHECK-DEC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int' + OP; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/umul-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/umul-overflow.cpp new file mode 100644 index 00000000..c84bb39e --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/umul-overflow.cpp @@ -0,0 +1,19 @@ +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s + +#include + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) * int8_t(0x7f)); + (void)(int16_t(0x7fff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * uint16_t(0x8000)); + + // Not an unsigned overflow + (void)(uint16_t(0xffff) * uint16_t(0x8001)); + + (void)(uint32_t(0xffffffff) * uint32_t(0x2)); + // CHECK: umul-overflow.cpp:15:31: runtime error: unsigned integer overflow: 4294967295 * 2 cannot be represented in type 'unsigned int' + + return 0; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/usub-overflow.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/usub-overflow.cpp new file mode 100644 index 00000000..78f74557 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Integer/usub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 + +#include +#include + +int main() { + // These promote to 'int'. + (void)(uint8_t(0) - uint8_t(0x7f)); + (void)(uint16_t(0) - uint16_t(0x7fff)); + +#ifdef SUB_I32 + (void)(uint32_t(1) - uint32_t(2)); + // CHECK-SUB_I32: usub-overflow.cpp:[[@LINE-1]]:22: runtime error: unsigned integer overflow: 1 - 2 cannot be represented in type 'unsigned int' +#endif + +#ifdef SUB_I64 + (void)(uint64_t(8000000000000000000ll) - uint64_t(9000000000000000000ll)); + // CHECK-SUB_I64: 8000000000000000000 - 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' +#endif + +#ifdef SUB_I128 +# ifdef __SIZEOF_INT128__ + (void)((__uint128_t(1) << 126) - (__uint128_t(1) << 127)); +# else + puts("__int128 not supported\n"); +# endif + // CHECK-SUB_I128: {{0x40000000000000000000000000000000 - 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}} +#endif +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/bool.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/bool.cpp new file mode 100644 index 00000000..e916e7fb --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/bool.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=bool %s -O3 -o %T/bool.exe && %T/bool.exe 2>&1 | FileCheck %s + +unsigned char NotABool = 123; + +int main(int argc, char **argv) { + bool *p = (bool*)&NotABool; + + // CHECK: bool.cpp:9:10: runtime error: load of value 123, which is not a valid value for type 'bool' + return *p; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/bounds.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/bounds.cpp new file mode 100644 index 00000000..dc4c4a51 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/bounds.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=bounds %s -O3 -o %T/bounds.exe +// RUN: %T/bounds.exe 0 0 0 +// RUN: %T/bounds.exe 1 2 3 +// RUN: %T/bounds.exe 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2 +// RUN: %T/bounds.exe 0 3 0 2>&1 | FileCheck %s --check-prefix=CHECK-B-3 +// RUN: %T/bounds.exe 0 0 4 2>&1 | FileCheck %s --check-prefix=CHECK-C-4 + +int main(int argc, char **argv) { + int arr[2][3][4] = {}; + + return arr[argv[1][0] - '0'][argv[2][0] - '0'][argv[3][0] - '0']; + // CHECK-A-2: bounds.cpp:11:10: runtime error: index 2 out of bounds for type 'int [2][3][4]' + // CHECK-B-3: bounds.cpp:11:10: runtime error: index 3 out of bounds for type 'int [3][4]' + // CHECK-C-4: bounds.cpp:11:10: runtime error: index 4 out of bounds for type 'int [4]' +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/deduplication.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/deduplication.cpp new file mode 100644 index 00000000..d325bf6d --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/deduplication.cpp @@ -0,0 +1,25 @@ +// RUN: %clangxx -fsanitize=undefined %s -o %t && %t 2>&1 | FileCheck %s +// Verify deduplication works by ensuring only one diag is emitted. +#include +#include + +void overflow() { + int i = INT_MIN; + --i; +} + +int main() { + // CHECK: Start + fprintf(stderr, "Start\n"); + + // CHECK: runtime error + // CHECK-NOT: runtime error + // CHECK-NOT: runtime error + overflow(); + overflow(); + overflow(); + + // CHECK: End + fprintf(stderr, "End\n"); + return 0; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/enum.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/enum.cpp new file mode 100644 index 00000000..c5642507 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/enum.cpp @@ -0,0 +1,17 @@ +// RUN: %clangxx -fsanitize=enum %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %t +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL + +enum E { a = 1 } e; +#undef E + +int main(int argc, char **argv) { + // memset(&e, 0xff, sizeof(e)); + for (unsigned char *p = (unsigned char*)&e; p != (unsigned char*)(&e + 1); ++p) + *p = 0xff; + + // CHECK-PLAIN: error: load of value 4294967295, which is not a valid value for type 'enum E' + // FIXME: Support marshalling and display of enum class values. + // CHECK-BOOL: error: load of value , which is not a valid value for type 'enum E' + return (int)e != -1; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/missing_return.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/missing_return.cpp new file mode 100644 index 00000000..7da238e2 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/missing_return.cpp @@ -0,0 +1,9 @@ +// RUN: %clangxx -fsanitize=return %s -O3 -o %t && %t 2>&1 | FileCheck %s + +// CHECK: missing_return.cpp:4:5: runtime error: execution reached the end of a value-returning function without returning a value +int f() { +} + +int main(int, char **argv) { + return f(); +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/unreachable.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/unreachable.cpp new file mode 100644 index 00000000..75fc3e5b --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/unreachable.cpp @@ -0,0 +1,6 @@ +// RUN: %clangxx -fsanitize=unreachable %s -O3 -o %t && %t 2>&1 | FileCheck %s + +int main(int, char **argv) { + // CHECK: unreachable.cpp:5:3: runtime error: execution reached a __builtin_unreachable() call + __builtin_unreachable(); +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/vla.c b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/vla.c new file mode 100644 index 00000000..2fa88add --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/Misc/vla.c @@ -0,0 +1,11 @@ +// RUN: %clang -fsanitize=vla-bound %s -O3 -o %t +// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-MINUS-ONE +// RUN: %t a 2>&1 | FileCheck %s --check-prefix=CHECK-ZERO +// RUN: %t a b + +int main(int argc, char **argv) { + // CHECK-MINUS-ONE: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value -1 + // CHECK-ZERO: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value 0 + int arr[argc - 2]; + return 0; +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/function.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/function.cpp new file mode 100644 index 00000000..8106ae47 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/function.cpp @@ -0,0 +1,17 @@ +// RUN: %clangxx -fsanitize=function %s -O3 -g -o %t +// RUN: %t 2>&1 | FileCheck %s + +#include + +void f() {} + +void g(int x) {} + +int main(void) { + // CHECK: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)' + // CHECK-NEXT: function.cpp:6: note: f() defined here + reinterpret_cast(reinterpret_cast(f))(42); + + // CHECK-NOT: runtime error: call to function g + reinterpret_cast(reinterpret_cast(g))(42); +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/lit.local.cfg b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/lit.local.cfg new file mode 100644 index 00000000..27c61a34 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/Function/lit.local.cfg @@ -0,0 +1,3 @@ +# The function type checker is only supported on x86 and x86_64 for now. +if config.root.host_arch not in ['x86', 'x86_64']: + config.unsupported = True diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/misaligned.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/misaligned.cpp new file mode 100644 index 00000000..9b0b9a11 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/misaligned.cpp @@ -0,0 +1,73 @@ +// RUN: %clangxx -fsanitize=alignment %s -O3 -o %t +// RUN: %t l0 && %t s0 && %t r0 && %t m0 && %t f0 && %t n0 +// RUN: %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace +// RUN: %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN +// RUN: %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW + +#include + +struct S { + S() {} + int f() { return 0; } + int k; +}; + +int main(int, char **argv) { + char c[] __attribute__((aligned(8))) = { 0, 0, 0, 0, 1, 2, 3, 4, 5 }; + + // Pointer value may be unspecified here, but behavior is not undefined. + int *p = (int*)&c[4 + argv[1][1] - '0']; + S *s = (S*)p; + + (void)*p; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: misaligned.cpp:[[@LINE+4]]:12: runtime error: load of misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-LOAD-NEXT: [[PTR]]: note: pointer points here + // CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-LOAD-NEXT: {{^ \^}} + return *p && 0; + + case 's': + // CHECK-STORE: misaligned.cpp:[[@LINE+4]]:5: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-STORE-NEXT: [[PTR]]: note: pointer points here + // CHECK-STORE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-STORE-NEXT: {{^ \^}} + *p = 1; + break; + + case 'r': + // CHECK-REFERENCE: misaligned.cpp:[[@LINE+4]]:15: runtime error: reference binding to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-REFERENCE-NEXT: [[PTR]]: note: pointer points here + // CHECK-REFERENCE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-REFERENCE-NEXT: {{^ \^}} + {int &r = *p;} + break; + + case 'm': + // CHECK-MEMBER: misaligned.cpp:[[@LINE+4]]:15: runtime error: member access within misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMBER-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMBER-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMBER-NEXT: {{^ \^}} + return s->k && 0; + + case 'f': + // CHECK-MEMFUN: misaligned.cpp:[[@LINE+4]]:12: runtime error: member call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMFUN-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMFUN-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMFUN-NEXT: {{^ \^}} + return s->f() && 0; + + case 'n': + // FIXME: Provide a better source location here. + // CHECK-NEW: misaligned{{.*}}+0x{{[0-9a-f]*}}): runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here + // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-NEW-NEXT: {{^ \^}} + return (new (s) S)->k && 0; + } +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/null.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/null.cpp new file mode 100644 index 00000000..79726924 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/null.cpp @@ -0,0 +1,38 @@ +// RUN: %clangxx -fsanitize=null %s -O3 -o %t +// RUN: %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD +// RUN: %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: %t f 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN + +struct S { + int f() { return 0; } + int k; +}; + +int main(int, char **argv) { + int *p = 0; + S *s = 0; + + (void)*p; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: null.cpp:22:12: runtime error: load of null pointer of type 'int' + return *p; + case 's': + // CHECK-STORE: null.cpp:25:5: runtime error: store to null pointer of type 'int' + *p = 1; + break; + case 'r': + // CHECK-REFERENCE: null.cpp:29:15: runtime error: reference binding to null pointer of type 'int' + {int &r = *p;} + break; + case 'm': + // CHECK-MEMBER: null.cpp:33:15: runtime error: member access within null pointer of type 'S' + return s->k; + case 'f': + // CHECK-MEMFUN: null.cpp:36:12: runtime error: member call on null pointer of type 'S' + return s->f(); + } +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/vptr.cpp b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/vptr.cpp new file mode 100644 index 00000000..9095f727 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/TestCases/TypeCheck/vptr.cpp @@ -0,0 +1,117 @@ +// RUN: %clangxx -fsanitize=vptr %s -O3 -o %t +// RUN: %t rT && %t mT && %t fT && %t cT +// RUN: %t rU && %t mU && %t fU && %t cU +// RUN: %t rS && %t rV && %t oV +// RUN: %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace +// RUN: %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace +// RUN: %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace +// RUN: %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace + +// FIXME: This test produces linker errors on Darwin. +// XFAIL: darwin + +struct S { + S() : a(0) {} + ~S() {} + int a; + int f() { return 0; } + virtual int v() { return 0; } +}; + +struct T : S { + T() : b(0) {} + int b; + int g() { return 0; } + virtual int v() { return 1; } +}; + +struct U : S, T { virtual int v() { return 2; } }; + +int main(int, char **argv) { + T t; + (void)t.a; + (void)t.b; + (void)t.f(); + (void)t.g(); + (void)t.v(); + (void)t.S::v(); + + U u; + (void)u.T::a; + (void)u.b; + (void)u.T::f(); + (void)u.g(); + (void)u.v(); + (void)u.T::v(); + (void)((T&)u).S::v(); + + T *p = 0; + char Buffer[sizeof(U)] = {}; + switch (argv[1][1]) { + case '0': + p = reinterpret_cast(Buffer); + break; + case 'S': + p = reinterpret_cast(new S); + break; + case 'T': + p = new T; + break; + case 'U': + p = new U; + break; + case 'V': + p = reinterpret_cast(new U); + break; + } + + switch (argv[1][0]) { + case 'r': + // Binding a reference to storage of appropriate size and alignment is OK. + {T &r = *p;} + break; + + case 'm': + // CHECK-MEMBER: vptr.cpp:[[@LINE+5]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]] + return p->b; + + // CHECK-NULL-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-NULL-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr + // CHECK-NULL-MEMBER-NEXT: {{^ .. .. .. .. 00 00 00 00 00 00 00 00 }} + // CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}} + + case 'f': + // CHECK-MEMFUN: vptr.cpp:[[@LINE+5]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-MEMFUN-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]] + return p->g(); + + case 'o': + // CHECK-OFFSET: vptr.cpp:[[@LINE+5]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'U' + // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:'U']] + // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}} + // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]] + return reinterpret_cast(p)->v() - 2; + + case 'c': + // CHECK-DOWNCAST: vptr.cpp:[[@LINE+5]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-DOWNCAST-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]] + static_cast(reinterpret_cast(p)); + return 0; + } +} diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/UbsanConfig/lit.cfg b/projects/compiler-rt/lib/ubsan/lit_tests/UbsanConfig/lit.cfg new file mode 100644 index 00000000..fcc93035 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/UbsanConfig/lit.cfg @@ -0,0 +1,23 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +ubsan_lit_tests_dir = get_required_attr(config, "ubsan_lit_tests_dir") +ubsan_lit_cfg = os.path.join(ubsan_lit_tests_dir, "lit.common.cfg") +lit_config.load_config(config, ubsan_lit_cfg) + +config.name = 'UndefinedBehaviorSanitizer-Standalone' + +# Define %clang and %clangxx substitutions to use in test RUN lines. +config.substitutions.append( ("%clang ", (" " + config.clang + " ")) ) +config.substitutions.append( ("%clangxx ", (" " + config.clang + + " --driver-mode=g++ ")) ) diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/UbsanConfig/lit.site.cfg.in b/projects/compiler-rt/lib/ubsan/lit_tests/UbsanConfig/lit.site.cfg.in new file mode 100644 index 00000000..c08fc30d --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/UbsanConfig/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") + +# Tool-specific config options. +config.ubsan_lit_tests_dir = "@UBSAN_LIT_TESTS_DIR@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/UbsanConfig/lit.cfg") diff --git a/projects/compiler-rt/lib/ubsan/lit_tests/lit.common.cfg b/projects/compiler-rt/lib/ubsan/lit_tests/lit.common.cfg new file mode 100644 index 00000000..23c85e9f --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/lit_tests/lit.common.cfg @@ -0,0 +1,31 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup source root. +ubsan_lit_tests_dir = get_required_attr(config, 'ubsan_lit_tests_dir') +config.test_source_root = os.path.join(ubsan_lit_tests_dir, 'TestCases') + +def DisplayNoConfigMessage(): + lit_config.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-ubsan") + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# UndefinedBehaviorSanitizer tests are currently supported on +# Linux and Darwin only. +if config.host_os not in ['Linux', 'Darwin']: + config.unsupported = True + +config.pipefail = False diff --git a/projects/compiler-rt/lib/ubsan/ubsan.syms.extra b/projects/compiler-rt/lib/ubsan/ubsan.syms.extra new file mode 100644 index 00000000..7f8be694 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan.syms.extra @@ -0,0 +1 @@ +__ubsan_* diff --git a/projects/compiler-rt/lib/ubsan/ubsan_diag.cc b/projects/compiler-rt/lib/ubsan/ubsan_diag.cc new file mode 100644 index 00000000..fa643658 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_diag.cc @@ -0,0 +1,273 @@ +//===-- ubsan_diag.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Diagnostic reporting for the UBSan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_diag.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_symbolizer.h" +#include + +using namespace __ubsan; + +Location __ubsan::getCallerLocation(uptr CallerLoc) { + if (!CallerLoc) + return Location(); + + uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); + return getFunctionLocation(Loc, 0); +} + +Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) { + if (!Loc) + return Location(); + + AddressInfo Info; + if (!Symbolizer::GetOrInit()->SymbolizeCode(Loc, &Info, 1) || + !Info.module || !*Info.module) + return Location(Loc); + + if (FName && Info.function) + *FName = Info.function; + + if (!Info.file) + return ModuleLocation(Info.module, Info.module_offset); + + return SourceLocation(Info.file, Info.line, Info.column); +} + +Diag &Diag::operator<<(const TypeDescriptor &V) { + return AddArg(V.getTypeName()); +} + +Diag &Diag::operator<<(const Value &V) { + if (V.getType().isSignedIntegerTy()) + AddArg(V.getSIntValue()); + else if (V.getType().isUnsignedIntegerTy()) + AddArg(V.getUIntValue()); + else if (V.getType().isFloatTy()) + AddArg(V.getFloatValue()); + else + AddArg(""); + return *this; +} + +/// Hexadecimal printing for numbers too large for Printf to handle directly. +static void PrintHex(UIntMax Val) { +#if HAVE_INT128_T + Printf("0x%08x%08x%08x%08x", + (unsigned int)(Val >> 96), + (unsigned int)(Val >> 64), + (unsigned int)(Val >> 32), + (unsigned int)(Val)); +#else + UNREACHABLE("long long smaller than 64 bits?"); +#endif +} + +static void renderLocation(Location Loc) { + InternalScopedString LocBuffer(1024); + switch (Loc.getKind()) { + case Location::LK_Source: { + SourceLocation SLoc = Loc.getSourceLocation(); + if (SLoc.isInvalid()) + LocBuffer.append(""); + else + PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), + SLoc.getColumn()); + break; + } + case Location::LK_Module: + PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(), + Loc.getModuleLocation().getOffset()); + break; + case Location::LK_Memory: + LocBuffer.append("%p", Loc.getMemoryLocation()); + break; + case Location::LK_Null: + LocBuffer.append(""); + break; + } + Printf("%s:", LocBuffer.data()); +} + +static void renderText(const char *Message, const Diag::Arg *Args) { + for (const char *Msg = Message; *Msg; ++Msg) { + if (*Msg != '%') { + char Buffer[64]; + unsigned I; + for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I) + Buffer[I] = Msg[I]; + Buffer[I] = '\0'; + Printf(Buffer); + Msg += I - 1; + } else { + const Diag::Arg &A = Args[*++Msg - '0']; + switch (A.Kind) { + case Diag::AK_String: + Printf("%s", A.String); + break; + case Diag::AK_Mangled: { + Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); + break; + } + case Diag::AK_SInt: + // 'long long' is guaranteed to be at least 64 bits wide. + if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX) + Printf("%lld", (long long)A.SInt); + else + PrintHex(A.SInt); + break; + case Diag::AK_UInt: + if (A.UInt <= UINT64_MAX) + Printf("%llu", (unsigned long long)A.UInt); + else + PrintHex(A.UInt); + break; + case Diag::AK_Float: { + // FIXME: Support floating-point formatting in sanitizer_common's + // printf, and stop using snprintf here. + char Buffer[32]; + snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float); + Printf("%s", Buffer); + break; + } + case Diag::AK_Pointer: + Printf("%p", A.Pointer); + break; + } + } + } +} + +/// Find the earliest-starting range in Ranges which ends after Loc. +static Range *upperBound(MemoryLocation Loc, Range *Ranges, + unsigned NumRanges) { + Range *Best = 0; + for (unsigned I = 0; I != NumRanges; ++I) + if (Ranges[I].getEnd().getMemoryLocation() > Loc && + (!Best || + Best->getStart().getMemoryLocation() > + Ranges[I].getStart().getMemoryLocation())) + Best = &Ranges[I]; + return Best; +} + +/// Render a snippet of the address space near a location. +static void renderMemorySnippet(const __sanitizer::AnsiColorDecorator &Decor, + MemoryLocation Loc, + Range *Ranges, unsigned NumRanges, + const Diag::Arg *Args) { + const unsigned BytesToShow = 32; + const unsigned MinBytesNearLoc = 4; + + // Show at least the 8 bytes surrounding Loc. + MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc; + for (unsigned I = 0; I < NumRanges; ++I) { + Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min); + Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max); + } + + // If we have too many interesting bytes, prefer to show bytes after Loc. + if (Max - Min > BytesToShow) + Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc); + Max = Min + BytesToShow; + + // Emit data. + for (uptr P = Min; P != Max; ++P) { + // FIXME: Check that the address is readable before printing it. + unsigned char C = *reinterpret_cast(P); + Printf("%s%02x", (P % 8 == 0) ? " " : " ", C); + } + Printf("\n"); + + // Emit highlights. + Printf(Decor.Green()); + Range *InRange = upperBound(Min, Ranges, NumRanges); + for (uptr P = Min; P != Max; ++P) { + char Pad = ' ', Byte = ' '; + if (InRange && InRange->getEnd().getMemoryLocation() == P) + InRange = upperBound(P, Ranges, NumRanges); + if (!InRange && P > Loc) + break; + if (InRange && InRange->getStart().getMemoryLocation() < P) + Pad = '~'; + if (InRange && InRange->getStart().getMemoryLocation() <= P) + Byte = '~'; + char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 }; + Printf((P % 8 == 0) ? Buffer : &Buffer[1]); + } + Printf("%s\n", Decor.Default()); + + // Go over the line again, and print names for the ranges. + InRange = 0; + unsigned Spaces = 0; + for (uptr P = Min; P != Max; ++P) { + if (!InRange || InRange->getEnd().getMemoryLocation() == P) + InRange = upperBound(P, Ranges, NumRanges); + if (!InRange) + break; + + Spaces += (P % 8) == 0 ? 2 : 1; + + if (InRange && InRange->getStart().getMemoryLocation() == P) { + while (Spaces--) + Printf(" "); + renderText(InRange->getText(), Args); + Printf("\n"); + // FIXME: We only support naming one range for now! + break; + } + + Spaces += 2; + } + + // FIXME: Print names for anything we can identify within the line: + // + // * If we can identify the memory itself as belonging to a particular + // global, stack variable, or dynamic allocation, then do so. + // + // * If we have a pointer-size, pointer-aligned range highlighted, + // determine whether the value of that range is a pointer to an + // entity which we can name, and if so, print that name. + // + // This needs an external symbolizer, or (preferably) ASan instrumentation. +} + +Diag::~Diag() { + __sanitizer::AnsiColorDecorator Decor(PrintsToTty()); + SpinMutexLock l(&CommonSanitizerReportMutex); + Printf(Decor.Bold()); + + renderLocation(Loc); + + switch (Level) { + case DL_Error: + Printf("%s runtime error: %s%s", + Decor.Red(), Decor.Default(), Decor.Bold()); + break; + + case DL_Note: + Printf("%s note: %s", Decor.Black(), Decor.Default()); + break; + } + + renderText(Message, Args); + + Printf("%s\n", Decor.Default()); + + if (Loc.isMemoryLocation()) + renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, + NumRanges, Args); +} diff --git a/projects/compiler-rt/lib/ubsan/ubsan_diag.h b/projects/compiler-rt/lib/ubsan/ubsan_diag.h new file mode 100644 index 00000000..54d15a0c --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_diag.h @@ -0,0 +1,208 @@ +//===-- ubsan_diag.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Diagnostics emission for Clang's undefined behavior sanitizer. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_DIAG_H +#define UBSAN_DIAG_H + +#include "ubsan_value.h" + +namespace __ubsan { + +/// \brief A location within a loaded module in the program. These are used when +/// the location can't be resolved to a SourceLocation. +class ModuleLocation { + const char *ModuleName; + uptr Offset; + +public: + ModuleLocation() : ModuleName(0), Offset(0) {} + ModuleLocation(const char *ModuleName, uptr Offset) + : ModuleName(ModuleName), Offset(Offset) {} + const char *getModuleName() const { return ModuleName; } + uptr getOffset() const { return Offset; } +}; + +/// A location of some data within the program's address space. +typedef uptr MemoryLocation; + +/// \brief Location at which a diagnostic can be emitted. Either a +/// SourceLocation, a ModuleLocation, or a MemoryLocation. +class Location { +public: + enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory }; + +private: + LocationKind Kind; + // FIXME: In C++11, wrap these in an anonymous union. + SourceLocation SourceLoc; + ModuleLocation ModuleLoc; + MemoryLocation MemoryLoc; + +public: + Location() : Kind(LK_Null) {} + Location(SourceLocation Loc) : + Kind(LK_Source), SourceLoc(Loc) {} + Location(ModuleLocation Loc) : + Kind(LK_Module), ModuleLoc(Loc) {} + Location(MemoryLocation Loc) : + Kind(LK_Memory), MemoryLoc(Loc) {} + + LocationKind getKind() const { return Kind; } + + bool isSourceLocation() const { return Kind == LK_Source; } + bool isModuleLocation() const { return Kind == LK_Module; } + bool isMemoryLocation() const { return Kind == LK_Memory; } + + SourceLocation getSourceLocation() const { + CHECK(isSourceLocation()); + return SourceLoc; + } + ModuleLocation getModuleLocation() const { + CHECK(isModuleLocation()); + return ModuleLoc; + } + MemoryLocation getMemoryLocation() const { + CHECK(isMemoryLocation()); + return MemoryLoc; + } +}; + +/// Try to obtain a location for the caller. This might fail, and produce either +/// an invalid location or a module location for the caller. +Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC()); + +/// Try to obtain a location for the given function pointer. This might fail, +/// and produce either an invalid location or a module location for the caller. +/// If FName is non-null and the name of the function is known, set *FName to +/// the function name, otherwise *FName is unchanged. +Location getFunctionLocation(uptr Loc, const char **FName); + +/// A diagnostic severity level. +enum DiagLevel { + DL_Error, ///< An error. + DL_Note ///< A note, attached to a prior diagnostic. +}; + +/// \brief Annotation for a range of locations in a diagnostic. +class Range { + Location Start, End; + const char *Text; + +public: + Range() : Start(), End(), Text() {} + Range(MemoryLocation Start, MemoryLocation End, const char *Text) + : Start(Start), End(End), Text(Text) {} + Location getStart() const { return Start; } + Location getEnd() const { return End; } + const char *getText() const { return Text; } +}; + +/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'. +class MangledName { + const char *Name; +public: + MangledName(const char *Name) : Name(Name) {} + const char *getName() const { return Name; } +}; + +/// \brief Representation of an in-flight diagnostic. +/// +/// Temporary \c Diag instances are created by the handler routines to +/// accumulate arguments for a diagnostic. The destructor emits the diagnostic +/// message. +class Diag { + /// The location at which the problem occurred. + Location Loc; + + /// The diagnostic level. + DiagLevel Level; + + /// The message which will be emitted, with %0, %1, ... placeholders for + /// arguments. + const char *Message; + +public: + /// Kinds of arguments, corresponding to members of \c Arg's union. + enum ArgKind { + AK_String, ///< A string argument, displayed as-is. + AK_Mangled,///< A C++ mangled name, demangled before display. + AK_UInt, ///< An unsigned integer argument. + AK_SInt, ///< A signed integer argument. + AK_Float, ///< A floating-point argument. + AK_Pointer ///< A pointer argument, displayed in hexadecimal. + }; + + /// An individual diagnostic message argument. + struct Arg { + Arg() {} + Arg(const char *String) : Kind(AK_String), String(String) {} + Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {} + Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {} + Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {} + Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {} + Arg(const void *Pointer) : Kind(AK_Pointer), Pointer(Pointer) {} + + ArgKind Kind; + union { + const char *String; + UIntMax UInt; + SIntMax SInt; + FloatMax Float; + const void *Pointer; + }; + }; + +private: + static const unsigned MaxArgs = 5; + static const unsigned MaxRanges = 1; + + /// The arguments which have been added to this diagnostic so far. + Arg Args[MaxArgs]; + unsigned NumArgs; + + /// The ranges which have been added to this diagnostic so far. + Range Ranges[MaxRanges]; + unsigned NumRanges; + + Diag &AddArg(Arg A) { + CHECK(NumArgs != MaxArgs); + Args[NumArgs++] = A; + return *this; + } + + Diag &AddRange(Range A) { + CHECK(NumRanges != MaxRanges); + Ranges[NumRanges++] = A; + return *this; + } + + /// \c Diag objects are not copyable. + Diag(const Diag &); // NOT IMPLEMENTED + Diag &operator=(const Diag &); + +public: + Diag(Location Loc, DiagLevel Level, const char *Message) + : Loc(Loc), Level(Level), Message(Message), NumArgs(0), NumRanges(0) {} + ~Diag(); + + Diag &operator<<(const char *Str) { return AddArg(Str); } + Diag &operator<<(MangledName MN) { return AddArg(MN); } + Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); } + Diag &operator<<(const void *V) { return AddArg(V); } + Diag &operator<<(const TypeDescriptor &V); + Diag &operator<<(const Value &V); + Diag &operator<<(const Range &R) { return AddRange(R); } +}; + +} // namespace __ubsan + +#endif // UBSAN_DIAG_H diff --git a/projects/compiler-rt/lib/ubsan/ubsan_handlers.cc b/projects/compiler-rt/lib/ubsan/ubsan_handlers.cc new file mode 100644 index 00000000..d5564314 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_handlers.cc @@ -0,0 +1,281 @@ +//===-- ubsan_handlers.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Error logging entry points for the UBSan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_handlers.h" +#include "ubsan_diag.h" + +#include "sanitizer_common/sanitizer_common.h" + +using namespace __sanitizer; +using namespace __ubsan; + +namespace __ubsan { + const char *TypeCheckKinds[] = { + "load of", "store to", "reference binding to", "member access within", + "member call on", "constructor call on", "downcast of", "downcast of" + }; +} + +static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, + Location FallbackLoc) { + Location Loc = Data->Loc.acquire(); + + // Use the SourceLocation from Data to track deduplication, even if 'invalid' + if (Loc.getSourceLocation().isDisabled()) + return; + if (Data->Loc.isInvalid()) + Loc = FallbackLoc; + + if (!Pointer) + Diag(Loc, DL_Error, "%0 null pointer of type %1") + << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; + else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) + Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, " + "which requires %2 byte alignment") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer + << Data->Alignment << Data->Type; + else + Diag(Loc, DL_Error, "%0 address %1 with insufficient space " + "for an object of type %2") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; + if (Pointer) + Diag(Pointer, DL_Note, "pointer points here"); +} +void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data, + ValueHandle Pointer) { + handleTypeMismatchImpl(Data, Pointer, getCallerLocation()); +} +void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data, + ValueHandle Pointer) { + handleTypeMismatchImpl(Data, Pointer, getCallerLocation()); + Die(); +} + +/// \brief Common diagnostic emission for various forms of integer overflow. +template static void HandleIntegerOverflow(OverflowData *Data, + ValueHandle LHS, + const char *Operator, + T RHS) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, "%0 integer overflow: " + "%1 %2 %3 cannot be represented in type %4") + << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned") + << Value(Data->Type, LHS) << Operator << RHS << Data->Type; +} + +void __ubsan::__ubsan_handle_add_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleIntegerOverflow(Data, LHS, "+", Value(Data->Type, RHS)); +} +void __ubsan::__ubsan_handle_add_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_add_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_sub_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleIntegerOverflow(Data, LHS, "-", Value(Data->Type, RHS)); +} +void __ubsan::__ubsan_handle_sub_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_sub_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_mul_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleIntegerOverflow(Data, LHS, "*", Value(Data->Type, RHS)); +} +void __ubsan::__ubsan_handle_mul_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_mul_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, + ValueHandle OldVal) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + if (Data->Type.isSignedIntegerTy()) + Diag(Loc, DL_Error, + "negation of %0 cannot be represented in type %1; " + "cast to an unsigned type to negate this value to itself") + << Value(Data->Type, OldVal) << Data->Type; + else + Diag(Loc, DL_Error, + "negation of %0 cannot be represented in type %1") + << Value(Data->Type, OldVal) << Data->Type; +} +void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data, + ValueHandle OldVal) { + __ubsan_handle_negate_overflow(Data, OldVal); + Die(); +} + +void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Value LHSVal(Data->Type, LHS); + Value RHSVal(Data->Type, RHS); + if (RHSVal.isMinusOne()) + Diag(Loc, DL_Error, + "division of %0 by -1 cannot be represented in type %1") + << LHSVal << Data->Type; + else + Diag(Loc, DL_Error, "division by zero"); +} +void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_divrem_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, + ValueHandle LHS, + ValueHandle RHS) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Value LHSVal(Data->LHSType, LHS); + Value RHSVal(Data->RHSType, RHS); + if (RHSVal.isNegative()) + Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; + else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) + Diag(Loc, DL_Error, + "shift exponent %0 is too large for %1-bit type %2") + << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; + else if (LHSVal.isNegative()) + Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal; + else + Diag(Loc, DL_Error, + "left shift of %0 by %1 places cannot be represented in type %2") + << LHSVal << RHSVal << Data->LHSType; +} +void __ubsan::__ubsan_handle_shift_out_of_bounds_abort( + ShiftOutOfBoundsData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_shift_out_of_bounds(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data, + ValueHandle Index) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Value IndexVal(Data->IndexType, Index); + Diag(Loc, DL_Error, "index %0 out of bounds for type %1") + << IndexVal << Data->ArrayType; +} +void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data, + ValueHandle Index) { + __ubsan_handle_out_of_bounds(Data, Index); + Die(); +} + +void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { + Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call"); + Die(); +} + +void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) { + Diag(Data->Loc, DL_Error, + "execution reached the end of a value-returning function " + "without returning a value"); + Die(); +} + +void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data, + ValueHandle Bound) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, "variable length array bound evaluates to " + "non-positive value %0") + << Value(Data->Type, Bound); +} +void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, + ValueHandle Bound) { + __ubsan_handle_vla_bound_not_positive(Data, Bound); + Die(); +} + + +void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data, + ValueHandle From) { + // TODO: Add deduplication once a SourceLocation is generated for this check. + Diag(getCallerLocation(), DL_Error, + "value %0 is outside the range of representable values of type %2") + << Value(Data->FromType, From) << Data->FromType << Data->ToType; +} +void __ubsan::__ubsan_handle_float_cast_overflow_abort( + FloatCastOverflowData *Data, + ValueHandle From) { + Diag(getCallerLocation(), DL_Error, + "value %0 is outside the range of representable values of type %2") + << Value(Data->FromType, From) << Data->FromType << Data->ToType; + Die(); +} + +void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, + ValueHandle Val) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, + "load of value %0, which is not a valid value for type %1") + << Value(Data->Type, Val) << Data->Type; +} +void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, + ValueHandle Val) { + __ubsan_handle_load_invalid_value(Data, Val); + Die(); +} + +void __ubsan::__ubsan_handle_function_type_mismatch( + FunctionTypeMismatchData *Data, + ValueHandle Function) { + const char *FName = "(unknown)"; + + Location Loc = getFunctionLocation(Function, &FName); + + Diag(Data->Loc, DL_Error, + "call to function %0 through pointer to incorrect function type %1") + << FName << Data->Type; + Diag(Loc, DL_Note, "%0 defined here") << FName; +} + +void __ubsan::__ubsan_handle_function_type_mismatch_abort( + FunctionTypeMismatchData *Data, + ValueHandle Function) { + __ubsan_handle_function_type_mismatch(Data, Function); + Die(); +} diff --git a/projects/compiler-rt/lib/ubsan/ubsan_handlers.h b/projects/compiler-rt/lib/ubsan/ubsan_handlers.h new file mode 100644 index 00000000..14e6f04c --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_handlers.h @@ -0,0 +1,126 @@ +//===-- ubsan_handlers.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Entry points to the runtime library for Clang's undefined behavior sanitizer. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_HANDLERS_H +#define UBSAN_HANDLERS_H + +#include "ubsan_value.h" + +namespace __ubsan { + +struct TypeMismatchData { + SourceLocation Loc; + const TypeDescriptor &Type; + uptr Alignment; + unsigned char TypeCheckKind; +}; + +#define RECOVERABLE(checkname, ...) \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE \ + void __ubsan_handle_ ## checkname( __VA_ARGS__ ); \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE \ + void __ubsan_handle_ ## checkname ## _abort( __VA_ARGS__ ); + +/// \brief Handle a runtime type check failure, caused by either a misaligned +/// pointer, a null pointer, or a pointer to insufficient storage for the +/// type. +RECOVERABLE(type_mismatch, TypeMismatchData *Data, ValueHandle Pointer) + +struct OverflowData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle an integer addition overflow. +RECOVERABLE(add_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) + +/// \brief Handle an integer subtraction overflow. +RECOVERABLE(sub_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) + +/// \brief Handle an integer multiplication overflow. +RECOVERABLE(mul_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) + +/// \brief Handle a signed integer overflow for a unary negate operator. +RECOVERABLE(negate_overflow, OverflowData *Data, ValueHandle OldVal) + +/// \brief Handle an INT_MIN/-1 overflow or division by zero. +RECOVERABLE(divrem_overflow, OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) + +struct ShiftOutOfBoundsData { + SourceLocation Loc; + const TypeDescriptor &LHSType; + const TypeDescriptor &RHSType; +}; + +/// \brief Handle a shift where the RHS is out of bounds or a left shift where +/// the LHS is negative or overflows. +RECOVERABLE(shift_out_of_bounds, ShiftOutOfBoundsData *Data, + ValueHandle LHS, ValueHandle RHS) + +struct OutOfBoundsData { + SourceLocation Loc; + const TypeDescriptor &ArrayType; + const TypeDescriptor &IndexType; +}; + +/// \brief Handle an array index out of bounds error. +RECOVERABLE(out_of_bounds, OutOfBoundsData *Data, ValueHandle Index) + +struct UnreachableData { + SourceLocation Loc; +}; + +/// \brief Handle a __builtin_unreachable which is reached. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_builtin_unreachable(UnreachableData *Data); +/// \brief Handle reaching the end of a value-returning function. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_missing_return(UnreachableData *Data); + +struct VLABoundData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle a VLA with a non-positive bound. +RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound) + +struct FloatCastOverflowData { + // FIXME: SourceLocation Loc; + const TypeDescriptor &FromType; + const TypeDescriptor &ToType; +}; + +/// \brief Handle overflow in a conversion to or from a floating-point type. +RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From) + +struct InvalidValueData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle a load of an invalid value for the type. +RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val) + +struct FunctionTypeMismatchData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +RECOVERABLE(function_type_mismatch, + FunctionTypeMismatchData *Data, + ValueHandle Val) + +} + +#endif // UBSAN_HANDLERS_H diff --git a/projects/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc b/projects/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc new file mode 100644 index 00000000..b6cddefe --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc @@ -0,0 +1,74 @@ +//===-- ubsan_handlers_cxx.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Error logging entry points for the UBSan runtime, which are only used for C++ +// compilations. This file is permitted to use language features which require +// linking against a C++ ABI library. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_handlers_cxx.h" +#include "ubsan_diag.h" +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +using namespace __sanitizer; +using namespace __ubsan; + +namespace __ubsan { + extern const char *TypeCheckKinds[]; +} + +static void HandleDynamicTypeCacheMiss( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash, + bool Abort) { + if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash)) + // Just a cache miss. The type matches after all. + return; + + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, + "%0 address %1 which does not point to an object of type %2") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; + + // If possible, say what type it actually points to. + DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); + if (!DTI.isValid()) + Diag(Pointer, DL_Note, "object has invalid vptr") + << MangledName(DTI.getMostDerivedTypeName()) + << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); + else if (!DTI.getOffset()) + Diag(Pointer, DL_Note, "object is of type %0") + << MangledName(DTI.getMostDerivedTypeName()) + << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0"); + else + // FIXME: Find the type at the specified offset, and include that + // in the note. + Diag(Pointer - DTI.getOffset(), DL_Note, + "object is base class subobject at offset %0 within object of type %1") + << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName()) + << MangledName(DTI.getSubobjectTypeName()) + << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1"); + + if (Abort) + Die(); +} + +void __ubsan::__ubsan_handle_dynamic_type_cache_miss( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { + HandleDynamicTypeCacheMiss(Data, Pointer, Hash, false); +} +void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { + HandleDynamicTypeCacheMiss(Data, Pointer, Hash, true); +} diff --git a/projects/compiler-rt/lib/ubsan/ubsan_handlers_cxx.h b/projects/compiler-rt/lib/ubsan/ubsan_handlers_cxx.h new file mode 100644 index 00000000..cb1bca78 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_handlers_cxx.h @@ -0,0 +1,40 @@ +//===-- ubsan_handlers_cxx.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Entry points to the runtime library for Clang's undefined behavior sanitizer, +// for C++-specific checks. This code is not linked into C binaries. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_HANDLERS_CXX_H +#define UBSAN_HANDLERS_CXX_H + +#include "ubsan_value.h" + +namespace __ubsan { + +struct DynamicTypeCacheMissData { + SourceLocation Loc; + const TypeDescriptor &Type; + void *TypeInfo; + unsigned char TypeCheckKind; +}; + +/// \brief Handle a runtime type check failure, caused by an incorrect vptr. +/// When this handler is called, all we know is that the type was not in the +/// cache; this does not necessarily imply the existence of a bug. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_dynamic_type_cache_miss( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash); +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_dynamic_type_cache_miss_abort( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash); + +} + +#endif // UBSAN_HANDLERS_H diff --git a/projects/compiler-rt/lib/ubsan/ubsan_type_hash.cc b/projects/compiler-rt/lib/ubsan/ubsan_type_hash.cc new file mode 100644 index 00000000..a388bcc6 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_type_hash.cc @@ -0,0 +1,250 @@ +//===-- ubsan_type_hash.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of a hash table for fast checking of inheritance +// relationships. This file is only linked into C++ compilations, and is +// permitted to use language features which require a C++ ABI library. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +// The following are intended to be binary compatible with the definitions +// given in the Itanium ABI. We make no attempt to be ODR-compatible with +// those definitions, since existing ABI implementations aren't. + +namespace std { + class type_info { + public: + virtual ~type_info(); + + const char *__type_name; + }; +} + +namespace __cxxabiv1 { + +/// Type info for classes with no bases, and base class for type info for +/// classes with bases. +class __class_type_info : public std::type_info { + virtual ~__class_type_info(); +}; + +/// Type info for classes with simple single public inheritance. +class __si_class_type_info : public __class_type_info { +public: + virtual ~__si_class_type_info(); + + const __class_type_info *__base_type; +}; + +class __base_class_type_info { +public: + const __class_type_info *__base_type; + long __offset_flags; + + enum __offset_flags_masks { + __virtual_mask = 0x1, + __public_mask = 0x2, + __offset_shift = 8 + }; +}; + +/// Type info for classes with multiple, virtual, or non-public inheritance. +class __vmi_class_type_info : public __class_type_info { +public: + virtual ~__vmi_class_type_info(); + + unsigned int flags; + unsigned int base_count; + __base_class_type_info base_info[1]; +}; + +} + +namespace abi = __cxxabiv1; + +// We implement a simple two-level cache for type-checking results. For each +// (vptr,type) pair, a hash is computed. This hash is assumed to be globally +// unique; if it collides, we will get false negatives, but: +// * such a collision would have to occur on the *first* bad access, +// * the probability of such a collision is low (and for a 64-bit target, is +// negligible), and +// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs +// give better coverage. +// +// The first caching layer is a small hash table with no chaining; buckets are +// reused as needed. The second caching layer is a large hash table with open +// chaining. We can freely evict from either layer since this is just a cache. +// +// FIXME: Make these hash table accesses thread-safe. The races here are benign: +// assuming the unsequenced loads and stores don't misbehave too badly, +// the worst case is false negatives or poor cache behavior, not false +// positives or crashes. + +/// Find a bucket to store the given hash value in. +static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { + static const unsigned HashTableSize = 65537; + static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; + + unsigned First = (V & 65535) ^ 1; + unsigned Probe = First; + for (int Tries = 5; Tries; --Tries) { + if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) + return &__ubsan_vptr_hash_set[Probe]; + Probe += ((V >> 16) & 65535) + 1; + if (Probe >= HashTableSize) + Probe -= HashTableSize; + } + // FIXME: Pick a random entry from the probe sequence to evict rather than + // just taking the first. + return &__ubsan_vptr_hash_set[First]; +} + +/// A cache of recently-checked hashes. Mini hash table with "random" evictions. +__ubsan::HashValue +__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize]; + +/// \brief Determine whether \p Derived has a \p Base base class subobject at +/// offset \p Offset. +static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, + const abi::__class_type_info *Base, + sptr Offset) { + if (Derived->__type_name == Base->__type_name) + return Offset == 0; + + if (const abi::__si_class_type_info *SI = + dynamic_cast(Derived)) + return isDerivedFromAtOffset(SI->__base_type, Base, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast(Derived); + if (!VTI) + // No base class subobjects. + return false; + + // Look for a base class which is derived from \p Base at the right offset. + for (unsigned int base = 0; base != VTI->base_count; ++base) { + // FIXME: Curtail the recursion if this base can't possibly contain the + // given offset. + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // For now, just punt on virtual bases and say 'yes'. + // FIXME: OffsetHere is the offset in the vtable of the virtual base + // offset. Read the vbase offset out of the vtable and use it. + return true; + if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, + Base, Offset - OffsetHere)) + return true; + } + + return false; +} + +/// \brief Find the derived-most dynamic base class of \p Derived at offset +/// \p Offset. +static const abi::__class_type_info *findBaseAtOffset( + const abi::__class_type_info *Derived, sptr Offset) { + if (!Offset) + return Derived; + + if (const abi::__si_class_type_info *SI = + dynamic_cast(Derived)) + return findBaseAtOffset(SI->__base_type, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast(Derived); + if (!VTI) + // No base class subobjects. + return 0; + + for (unsigned int base = 0; base != VTI->base_count; ++base) { + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // FIXME: Can't handle virtual bases yet. + continue; + if (const abi::__class_type_info *Base = + findBaseAtOffset(VTI->base_info[base].__base_type, + Offset - OffsetHere)) + return Base; + } + + return 0; +} + +namespace { + +struct VtablePrefix { + /// The offset from the vptr to the start of the most-derived object. + /// This should never be greater than zero, and will usually be exactly + /// zero. + sptr Offset; + /// The type_info object describing the most-derived class type. + std::type_info *TypeInfo; +}; +VtablePrefix *getVtablePrefix(void *Object) { + VtablePrefix **VptrPtr = reinterpret_cast(Object); + if (!*VptrPtr) + return 0; + VtablePrefix *Prefix = *VptrPtr - 1; + if (Prefix->Offset > 0 || !Prefix->TypeInfo) + // This can't possibly be a valid vtable. + return 0; + return Prefix; +} + +} + +bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { + // A crash anywhere within this function probably means the vptr is corrupted. + // FIXME: Perform these checks more cautiously. + + // Check whether this is something we've evicted from the cache. + HashValue *Bucket = getTypeCacheHashTableBucket(Hash); + if (*Bucket == Hash) { + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + return true; + } + + VtablePrefix *Vtable = getVtablePrefix(Object); + if (!Vtable) + return false; + + // Check that this is actually a type_info object for a class type. + abi::__class_type_info *Derived = + dynamic_cast(Vtable->TypeInfo); + if (!Derived) + return false; + + abi::__class_type_info *Base = (abi::__class_type_info*)Type; + if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) + return false; + + // Success. Cache this result. + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + *Bucket = Hash; + return true; +} + +__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) { + VtablePrefix *Vtable = getVtablePrefix(Object); + if (!Vtable) + return DynamicTypeInfo(0, 0, 0); + const abi::__class_type_info *ObjectType = findBaseAtOffset( + static_cast(Vtable->TypeInfo), + -Vtable->Offset); + return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, + ObjectType ? ObjectType->__type_name : ""); +} diff --git a/projects/compiler-rt/lib/ubsan/ubsan_type_hash.h b/projects/compiler-rt/lib/ubsan/ubsan_type_hash.h new file mode 100644 index 00000000..58ecd3de --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_type_hash.h @@ -0,0 +1,63 @@ +//===-- ubsan_type_hash.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hashing of types for Clang's undefined behavior checker. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_TYPE_HASH_H +#define UBSAN_TYPE_HASH_H + +#include "sanitizer_common/sanitizer_common.h" + +namespace __ubsan { + +typedef uptr HashValue; + +/// \brief Information about the dynamic type of an object (extracted from its +/// vptr). +class DynamicTypeInfo { + const char *MostDerivedTypeName; + sptr Offset; + const char *SubobjectTypeName; + +public: + DynamicTypeInfo(const char *MDTN, sptr Offset, const char *STN) + : MostDerivedTypeName(MDTN), Offset(Offset), SubobjectTypeName(STN) {} + + /// Determine whether the object had a valid dynamic type. + bool isValid() const { return MostDerivedTypeName; } + /// Get the name of the most-derived type of the object. + const char *getMostDerivedTypeName() const { return MostDerivedTypeName; } + /// Get the offset from the most-derived type to this base class. + sptr getOffset() const { return Offset; } + /// Get the name of the most-derived type at the specified offset. + const char *getSubobjectTypeName() const { return SubobjectTypeName; } +}; + +/// \brief Get information about the dynamic type of an object. +DynamicTypeInfo getDynamicTypeInfo(void *Object); + +/// \brief Check whether the dynamic type of \p Object has a \p Type subobject +/// at offset 0. +/// \return \c true if the type matches, \c false if not. +bool checkDynamicType(void *Object, void *Type, HashValue Hash); + +const unsigned VptrTypeCacheSize = 128; + +/// \brief A cache of the results of checkDynamicType. \c checkDynamicType would +/// return \c true (modulo hash collisions) if +/// \code +/// __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] == Hash +/// \endcode +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +HashValue __ubsan_vptr_type_cache[VptrTypeCacheSize]; + +} // namespace __ubsan + +#endif // UBSAN_TYPE_HASH_H diff --git a/projects/compiler-rt/lib/ubsan/ubsan_value.cc b/projects/compiler-rt/lib/ubsan/ubsan_value.cc new file mode 100644 index 00000000..5d77350d --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_value.cc @@ -0,0 +1,101 @@ +//===-- ubsan_value.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Representation of a runtime value, as marshaled from the generated code to +// the ubsan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_value.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" + +using namespace __ubsan; + +SIntMax Value::getSIntValue() const { + CHECK(getType().isSignedIntegerTy()); + if (isInlineInt()) { + // Val was zero-extended to ValueHandle. Sign-extend from original width + // to SIntMax. + const unsigned ExtraBits = + sizeof(SIntMax) * 8 - getType().getIntegerBitWidth(); + return SIntMax(Val) << ExtraBits >> ExtraBits; + } + if (getType().getIntegerBitWidth() == 64) + return *reinterpret_cast(Val); +#if HAVE_INT128_T + if (getType().getIntegerBitWidth() == 128) + return *reinterpret_cast(Val); +#else + if (getType().getIntegerBitWidth() == 128) + UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); +#endif + UNREACHABLE("unexpected bit width"); +} + +UIntMax Value::getUIntValue() const { + CHECK(getType().isUnsignedIntegerTy()); + if (isInlineInt()) + return Val; + if (getType().getIntegerBitWidth() == 64) + return *reinterpret_cast(Val); +#if HAVE_INT128_T + if (getType().getIntegerBitWidth() == 128) + return *reinterpret_cast(Val); +#else + if (getType().getIntegerBitWidth() == 128) + UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); +#endif + UNREACHABLE("unexpected bit width"); +} + +UIntMax Value::getPositiveIntValue() const { + if (getType().isUnsignedIntegerTy()) + return getUIntValue(); + SIntMax Val = getSIntValue(); + CHECK(Val >= 0); + return Val; +} + +/// Get the floating-point value of this object, extended to a long double. +/// These are always passed by address (our calling convention doesn't allow +/// them to be passed in floating-point registers, so this has little cost). +FloatMax Value::getFloatValue() const { + CHECK(getType().isFloatTy()); + if (isInlineFloat()) { + switch (getType().getFloatBitWidth()) { +#if 0 + // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion + // from '__fp16' to 'long double'. + case 16: { + __fp16 Value; + internal_memcpy(&Value, &Val, 4); + return Value; + } +#endif + case 32: { + float Value; + internal_memcpy(&Value, &Val, 4); + return Value; + } + case 64: { + double Value; + internal_memcpy(&Value, &Val, 8); + return Value; + } + } + } else { + switch (getType().getFloatBitWidth()) { + case 64: return *reinterpret_cast(Val); + case 80: return *reinterpret_cast(Val); + case 128: return *reinterpret_cast(Val); + } + } + UNREACHABLE("unexpected floating point bit width"); +} diff --git a/projects/compiler-rt/lib/ubsan/ubsan_value.h b/projects/compiler-rt/lib/ubsan/ubsan_value.h new file mode 100644 index 00000000..54ed5ad1 --- /dev/null +++ b/projects/compiler-rt/lib/ubsan/ubsan_value.h @@ -0,0 +1,204 @@ +//===-- ubsan_value.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Representation of data which is passed from the compiler-generated calls into +// the ubsan runtime. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_VALUE_H +#define UBSAN_VALUE_H + +// For now, only support linux and darwin. Other platforms should be easy to +// add, and probably work as-is. +#if !defined(__linux__) && !defined(__APPLE__) +#error "UBSan not supported for this platform!" +#endif + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" + +// FIXME: Move this out to a config header. +#if __SIZEOF_INT128__ +typedef __int128 s128; +typedef unsigned __int128 u128; +#define HAVE_INT128_T 1 +#else +#define HAVE_INT128_T 0 +#endif + + +namespace __ubsan { + +/// \brief Largest integer types we support. +#if HAVE_INT128_T +typedef s128 SIntMax; +typedef u128 UIntMax; +#else +typedef s64 SIntMax; +typedef u64 UIntMax; +#endif + +/// \brief Largest floating-point type we support. +typedef long double FloatMax; + +/// \brief A description of a source location. This corresponds to Clang's +/// \c PresumedLoc type. +class SourceLocation { + const char *Filename; + u32 Line; + u32 Column; + +public: + SourceLocation() : Filename(), Line(), Column() {} + SourceLocation(const char *Filename, unsigned Line, unsigned Column) + : Filename(Filename), Line(Line), Column(Column) {} + + /// \brief Determine whether the source location is known. + bool isInvalid() const { return !Filename; } + + /// \brief Atomically acquire a copy, disabling original in-place. + /// Exactly one call to acquire() returns a copy that isn't disabled. + SourceLocation acquire() { + u32 OldColumn = __sanitizer::atomic_exchange( + (__sanitizer::atomic_uint32_t *)&Column, ~u32(0), + __sanitizer::memory_order_relaxed); + return SourceLocation(Filename, Line, OldColumn); + } + + /// \brief Determine if this Location has been disabled. + /// Disabled SourceLocations are invalid to use. + bool isDisabled() { + return Column == ~u32(0); + } + + /// \brief Get the presumed filename for the source location. + const char *getFilename() const { return Filename; } + /// \brief Get the presumed line number. + unsigned getLine() const { return Line; } + /// \brief Get the column within the presumed line. + unsigned getColumn() const { return Column; } +}; + + +/// \brief A description of a type. +class TypeDescriptor { + /// A value from the \c Kind enumeration, specifying what flavor of type we + /// have. + u16 TypeKind; + + /// A \c Type-specific value providing information which allows us to + /// interpret the meaning of a ValueHandle of this type. + u16 TypeInfo; + + /// The name of the type follows, in a format suitable for including in + /// diagnostics. + char TypeName[1]; + +public: + enum Kind { + /// An integer type. Lowest bit is 1 for a signed value, 0 for an unsigned + /// value. Remaining bits are log_2(bit width). The value representation is + /// the integer itself if it fits into a ValueHandle, and a pointer to the + /// integer otherwise. + TK_Integer = 0x0000, + /// A floating-point type. Low 16 bits are bit width. The value + /// representation is that of bitcasting the floating-point value to an + /// integer type. + TK_Float = 0x0001, + /// Any other type. The value representation is unspecified. + TK_Unknown = 0xffff + }; + + const char *getTypeName() const { return TypeName; } + + Kind getKind() const { + return static_cast(TypeKind); + } + + bool isIntegerTy() const { return getKind() == TK_Integer; } + bool isSignedIntegerTy() const { + return isIntegerTy() && (TypeInfo & 1); + } + bool isUnsignedIntegerTy() const { + return isIntegerTy() && !(TypeInfo & 1); + } + unsigned getIntegerBitWidth() const { + CHECK(isIntegerTy()); + return 1 << (TypeInfo >> 1); + } + + bool isFloatTy() const { return getKind() == TK_Float; } + unsigned getFloatBitWidth() const { + CHECK(isFloatTy()); + return TypeInfo; + } +}; + +/// \brief An opaque handle to a value. +typedef uptr ValueHandle; + + +/// \brief Representation of an operand value provided by the instrumented code. +/// +/// This is a combination of a TypeDescriptor (which is emitted as constant data +/// as an operand to a handler function) and a ValueHandle (which is passed at +/// runtime when a check failure occurs). +class Value { + /// The type of the value. + const TypeDescriptor &Type; + /// The encoded value itself. + ValueHandle Val; + + /// Is \c Val a (zero-extended) integer? + bool isInlineInt() const { + CHECK(getType().isIntegerTy()); + const unsigned InlineBits = sizeof(ValueHandle) * 8; + const unsigned Bits = getType().getIntegerBitWidth(); + return Bits <= InlineBits; + } + + /// Is \c Val a (zero-extended) integer representation of a float? + bool isInlineFloat() const { + CHECK(getType().isFloatTy()); + const unsigned InlineBits = sizeof(ValueHandle) * 8; + const unsigned Bits = getType().getFloatBitWidth(); + return Bits <= InlineBits; + } + +public: + Value(const TypeDescriptor &Type, ValueHandle Val) : Type(Type), Val(Val) {} + + const TypeDescriptor &getType() const { return Type; } + + /// \brief Get this value as a signed integer. + SIntMax getSIntValue() const; + + /// \brief Get this value as an unsigned integer. + UIntMax getUIntValue() const; + + /// \brief Decode this value, which must be a positive or unsigned integer. + UIntMax getPositiveIntValue() const; + + /// Is this an integer with value -1? + bool isMinusOne() const { + return getType().isSignedIntegerTy() && getSIntValue() == -1; + } + + /// Is this a negative integer? + bool isNegative() const { + return getType().isSignedIntegerTy() && getSIntValue() < 0; + } + + /// \brief Get this value as a floating-point quantity. + FloatMax getFloatValue() const; +}; + +} // namespace __ubsan + +#endif // UBSAN_VALUE_H diff --git a/projects/compiler-rt/lib/ucmpdi2.c b/projects/compiler-rt/lib/ucmpdi2.c new file mode 100644 index 00000000..40af2361 --- /dev/null +++ b/projects/compiler-rt/lib/ucmpdi2.c @@ -0,0 +1,51 @@ +/* ===-- ucmpdi2.c - Implement __ucmpdi2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ucmpdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: if (a < b) returns 0 + * if (a == b) returns 1 + * if (a > b) returns 2 + */ + +COMPILER_RT_ABI si_int +__ucmpdi2(du_int a, du_int b) +{ + udwords x; + x.all = a; + udwords y; + y.all = b; + if (x.s.high < y.s.high) + return 0; + if (x.s.high > y.s.high) + return 2; + if (x.s.low < y.s.low) + return 0; + if (x.s.low > y.s.low) + return 2; + return 1; +} + +#ifdef __ARM_EABI__ +/* Returns: if (a < b) returns -1 +* if (a == b) returns 0 +* if (a > b) returns 1 +*/ +COMPILER_RT_ABI si_int +__aeabi_ulcmp(di_int a, di_int b) +{ + return __ucmpdi2(a, b) - 1; +} +#endif + diff --git a/projects/compiler-rt/lib/ucmpti2.c b/projects/compiler-rt/lib/ucmpti2.c new file mode 100644 index 00000000..5466d217 --- /dev/null +++ b/projects/compiler-rt/lib/ucmpti2.c @@ -0,0 +1,42 @@ +/* ===-- ucmpti2.c - Implement __ucmpti2 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __ucmpti2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Returns: if (a < b) returns 0 + * if (a == b) returns 1 + * if (a > b) returns 2 + */ + +si_int +__ucmpti2(tu_int a, tu_int b) +{ + utwords x; + x.all = a; + utwords y; + y.all = b; + if (x.s.high < y.s.high) + return 0; + if (x.s.high > y.s.high) + return 2; + if (x.s.low < y.s.low) + return 0; + if (x.s.low > y.s.low) + return 2; + return 1; +} + +#endif diff --git a/projects/compiler-rt/lib/udivdi3.c b/projects/compiler-rt/lib/udivdi3.c new file mode 100644 index 00000000..6c0303df --- /dev/null +++ b/projects/compiler-rt/lib/udivdi3.c @@ -0,0 +1,25 @@ +/* ===-- udivdi3.c - Implement __udivdi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +du_int COMPILER_RT_ABI __udivmoddi4(du_int a, du_int b, du_int* rem); + +/* Returns: a / b */ + +COMPILER_RT_ABI du_int +__udivdi3(du_int a, du_int b) +{ + return __udivmoddi4(a, b, 0); +} diff --git a/projects/compiler-rt/lib/udivmoddi4.c b/projects/compiler-rt/lib/udivmoddi4.c new file mode 100644 index 00000000..57282d5b --- /dev/null +++ b/projects/compiler-rt/lib/udivmoddi4.c @@ -0,0 +1,251 @@ +/* ===-- udivmoddi4.c - Implement __udivmoddi4 -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivmoddi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Effects: if rem != 0, *rem = a % b + * Returns: a / b + */ + +/* Translated from Figure 3-40 of The PowerPC Compiler Writer's Guide */ + +COMPILER_RT_ABI du_int +__udivmoddi4(du_int a, du_int b, du_int* rem) +{ + const unsigned n_uword_bits = sizeof(su_int) * CHAR_BIT; + const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT; + udwords n; + n.all = a; + udwords d; + d.all = b; + udwords q; + udwords r; + unsigned sr; + /* special cases, X is unknown, K != 0 */ + if (n.s.high == 0) + { + if (d.s.high == 0) + { + /* 0 X + * --- + * 0 X + */ + if (rem) + *rem = n.s.low % d.s.low; + return n.s.low / d.s.low; + } + /* 0 X + * --- + * K X + */ + if (rem) + *rem = n.s.low; + return 0; + } + /* n.s.high != 0 */ + if (d.s.low == 0) + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 0 + */ + if (rem) + *rem = n.s.high % d.s.low; + return n.s.high / d.s.low; + } + /* d.s.high != 0 */ + if (n.s.low == 0) + { + /* K 0 + * --- + * K 0 + */ + if (rem) + { + r.s.high = n.s.high % d.s.high; + r.s.low = 0; + *rem = r.all; + } + return n.s.high / d.s.high; + } + /* K K + * --- + * K 0 + */ + if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + { + r.s.low = n.s.low; + r.s.high = n.s.high & (d.s.high - 1); + *rem = r.all; + } + return n.s.high >> __builtin_ctz(d.s.high); + } + /* K K + * --- + * K 0 + */ + sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high); + /* 0 <= sr <= n_uword_bits - 2 or sr large */ + if (sr > n_uword_bits - 2) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_uword_bits - 1 */ + /* q.all = n.all << (n_udword_bits - sr); */ + q.s.low = 0; + q.s.high = n.s.low << (n_uword_bits - sr); + /* r.all = n.all >> sr; */ + r.s.high = n.s.high >> sr; + r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + } + else /* d.s.low != 0 */ + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 K + */ + if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + *rem = n.s.low & (d.s.low - 1); + if (d.s.low == 1) + return n.all; + sr = __builtin_ctz(d.s.low); + q.s.high = n.s.high >> sr; + q.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + return q.all; + } + /* K X + * --- + *0 K + */ + sr = 1 + n_uword_bits + __builtin_clz(d.s.low) - __builtin_clz(n.s.high); + /* 2 <= sr <= n_udword_bits - 1 + * q.all = n.all << (n_udword_bits - sr); + * r.all = n.all >> sr; + * if (sr == n_uword_bits) + * { + * q.s.low = 0; + * q.s.high = n.s.low; + * r.s.high = 0; + * r.s.low = n.s.high; + * } + * else if (sr < n_uword_bits) // 2 <= sr <= n_uword_bits - 1 + * { + * q.s.low = 0; + * q.s.high = n.s.low << (n_uword_bits - sr); + * r.s.high = n.s.high >> sr; + * r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + * } + * else // n_uword_bits + 1 <= sr <= n_udword_bits - 1 + * { + * q.s.low = n.s.low << (n_udword_bits - sr); + * q.s.high = (n.s.high << (n_udword_bits - sr)) | + * (n.s.low >> (sr - n_uword_bits)); + * r.s.high = 0; + * r.s.low = n.s.high >> (sr - n_uword_bits); + * } + */ + q.s.low = (n.s.low << (n_udword_bits - sr)) & + ((si_int)(n_uword_bits - sr) >> (n_uword_bits-1)); + q.s.high = ((n.s.low << ( n_uword_bits - sr)) & + ((si_int)(sr - n_uword_bits - 1) >> (n_uword_bits-1))) | + (((n.s.high << (n_udword_bits - sr)) | + (n.s.low >> (sr - n_uword_bits))) & + ((si_int)(n_uword_bits - sr) >> (n_uword_bits-1))); + r.s.high = (n.s.high >> sr) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1)); + r.s.low = ((n.s.high >> (sr - n_uword_bits)) & + ((si_int)(n_uword_bits - sr - 1) >> (n_uword_bits-1))) | + (((n.s.high << (n_uword_bits - sr)) | + (n.s.low >> sr)) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1))); + } + else + { + /* K X + * --- + * K K + */ + sr = __builtin_clz(d.s.high) - __builtin_clz(n.s.high); + /* 0 <= sr <= n_uword_bits - 1 or sr large */ + if (sr > n_uword_bits - 1) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_uword_bits */ + /* q.all = n.all << (n_udword_bits - sr); */ + q.s.low = 0; + q.s.high = n.s.low << (n_uword_bits - sr); + /* r.all = n.all >> sr; + * if (sr < n_uword_bits) + * { + * r.s.high = n.s.high >> sr; + * r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr); + * } + * else + * { + * r.s.high = 0; + * r.s.low = n.s.high; + * } + */ + r.s.high = (n.s.high >> sr) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1)); + r.s.low = (n.s.high << (n_uword_bits - sr)) | + ((n.s.low >> sr) & + ((si_int)(sr - n_uword_bits) >> (n_uword_bits-1))); + } + } + /* Not a special case + * q and r are initialized with: + * q.all = n.all << (n_udword_bits - sr); + * r.all = n.all >> sr; + * 1 <= sr <= n_udword_bits - 1 + */ + su_int carry = 0; + for (; sr > 0; --sr) + { + /* r:q = ((r:q) << 1) | carry */ + r.s.high = (r.s.high << 1) | (r.s.low >> (n_uword_bits - 1)); + r.s.low = (r.s.low << 1) | (q.s.high >> (n_uword_bits - 1)); + q.s.high = (q.s.high << 1) | (q.s.low >> (n_uword_bits - 1)); + q.s.low = (q.s.low << 1) | carry; + /* carry = 0; + * if (r.all >= d.all) + * { + * r.all -= d.all; + * carry = 1; + * } + */ + const di_int s = (di_int)(d.all - r.all - 1) >> (n_udword_bits - 1); + carry = s & 1; + r.all -= d.all & s; + } + q.all = (q.all << 1) | carry; + if (rem) + *rem = r.all; + return q.all; +} diff --git a/projects/compiler-rt/lib/udivmodsi4.c b/projects/compiler-rt/lib/udivmodsi4.c new file mode 100644 index 00000000..5b49089f --- /dev/null +++ b/projects/compiler-rt/lib/udivmodsi4.c @@ -0,0 +1,30 @@ +/*===-- udivmodsi4.c - Implement __udivmodsi4 ------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivmodsi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +extern su_int COMPILER_RT_ABI __udivsi3(su_int n, su_int d); + + +/* Returns: a / b, *rem = a % b */ + +COMPILER_RT_ABI su_int +__udivmodsi4(su_int a, su_int b, su_int* rem) +{ + si_int d = __udivsi3(a,b); + *rem = a - (d*b); + return d; +} + + diff --git a/projects/compiler-rt/lib/udivmodti4.c b/projects/compiler-rt/lib/udivmodti4.c new file mode 100644 index 00000000..f619c749 --- /dev/null +++ b/projects/compiler-rt/lib/udivmodti4.c @@ -0,0 +1,256 @@ +/* ===-- udivmodti4.c - Implement __udivmodti4 -----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivmodti4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +/* Effects: if rem != 0, *rem = a % b + * Returns: a / b + */ + +/* Translated from Figure 3-40 of The PowerPC Compiler Writer's Guide */ + +tu_int +__udivmodti4(tu_int a, tu_int b, tu_int* rem) +{ + const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT; + const unsigned n_utword_bits = sizeof(tu_int) * CHAR_BIT; + utwords n; + n.all = a; + utwords d; + d.all = b; + utwords q; + utwords r; + unsigned sr; + /* special cases, X is unknown, K != 0 */ + if (n.s.high == 0) + { + if (d.s.high == 0) + { + /* 0 X + * --- + * 0 X + */ + if (rem) + *rem = n.s.low % d.s.low; + return n.s.low / d.s.low; + } + /* 0 X + * --- + * K X + */ + if (rem) + *rem = n.s.low; + return 0; + } + /* n.s.high != 0 */ + if (d.s.low == 0) + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 0 + */ + if (rem) + *rem = n.s.high % d.s.low; + return n.s.high / d.s.low; + } + /* d.s.high != 0 */ + if (n.s.low == 0) + { + /* K 0 + * --- + * K 0 + */ + if (rem) + { + r.s.high = n.s.high % d.s.high; + r.s.low = 0; + *rem = r.all; + } + return n.s.high / d.s.high; + } + /* K K + * --- + * K 0 + */ + if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + { + r.s.low = n.s.low; + r.s.high = n.s.high & (d.s.high - 1); + *rem = r.all; + } + return n.s.high >> __builtin_ctzll(d.s.high); + } + /* K K + * --- + * K 0 + */ + sr = __builtin_clzll(d.s.high) - __builtin_clzll(n.s.high); + /* 0 <= sr <= n_udword_bits - 2 or sr large */ + if (sr > n_udword_bits - 2) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_udword_bits - 1 */ + /* q.all = n.all << (n_utword_bits - sr); */ + q.s.low = 0; + q.s.high = n.s.low << (n_udword_bits - sr); + /* r.all = n.all >> sr; */ + r.s.high = n.s.high >> sr; + r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + } + else /* d.s.low != 0 */ + { + if (d.s.high == 0) + { + /* K X + * --- + * 0 K + */ + if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */ + { + if (rem) + *rem = n.s.low & (d.s.low - 1); + if (d.s.low == 1) + return n.all; + sr = __builtin_ctzll(d.s.low); + q.s.high = n.s.high >> sr; + q.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + return q.all; + } + /* K X + * --- + * 0 K + */ + sr = 1 + n_udword_bits + __builtin_clzll(d.s.low) + - __builtin_clzll(n.s.high); + /* 2 <= sr <= n_utword_bits - 1 + * q.all = n.all << (n_utword_bits - sr); + * r.all = n.all >> sr; + * if (sr == n_udword_bits) + * { + * q.s.low = 0; + * q.s.high = n.s.low; + * r.s.high = 0; + * r.s.low = n.s.high; + * } + * else if (sr < n_udword_bits) // 2 <= sr <= n_udword_bits - 1 + * { + * q.s.low = 0; + * q.s.high = n.s.low << (n_udword_bits - sr); + * r.s.high = n.s.high >> sr; + * r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + * } + * else // n_udword_bits + 1 <= sr <= n_utword_bits - 1 + * { + * q.s.low = n.s.low << (n_utword_bits - sr); + * q.s.high = (n.s.high << (n_utword_bits - sr)) | + * (n.s.low >> (sr - n_udword_bits)); + * r.s.high = 0; + * r.s.low = n.s.high >> (sr - n_udword_bits); + * } + */ + q.s.low = (n.s.low << (n_utword_bits - sr)) & + ((di_int)(int)(n_udword_bits - sr) >> (n_udword_bits-1)); + q.s.high = ((n.s.low << ( n_udword_bits - sr)) & + ((di_int)(int)(sr - n_udword_bits - 1) >> (n_udword_bits-1))) | + (((n.s.high << (n_utword_bits - sr)) | + (n.s.low >> (sr - n_udword_bits))) & + ((di_int)(int)(n_udword_bits - sr) >> (n_udword_bits-1))); + r.s.high = (n.s.high >> sr) & + ((di_int)(int)(sr - n_udword_bits) >> (n_udword_bits-1)); + r.s.low = ((n.s.high >> (sr - n_udword_bits)) & + ((di_int)(int)(n_udword_bits - sr - 1) >> (n_udword_bits-1))) | + (((n.s.high << (n_udword_bits - sr)) | + (n.s.low >> sr)) & + ((di_int)(int)(sr - n_udword_bits) >> (n_udword_bits-1))); + } + else + { + /* K X + * --- + * K K + */ + sr = __builtin_clzll(d.s.high) - __builtin_clzll(n.s.high); + /*0 <= sr <= n_udword_bits - 1 or sr large */ + if (sr > n_udword_bits - 1) + { + if (rem) + *rem = n.all; + return 0; + } + ++sr; + /* 1 <= sr <= n_udword_bits */ + /* q.all = n.all << (n_utword_bits - sr); */ + q.s.low = 0; + q.s.high = n.s.low << (n_udword_bits - sr); + /* r.all = n.all >> sr; + * if (sr < n_udword_bits) + * { + * r.s.high = n.s.high >> sr; + * r.s.low = (n.s.high << (n_udword_bits - sr)) | (n.s.low >> sr); + * } + * else + * { + * r.s.high = 0; + * r.s.low = n.s.high; + * } + */ + r.s.high = (n.s.high >> sr) & + ((di_int)(int)(sr - n_udword_bits) >> (n_udword_bits-1)); + r.s.low = (n.s.high << (n_udword_bits - sr)) | + ((n.s.low >> sr) & + ((di_int)(int)(sr - n_udword_bits) >> (n_udword_bits-1))); + } + } + /* Not a special case + * q and r are initialized with: + * q.all = n.all << (n_utword_bits - sr); + * r.all = n.all >> sr; + * 1 <= sr <= n_utword_bits - 1 + */ + su_int carry = 0; + for (; sr > 0; --sr) + { + /* r:q = ((r:q) << 1) | carry */ + r.s.high = (r.s.high << 1) | (r.s.low >> (n_udword_bits - 1)); + r.s.low = (r.s.low << 1) | (q.s.high >> (n_udword_bits - 1)); + q.s.high = (q.s.high << 1) | (q.s.low >> (n_udword_bits - 1)); + q.s.low = (q.s.low << 1) | carry; + /* carry = 0; + * if (r.all >= d.all) + * { + * r.all -= d.all; + * carry = 1; + * } + */ + const ti_int s = (ti_int)(d.all - r.all - 1) >> (n_utword_bits - 1); + carry = s & 1; + r.all -= d.all & s; + } + q.all = (q.all << 1) | carry; + if (rem) + *rem = r.all; + return q.all; +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/udivsi3.c b/projects/compiler-rt/lib/udivsi3.c new file mode 100644 index 00000000..5d0140cc --- /dev/null +++ b/projects/compiler-rt/lib/udivsi3.c @@ -0,0 +1,66 @@ +/* ===-- udivsi3.c - Implement __udivsi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivsi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a / b */ + +/* Translated from Figure 3-40 of The PowerPC Compiler Writer's Guide */ + +ARM_EABI_FNALIAS(uidiv, udivsi3) + +/* This function should not call __divsi3! */ +COMPILER_RT_ABI su_int +__udivsi3(su_int n, su_int d) +{ + const unsigned n_uword_bits = sizeof(su_int) * CHAR_BIT; + su_int q; + su_int r; + unsigned sr; + /* special cases */ + if (d == 0) + return 0; /* ?! */ + if (n == 0) + return 0; + sr = __builtin_clz(d) - __builtin_clz(n); + /* 0 <= sr <= n_uword_bits - 1 or sr large */ + if (sr > n_uword_bits - 1) /* d > r */ + return 0; + if (sr == n_uword_bits - 1) /* d == 1 */ + return n; + ++sr; + /* 1 <= sr <= n_uword_bits - 1 */ + /* Not a special case */ + q = n << (n_uword_bits - sr); + r = n >> sr; + su_int carry = 0; + for (; sr > 0; --sr) + { + /* r:q = ((r:q) << 1) | carry */ + r = (r << 1) | (q >> (n_uword_bits - 1)); + q = (q << 1) | carry; + /* carry = 0; + * if (r.all >= d.all) + * { + * r.all -= d.all; + * carry = 1; + * } + */ + const si_int s = (si_int)(d - r - 1) >> (n_uword_bits - 1); + carry = s & 1; + r -= d & s; + } + q = (q << 1) | carry; + return q; +} diff --git a/projects/compiler-rt/lib/udivti3.c b/projects/compiler-rt/lib/udivti3.c new file mode 100644 index 00000000..d9e1bb46 --- /dev/null +++ b/projects/compiler-rt/lib/udivti3.c @@ -0,0 +1,29 @@ +/* ===-- udivti3.c - Implement __udivti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __udivti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); + +/* Returns: a / b */ + +tu_int +__udivti3(tu_int a, tu_int b) +{ + return __udivmodti4(a, b, 0); +} + +#endif /* __x86_64 */ diff --git a/projects/compiler-rt/lib/umoddi3.c b/projects/compiler-rt/lib/umoddi3.c new file mode 100644 index 00000000..3541ab6e --- /dev/null +++ b/projects/compiler-rt/lib/umoddi3.c @@ -0,0 +1,27 @@ +/* ===-- umoddi3.c - Implement __umoddi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __umoddi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +du_int COMPILER_RT_ABI __udivmoddi4(du_int a, du_int b, du_int* rem); + +/* Returns: a % b */ + +COMPILER_RT_ABI du_int +__umoddi3(du_int a, du_int b) +{ + du_int r; + __udivmoddi4(a, b, &r); + return r; +} diff --git a/projects/compiler-rt/lib/umodsi3.c b/projects/compiler-rt/lib/umodsi3.c new file mode 100644 index 00000000..aae741d8 --- /dev/null +++ b/projects/compiler-rt/lib/umodsi3.c @@ -0,0 +1,25 @@ +/* ===-- umodsi3.c - Implement __umodsi3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __umodsi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a % b */ + +su_int COMPILER_RT_ABI __udivsi3(su_int a, su_int b); + +COMPILER_RT_ABI su_int +__umodsi3(su_int a, su_int b) +{ + return a - __udivsi3(a, b) * b; +} diff --git a/projects/compiler-rt/lib/umodti3.c b/projects/compiler-rt/lib/umodti3.c new file mode 100644 index 00000000..8ebe7f0d --- /dev/null +++ b/projects/compiler-rt/lib/umodti3.c @@ -0,0 +1,31 @@ +/* ===-- umodti3.c - Implement __umodti3 -----------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __umodti3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +#if __x86_64 + +tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); + +/* Returns: a % b */ + +tu_int +__umodti3(tu_int a, tu_int b) +{ + tu_int r; + __udivmodti4(a, b, &r); + return r; +} + +#endif diff --git a/projects/compiler-rt/lib/x86_64/Makefile.mk b/projects/compiler-rt/lib/x86_64/Makefile.mk new file mode 100644 index 00000000..ee3f9ce8 --- /dev/null +++ b/projects/compiler-rt/lib/x86_64/Makefile.mk @@ -0,0 +1,20 @@ +#===- lib/x86_64/Makefile.mk -------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := builtins +SubDirs := +OnlyArchs := x86_64 + +AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file))) +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) +ObjNames := $(Sources:%.c=%.o) $(AsmSources:%.S=%.o) +Implementation := Optimized + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard lib/*.h $(Dir)/*.h) diff --git a/projects/compiler-rt/lib/x86_64/floatdidf.c b/projects/compiler-rt/lib/x86_64/floatdidf.c new file mode 100644 index 00000000..388404e5 --- /dev/null +++ b/projects/compiler-rt/lib/x86_64/floatdidf.c @@ -0,0 +1,16 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* double __floatdidf(di_int a); */ + +#ifdef __x86_64__ + +#include "../int_lib.h" + +double __floatdidf(int64_t a) +{ + return (double)a; +} + +#endif /* __x86_64__ */ diff --git a/projects/compiler-rt/lib/x86_64/floatdisf.c b/projects/compiler-rt/lib/x86_64/floatdisf.c new file mode 100644 index 00000000..96c3728e --- /dev/null +++ b/projects/compiler-rt/lib/x86_64/floatdisf.c @@ -0,0 +1,14 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +#ifdef __x86_64__ + +#include "../int_lib.h" + +float __floatdisf(int64_t a) +{ + return (float)a; +} + +#endif /* __x86_64__ */ diff --git a/projects/compiler-rt/lib/x86_64/floatdixf.c b/projects/compiler-rt/lib/x86_64/floatdixf.c new file mode 100644 index 00000000..c01193a8 --- /dev/null +++ b/projects/compiler-rt/lib/x86_64/floatdixf.c @@ -0,0 +1,16 @@ +/* This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + */ + +/* long double __floatdixf(di_int a); */ + +#ifdef __x86_64__ + +#include "../int_lib.h" + +long double __floatdixf(int64_t a) +{ + return (long double)a; +} + +#endif /* __i386__ */ diff --git a/projects/compiler-rt/lib/x86_64/floatundidf.S b/projects/compiler-rt/lib/x86_64/floatundidf.S new file mode 100644 index 00000000..1be553b7 --- /dev/null +++ b/projects/compiler-rt/lib/x86_64/floatundidf.S @@ -0,0 +1,43 @@ +//===-- floatundidf.S - Implement __floatundidf for x86_64 ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements __floatundidf for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// double __floatundidf(du_int a); + +#ifdef __x86_64__ + +#ifndef __ELF__ +.const +#endif +.align 4 +twop52: .quad 0x4330000000000000 +twop84_plus_twop52: + .quad 0x4530000000100000 +twop84: .quad 0x4530000000000000 + +#define REL_ADDR(_a) (_a)(%rip) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundidf) + movd %edi, %xmm0 // low 32 bits of a + shrq $32, %rdi // high 32 bits of a + orq REL_ADDR(twop84), %rdi // 0x1p84 + a_hi (no rounding occurs) + orpd REL_ADDR(twop52), %xmm0 // 0x1p52 + a_lo (no rounding occurs) + movd %rdi, %xmm1 + subsd REL_ADDR(twop84_plus_twop52), %xmm1 // a_hi - 0x1p52 (no rounding occurs) + addsd %xmm1, %xmm0 // a_hi + a_lo (round happens here) + ret + +#endif // __x86_64__ diff --git a/projects/compiler-rt/lib/x86_64/floatundisf.S b/projects/compiler-rt/lib/x86_64/floatundisf.S new file mode 100644 index 00000000..89d3f07d --- /dev/null +++ b/projects/compiler-rt/lib/x86_64/floatundisf.S @@ -0,0 +1,33 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// float __floatundisf(du_int a); + +#ifdef __x86_64__ + +#ifndef __ELF__ +.literal4 +#endif +two: .single 2.0 + +#define REL_ADDR(_a) (_a)(%rip) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundisf) + movq $1, %rsi + testq %rdi, %rdi + js 1f + cvtsi2ssq %rdi, %xmm0 + ret + +1: andq %rdi, %rsi + shrq %rdi + orq %rsi, %rdi + cvtsi2ssq %rdi, %xmm0 + mulss REL_ADDR(two), %xmm0 + ret + +#endif // __x86_64__ diff --git a/projects/compiler-rt/lib/x86_64/floatundixf.S b/projects/compiler-rt/lib/x86_64/floatundixf.S new file mode 100644 index 00000000..a7243f2c --- /dev/null +++ b/projects/compiler-rt/lib/x86_64/floatundixf.S @@ -0,0 +1,62 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// long double __floatundixf(du_int a); + +#ifdef __x86_64__ + +#ifndef __ELF__ +.const +#endif +.align 4 +twop64: .quad 0x43f0000000000000 + +#define REL_ADDR(_a) (_a)(%rip) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundixf) + movq %rdi, -8(%rsp) + fildq -8(%rsp) + test %rdi, %rdi + js 1f + ret +1: faddl REL_ADDR(twop64) + ret + +#endif // __x86_64__ + + +/* Branch-free implementation is ever so slightly slower, but more beautiful. + It is likely superior for inlining, so I kept it around for future reference. + +#ifdef __x86_64__ + +.const +.align 4 +twop52: .quad 0x4330000000000000 +twop84_plus_twop52_neg: + .quad 0xc530000000100000 +twop84: .quad 0x4530000000000000 + +#define REL_ADDR(_a) (_a)(%rip) + +.text +.align 4 +DEFINE_COMPILERRT_FUNCTION(__floatundixf) + movl %edi, %esi // low 32 bits of input + shrq $32, %rdi // hi 32 bits of input + orq REL_ADDR(twop84), %rdi // 2^84 + hi (as a double) + orq REL_ADDR(twop52), %rsi // 2^52 + lo (as a double) + movq %rdi, -8(%rsp) + movq %rsi, -16(%rsp) + fldl REL_ADDR(twop84_plus_twop52_neg) + faddl -8(%rsp) // hi - 2^52 (as double extended, no rounding occurs) + faddl -16(%rsp) // hi + lo (as double extended) + ret + +#endif // __x86_64__ + +*/ diff --git a/projects/compiler-rt/make/AppleBI.mk b/projects/compiler-rt/make/AppleBI.mk new file mode 100644 index 00000000..b2361521 --- /dev/null +++ b/projects/compiler-rt/make/AppleBI.mk @@ -0,0 +1,149 @@ + +# +# Make rules to build compiler_rt in Apple B&I infrastructure +# + +# set ProjSrcRoot appropriately +ProjSrcRoot := $(SRCROOT) +# set ProjObjRoot appropriately +ifdef OBJROOT + ProjObjRoot := $(OBJROOT) +else + ProjObjRoot := $(ProjSrcRoot) +endif + +ifeq (,$(RC_PURPLE)) + INSTALL_TARGET = install-MacOSX +else + ifeq (,$(RC_INDIGO)) + INSTALL_TARGET = install-iOS + else + INSTALL_TARGET = install-iOS-Simulator + endif +endif + + + +# Log full compile lines in B&I logs and omit summary lines. +Verb := +Summary := @true + +# List of functions needed for each architecture. + +# Copies any public headers to DSTROOT. +installhdrs: + + +# Copies source code to SRCROOT. +installsrc: + cp -r . $(SRCROOT) + + +install: $(INSTALL_TARGET) + +# Copy results to DSTROOT. +install-MacOSX : $(SYMROOT)/libcompiler_rt.dylib \ + $(SYMROOT)/libcompiler_rt-dyld.a + mkdir -p $(DSTROOT)/usr/local/lib/dyld + cp $(SYMROOT)/libcompiler_rt-dyld.a \ + $(DSTROOT)/usr/local/lib/dyld/libcompiler_rt.a + mkdir -p $(DSTROOT)/usr/lib/system + $(call GetCNAVar,STRIP,Platform.darwin_bni,Release,) -S $(SYMROOT)/libcompiler_rt.dylib \ + -o $(DSTROOT)/usr/lib/system/libcompiler_rt.dylib + cd $(DSTROOT)/usr/lib/system; \ + ln -s libcompiler_rt.dylib libcompiler_rt_profile.dylib; \ + ln -s libcompiler_rt.dylib libcompiler_rt_debug.dylib + +# Rule to make each dylib slice +$(OBJROOT)/libcompiler_rt-%.dylib : $(OBJROOT)/darwin_bni/Release/%/libcompiler_rt.a + echo "const char vers[] = \"@(#) $(RC_ProjectName)-$(RC_ProjectSourceVersion)\"; " > $(OBJROOT)/version.c + $(call GetCNAVar,CC,Platform.darwin_bni,Release,$*) \ + $(OBJROOT)/version.c -arch $* -dynamiclib \ + -install_name /usr/lib/system/libcompiler_rt.dylib \ + -compatibility_version 1 -current_version $(RC_ProjectSourceVersion) \ + -nodefaultlibs -umbrella System -dead_strip \ + -Wl,-upward-lunwind \ + -Wl,-upward-lsystem_m \ + -Wl,-upward-lsystem_c \ + -Wl,-upward-lsystem_kernel \ + -Wl,-upward-lsystem_platform \ + -Wl,-ldyld \ + -L$(SDKROOT)/usr/lib/system \ + $(DYLIB_FLAGS) -Wl,-force_load,$^ -o $@ + +# Rule to make fat dylib +$(SYMROOT)/libcompiler_rt.dylib: $(foreach arch,$(filter-out armv4t,$(RC_ARCHS)), \ + $(OBJROOT)/libcompiler_rt-$(arch).dylib) + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ + $(call GetCNAVar,DSYMUTIL,Platform.darwin_bni,Release,) $@ + + +# Copy results to DSTROOT. +install-iOS: $(SYMROOT)/libcompiler_rt-static.a \ + $(SYMROOT)/libcompiler_rt-dyld.a \ + $(SYMROOT)/libcompiler_rt.dylib + mkdir -p $(DSTROOT)/usr/local/lib + cp $(SYMROOT)/libcompiler_rt-static.a \ + $(DSTROOT)/usr/local/lib/libcompiler_rt-static.a + mkdir -p $(DSTROOT)/usr/local/lib/dyld + cp $(SYMROOT)/libcompiler_rt-dyld.a \ + $(DSTROOT)/usr/local/lib/dyld/libcompiler_rt.a + mkdir -p $(DSTROOT)/usr/lib/system + $(call GetCNAVar,STRIP,Platform.darwin_bni,Release,) -S $(SYMROOT)/libcompiler_rt.dylib \ + -o $(DSTROOT)/usr/lib/system/libcompiler_rt.dylib + +# Rule to make fat archive +$(SYMROOT)/libcompiler_rt-static.a : $(foreach arch,$(RC_ARCHS), \ + $(OBJROOT)/darwin_bni/Static/$(arch)/libcompiler_rt.a) + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ + +# rule to make each archive slice for dyld (which removes a few archive members) +$(OBJROOT)/libcompiler_rt-dyld-%.a : $(OBJROOT)/darwin_bni/Release/%/libcompiler_rt.a + cp $^ $@ + DEL_LIST=`$(AR) -t $@ | egrep 'apple_versioning|gcc_personality_v0|eprintf' | xargs echo` ; \ + if [ -n "$${DEL_LIST}" ] ; \ + then \ + $(call GetCNAVar,AR,Platform.darwin_bni,Release,) -d $@ $${DEL_LIST}; \ + $(call GetCNAVar,RANLIB,Platform.darwin_bni,Release,) $@ ; \ + fi + +# rule to make make archive for dyld +$(SYMROOT)/libcompiler_rt-dyld.a : $(foreach arch,$(RC_ARCHS), \ + $(OBJROOT)/libcompiler_rt-dyld-$(arch).a) + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ + + + +# Copy results to DSTROOT. +install-iOS-Simulator: $(SYMROOT)/libcompiler_rt_sim.dylib \ + $(SYMROOT)/libcompiler_rt-dyld.a + mkdir -p $(DSTROOT)/$(SDKROOT)/usr/lib/system + $(call GetCNAVar,STRIP,Platform.darwin_bni,Release,) -S $(SYMROOT)/libcompiler_rt_sim.dylib \ + -o $(DSTROOT)/$(SDKROOT)/usr/lib/system/libcompiler_rt_sim.dylib + mkdir -p $(DSTROOT)/$(SDKROOT)/usr/local/lib/dyld + cp $(SYMROOT)/libcompiler_rt-dyld.a \ + $(DSTROOT)/$(SDKROOT)/usr/local/lib/dyld/libcompiler_rt.a + +# Rule to make fat dylib +$(SYMROOT)/libcompiler_rt_sim.dylib: $(foreach arch,$(RC_ARCHS), \ + $(OBJROOT)/libcompiler_rt_sim-$(arch).dylib) + $(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@ + $(call GetCNAVar,DSYMUTIL,Platform.darwin_bni,Release,) $@ + +# Rule to make each dylib slice +$(OBJROOT)/libcompiler_rt_sim-%.dylib : $(OBJROOT)/darwin_bni/Release/%/libcompiler_rt.a + echo "const char vers[] = \"@(#) $(RC_ProjectName)-$(RC_ProjectSourceVersion)\"; " > $(OBJROOT)/version.c + $(call GetCNAVar,CC,Platform.darwin_bni,Release,$*) \ + $(OBJROOT)/version.c -arch $* -dynamiclib \ + -install_name /usr/lib/system/libcompiler_rt_sim.dylib \ + -compatibility_version 1 -current_version $(RC_ProjectSourceVersion) \ + -Wl,-unexported_symbol,___enable_execute_stack \ + -nostdlib \ + -Wl,-upward-lunwind_sim \ + -Wl,-upward-lsystem_sim_m \ + -Wl,-upward-lsystem_sim_c \ + -ldyld_sim \ + -Wl,-upward-lSystem \ + -umbrella System -Wl,-no_implicit_dylibs -L$(SDKROOT)/usr/lib/system -dead_strip \ + $(DYLIB_FLAGS) -Wl,-force_load,$^ -o $@ + diff --git a/projects/compiler-rt/make/config.mk b/projects/compiler-rt/make/config.mk new file mode 100644 index 00000000..6398d058 --- /dev/null +++ b/projects/compiler-rt/make/config.mk @@ -0,0 +1,47 @@ +### +# Configuration variables. + +OS := $(shell uname) + +# Assume make is always run from top-level of source directory. Note than an +# Apple style build overrides these variables later in the makefile. +ProjSrcRoot := $(shell pwd) +ProjObjRoot := $(ProjSrcRoot) + +# The list of modules which are required to be built into every library. This +# should only be used for internal utilities which could be used in any other +# module. Any other cases the platform should be allowed to opt-in to. +AlwaysRequiredModules := int_util + +### +# Tool configuration variables. + +# FIXME: LLVM uses autoconf/mkinstalldirs ? +MKDIR := mkdir -p +DATE := date +LIPO := lipo +CP := cp +DSYMUTIL := dsymutil + +VERBOSE := 0 +DEBUGMAKE := + +### +# Automatic and derived variables. + +# Adjust settings for verbose mode +ifneq ($(VERBOSE),1) + Verb := @ +else + Verb := +endif + +Echo := @echo +ifndef Summary + Summary = $(Echo) +endif + +### +# Common compiler options +COMMON_CXXFLAGS=-fno-exceptions -fPIC -funwind-tables -I${ProjSrcRoot}/lib -I${ProjSrcRoot}/include +COMMON_CFLAGS=-fPIC diff --git a/projects/compiler-rt/make/filter-inputs b/projects/compiler-rt/make/filter-inputs new file mode 100755 index 00000000..8a6bbe2a --- /dev/null +++ b/projects/compiler-rt/make/filter-inputs @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +#===- make/filter-inputs ---------------------------------------------------===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +# Given a list of files, return a new list of files taking only the +# first file for any particular filename. +def main(): + import os,sys + + seen = set() + for file in sys.argv[1:]: + base = os.path.basename(file) + if base not in seen: + seen.add(base) + print file + +if __name__ == '__main__': + main() diff --git a/projects/compiler-rt/make/lib_info.mk b/projects/compiler-rt/make/lib_info.mk new file mode 100644 index 00000000..31850f78 --- /dev/null +++ b/projects/compiler-rt/make/lib_info.mk @@ -0,0 +1,59 @@ +# compiler-rt Library Info +# +# This should be included once the subdirectory information has been loaded, and +# uses the utilities in 'util.mk'. +# +# This defines the following variables describing compiler-rt: +# AvailableFunctions - The entire list of function names (unmangled) the +# library can provide. +# CommonFunctions - The list of generic functions available. +# ArchFunctions. - The list of functions commonly available for +# 'arch'. This does not include any config specific +# functions. +# +# AvailableIn. - The list of subdir keys where 'function' is +# defined. + +# Determine the set of available modules. +AvailableModules := $(sort $(foreach key,$(SubDirKeys),\ + $($(key).ModuleName))) + +# Build a per-module map of subdir keys. +$(foreach key,$(SubDirKeys),\ + $(call Append,ModuleSubDirKeys.$($(key).ModuleName),$(key))) + +AvailableArchs := $(sort $(foreach key,$(SubDirKeys),\ + $($(key).OnlyArchs))) + +AvailableFunctions := $(sort $(foreach key,$(SubDirKeys),\ + $(basename $($(key).ObjNames)))) + +CommonFunctions := $(sort\ + $(foreach key,$(ModuleSubDirKeys.builtins),\ + $(if $(call strneq,,$(strip $($(key).OnlyArchs) $($(key).OnlyConfigs))),,\ + $(basename $($(key).ObjNames))))) + +# Compute common arch functions. +$(foreach key,$(ModuleSubDirKeys.builtins),\ + $(if $(call strneq,,$($(key).OnlyConfigs)),,\ + $(foreach arch,$($(key).OnlyArchs),\ + $(call Append,ArchFunctions.$(arch),$(sort \ + $(basename $($(key).ObjNames))))))) + +# Compute arch only functions. +$(foreach arch,$(AvailableArchs),\ + $(call Set,ArchFunctions.$(arch),$(sort $(ArchFunctions.$(arch))))\ + $(call Set,ArchOnlyFunctions.$(arch),\ + $(call set_difference,$(ArchFunctions.$(arch)),$(CommonFunctions)))) + +# Compute lists of where each function is available. +$(foreach key,$(SubDirKeys),\ + $(foreach fn,$(basename $($(key).ObjNames)),\ + $(call Append,AvailableIn.$(fn),$(key)))) + +# The names of all the available options. +AvailableOptions := AR ARFLAGS \ + CC CFLAGS LDFLAGS FUNCTIONS OPTIMIZED \ + RANLIB RANLIBFLAGS \ + VISIBILITY_HIDDEN KERNEL_USE \ + SHARED_LIBRARY SHARED_LIBRARY_SUFFIX STRIP LIPO DSYMUTIL diff --git a/projects/compiler-rt/make/lib_platforms.mk b/projects/compiler-rt/make/lib_platforms.mk new file mode 100644 index 00000000..9cf9704f --- /dev/null +++ b/projects/compiler-rt/make/lib_platforms.mk @@ -0,0 +1,82 @@ +# compiler-rt Configuration Support +# +# This should be included following 'lib_util.mk'. + +# The simple variables configurations can define. +PlainConfigVariables := Configs Description +PerConfigVariables := UniversalArchs Arch $(AvailableOptions) +RequiredConfigVariables := Configs Description + +### +# Load Platforms + +# Template: subdir_traverse_template subdir +define load_platform_template +$(call Set,PlatformName,$(basename $(notdir $(1)))) +ifneq ($(DEBUGMAKE),) + $$(info MAKE: $(PlatformName): Loading platform) +endif + +# Construct the variable key for this directory. +$(call Set,PlatformKey,Platform.$(PlatformName)) +$(call Append,PlatformKeys,$(PlatformKey)) +$(call Set,$(PlatformKey).Name,$(PlatformName)) +$(call Set,$(PlatformKey).Path,$(1)) + +# Reset platform specific variables to sentinel value. +$$(foreach var,$(PlainConfigVariables) $(PerConfigVariables),\ + $$(call Set,$$(var),UNDEFINED)) +$$(foreach var,$(PerConfigVariables),\ + $$(foreach config,$$(Configs),\ + $$(call Set,$$(var).$$(config),UNDEFINED))) +$$(foreach var,$(PerConfigVariables),\ + $$(foreach arch,$(AvailableArchs),\ + $$(call Set,$$(var).$$(arch),UNDEFINED))) + +# Get the platform variables. +include make/options.mk +include $(1) + +# Check for undefined required variables. +$$(foreach var,$(RequiredConfigVariables),\ + $$(if $$(call strneq,UNDEFINED,$$($$(var))),, \ + $$(error $(Dir): variable '$$(var)' was not undefined))) + +# Check that exactly one of UniversalArchs or Arch was defined. +$$(if $$(and $$(call strneq,UNDEFINED,$$(UniversalArchs)),\ + $$(call strneq,UNDEFINED,$$(Arch))),\ + $$(error '$(1)': cannot define both 'UniversalArchs' and 'Arch')) +$$(if $$(or $$(call strneq,UNDEFINED,$$(UniversalArchs)),\ + $$(call strneq,UNDEFINED,$$(Arch))),,\ + $$(error '$(1)': must define one of 'UniversalArchs' and 'Arch')) + +# Collect all the platform variables for subsequent use. +$$(foreach var,$(PlainConfigVariables) $(PerConfigVariables),\ + $$(if $$(call strneq,UNDEFINED,$$($$(var))),\ + $$(call CopyVariable,$$(var),$(PlatformKey).$$(var)))) +$$(foreach var,$(PerConfigVariables),\ + $$(foreach config,$$(Configs),\ + $$(if $$(call strneq,UNDEFINED,$$($$(var).$$(config))),\ + $$(call CopyVariable,$$(var).$$(config),$(PlatformKey).$$(var).$$(config))))\ + $$(foreach arch,$(AvailableArchs),\ + $$(if $$(call strneq,UNDEFINED,$$($$(var).$$(arch))),\ + $$(call CopyVariable,$$(var).$$(arch),$(PlatformKey).$$(var).$$(arch))))\ + $$(foreach config,$$(Configs),\ + $$(foreach arch,$(AvailableArchs),\ + $$(if $$(call strneq,UNDEFINED,$$($$(var).$$(config).$$(arch))),\ + $$(call CopyVariable,$$(var).$$(config).$$(arch),\ + $(PlatformKey).$$(var).$$(config).$$(arch)))))) + +ifneq ($(DEBUGMAKE),) + $$(info MAKE: $(PlatformName): Done loading platform) +endif +endef + +# Evaluate this now so we do not have to worry about order of evaluation. +PlatformFiles := $(wildcard make/platform/*.mk) +ifneq ($(DEBUGMAKE),) + $(info MAKE: Loading platforms: $(PlatformFiles)) +endif + +$(foreach file,$(PlatformFiles),\ + $(eval $(call load_platform_template,$(file)))) diff --git a/projects/compiler-rt/make/lib_util.mk b/projects/compiler-rt/make/lib_util.mk new file mode 100644 index 00000000..089a0e2e --- /dev/null +++ b/projects/compiler-rt/make/lib_util.mk @@ -0,0 +1,65 @@ +# Library Utility Functions +# +# This should be included following 'lib_info.mk'. + +# Function: GetCNAVar variable-name platform-key config arch +# +# Get a per-config-and-arch variable value. +GetCNAVar = $(strip \ + $(or $($(2).$(1).$(3).$(4)), \ + $($(2).$(1).$(3)), \ + $($(2).$(1).$(4)), \ + $($(2).$(1)))) + +# Function: SelectFunctionDir config arch function-name optimized +# +# Choose the appropriate implementation directory to use for 'function-name' in +# the configuration 'config' and on given arch. +SelectFunctionDir = $(strip \ + $(call Set,Tmp.SelectFunctionDir,$(call SelectFunctionDirs,$(1),$(2),$(3),$(4)))\ + $(if $(call streq,1,$(words $(Tmp.SelectFunctionDir))),\ + $(Tmp.SelectFunctionDir),\ + $(error SelectFunctionDir: invalid function name "$(3)" ($(strip\ + $(if $(call streq,0,$(words $(Tmp.SelectFunctionDir))),\ + no such function,\ + function implemented in multiple directories!!!)))))) + +# Helper functions that select the entire list of subdirs where a function is +# defined with a certain specificity. +SelectFunctionDirs_Opt_ConfigAndArch = $(strip \ + $(foreach key,$(AvailableIn.$(3)),\ + $(if $(and $(call streq,Optimized,$($(key).Implementation)),\ + $(call contains,$($(key).OnlyConfigs),$(1)),\ + $(call contains,$($(key).OnlyArchs),$(2))),$(key),))) +SelectFunctionDirs_Opt_Config = $(strip \ + $(foreach key,$(AvailableIn.$(3)),\ + $(if $(and $(call streq,Optimized,$($(key).Implementation)),\ + $(call contains,$($(key).OnlyConfigs),$(1))),$(key),))) +SelectFunctionDirs_Opt_Arch = $(strip \ + $(foreach key,$(AvailableIn.$(3)),\ + $(if $(and $(call streq,Optimized,$($(key).Implementation)),\ + $(call contains,$($(key).OnlyArchs),$(2))),$(key),))) +SelectFunctionDirs_Gen = $(strip \ + $(foreach key,$(AvailableIn.$(3)),\ + $(if $(call streq,Generic,$($(key).Implementation)),$(key)))) + +# Helper function to select the right set of dirs in generic priority order. +SelectFunctions_Gen = \ + $(or $(call SelectFunctionDirs_Gen,$(1),$(2),$(3)),\ + $(call SelectFunctionDirs_Opt_ConfigAndArch,$(1),$(2),$(3)), \ + $(call SelectFunctionDirs_Opt_Config,$(1),$(2),$(3)), \ + $(call SelectFunctionDirs_Opt_Arch,$(1),$(2),$(3))) + +# Helper function to select the right set of dirs in optimized priority order. +SelectFunctions_Opt = \ + $(or $(call SelectFunctionDirs_Opt_ConfigAndArch,$(1),$(2),$(3)), \ + $(call SelectFunctionDirs_Opt_Config,$(1),$(2),$(3)), \ + $(call SelectFunctionDirs_Opt_Arch,$(1),$(2),$(3)), \ + $(call SelectFunctionDirs_Gen,$(1),$(2),$(3))) + +# Helper function to select the right set of dirs (which should be exactly one) +# for a function. +SelectFunctionDirs = \ + $(if $(call streq,1,$(4)),\ + $(call SelectFunctions_Opt,$(1),$(2),$(3)),\ + $(call SelectFunctions_Gen,$(1),$(2),$(3))) diff --git a/projects/compiler-rt/make/options.mk b/projects/compiler-rt/make/options.mk new file mode 100644 index 00000000..67197de0 --- /dev/null +++ b/projects/compiler-rt/make/options.mk @@ -0,0 +1,48 @@ +# Options which may be overriden for platforms, etc. +# +# This list of such variables should be kept up to date with AvailableOptions in +# 'make/lib_info.mk'. + +# The compiler to use. +CC := gcc + +# The compiler flags to use. +CFLAGS := -Wall -Werror + +# The list of functions to include in the library. +FUNCTIONS := + +# Whether optimized function implementations should be used. +OPTIMIZED := 1 + +# Whether function definitions should use hidden visibility. This adds the +# -fvisibility=hidden compiler option and uses .private_extern annotations in +# assembly files. +# +# FIXME: Make this more portable. When that is done, it should probably be the +# default. +VISIBILITY_HIDDEN := 0 + +# Whether the library is being built for kernel use. +KERNEL_USE := 0 + +# Whether the library should be built as a shared object. +SHARED_LIBRARY := 0 + +# Miscellaneous tools. + +AR := ar +# FIXME: Remove these pipes once ranlib errors are fixed. +ARFLAGS := cru 2> /dev/null + +LDFLAGS := + +RANLIB := ranlib +# FIXME: Remove these pipes once ranlib errors are fixed. +RANLIBFLAGS := 2> /dev/null + +STRIP := strip +LIPO := lipo +DSYMUTIL := dsymutil + +SHARED_LIBRARY_SUFFIX := so diff --git a/projects/compiler-rt/make/platform/clang_darwin.mk b/projects/compiler-rt/make/platform/clang_darwin.mk new file mode 100644 index 00000000..ddb70294 --- /dev/null +++ b/projects/compiler-rt/make/platform/clang_darwin.mk @@ -0,0 +1,484 @@ +# These are the functions which clang needs when it is targetting a previous +# version of the OS. The issue is that the backend may use functions which were +# not present in the libgcc that shipped on the platform. In such cases, we link +# with a version of the library which contains private_extern definitions of all +# the extra functions which might be referenced. + +Description := Static runtime libraries for clang/Darwin. + +# A function that ensures we don't try to build for architectures that we +# don't have working toolchains for. +CheckArches = \ + $(shell \ + result=""; \ + for arch in $(1); do \ + if $(CC) -arch $$arch -c \ + -integrated-as \ + $(ProjSrcRoot)/make/platform/clang_darwin_test_input.c \ + -isysroot $(ProjSrcRoot)/SDKs/darwin \ + -o /dev/null > /dev/null 2> /dev/null; then \ + result="$$result$$arch "; \ + else \ + printf 1>&2 \ + "warning: clang_darwin.mk: dropping arch '$$arch' from lib '$(2)'\n"; \ + fi; \ + done; \ + echo $$result) + +XCRun = \ + $(shell \ + result=`xcrun -find $(1) 2> /dev/null`; \ + if [ "$$?" != "0" ]; then result=$(1); fi; \ + echo $$result) +XCRunSdkPath = \ + $(shell \ + result=`xcrun --sdk $(1) --show-sdk-path 2> /dev/null`; \ + if [ "$$?" != "0" ]; then result=""; fi; \ + echo $$result) +### + +CC := $(call XCRun,clang) +AR := $(call XCRun,ar) +RANLIB := $(call XCRun,ranlib) +STRIP := $(call XCRun,strip) +LIPO := $(call XCRun,lipo) +DSYMUTIL := $(call XCRun,dsymutil) + +Configs := +UniversalArchs := + +# Configuration solely for providing access to an eprintf symbol, which may +# still be referenced from Darwin system headers. This symbol is only ever +# needed on i386. +Configs += eprintf +UniversalArchs.eprintf := $(call CheckArches,i386,eprintf) + +# Configuration for targetting 10.4. We need a few functions missing from +# libgcc_s.10.4.dylib. We only build x86 slices since clang doesn't really +# support targetting PowerPC. +Configs += 10.4 +UniversalArchs.10.4 := $(call CheckArches,i386 x86_64,10.4) + +# Configuration for targetting iOS for a couple of functions that didn't +# make it into libSystem. +Configs += ios +UniversalArchs.ios := $(call CheckArches,i386 x86_64 armv7,ios) + +# Configuration for targetting OSX. These functions may not be in libSystem +# so we should provide our own. +Configs += osx +UniversalArchs.osx := $(call CheckArches,i386 x86_64,osx) + +# Configuration for use with kernel/kexts. +Configs += cc_kext +UniversalArchs.cc_kext := $(call CheckArches,armv7 i386 x86_64,cc_kext) + +# Configuration for use with kernel/kexts for iOS 5.0 and earlier (which used +# a different code generation strategy). +Configs += cc_kext_ios5 +UniversalArchs.cc_kext_ios5 := $(call CheckArches,x86_64 armv7,cc_kext_ios5) + +# Configurations which define the profiling support functions. +Configs += profile_osx +UniversalArchs.profile_osx := $(call CheckArches,i386 x86_64,profile_osx) +Configs += profile_ios +UniversalArchs.profile_ios := $(call CheckArches,i386 x86_64 armv7,profile_ios) + +# Configurations which define the ASAN support functions. +Configs += asan_osx_dynamic +UniversalArchs.asan_osx_dynamic := $(call CheckArches,i386 x86_64,asan_osx_dynamic) + +IOSSIM_SDK_PATH := $(call XCRunSdkPath,iphonesimulator) +ifneq ($(IOSSIM_SDK_PATH),) +Configs += asan_iossim_dynamic +UniversalArchs.asan_iossim_dynamic := $(call CheckArches,i386 x86_64,asan_iossim_dynamic) +endif + +Configs += ubsan_osx +UniversalArchs.ubsan_osx := $(call CheckArches,i386 x86_64,ubsan_osx) + +# Darwin 10.6 has a bug in cctools that makes it unable to use ranlib on our ARM +# object files. If we are on that platform, strip out all ARM archs. We still +# build the libraries themselves so that Clang can find them where it expects +# them, even though they might not have an expected slice. +ifneq ($(shell test -x /usr/bin/sw_vers && sw_vers -productVersion | grep 10.6),) +UniversalArchs.ios := $(filter-out armv7, $(UniversalArchs.ios)) +UniversalArchs.cc_kext := $(filter-out armv7, $(UniversalArchs.cc_kext)) +UniversalArchs.cc_kext_ios5 := $(filter-out armv7, $(UniversalArchs.cc_kext_ios5)) +UniversalArchs.profile_ios := $(filter-out armv7, $(UniversalArchs.profile_ios)) +endif + +# If RC_SUPPORTED_ARCHS is defined, treat it as a list of the architectures we +# are intended to support and limit what we try to build to that. +# +# We make sure to remove empty configs if we end up dropping all the requested +# archs for a particular config. +ifneq ($(RC_SUPPORTED_ARCHS),) +$(foreach config,$(Configs),\ + $(call Set,UniversalArchs.$(config),\ + $(filter $(RC_SUPPORTED_ARCHS),$(UniversalArchs.$(config))))\ + $(if $(UniversalArchs.$(config)),,\ + $(call Set,Configs,$(filter-out $(config),$(Configs))))) +endif + +### + +# Forcibly strip off any -arch, as that totally breaks our universal support. +override CC := $(subst -arch ,-arch_,$(CC)) +override CC := $(patsubst -arch_%,,$(CC)) + +CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer + +# Always set deployment target arguments for every build, these libraries should +# never depend on the environmental overrides. We simply set them to minimum +# supported deployment target -- nothing in the compiler-rt libraries should +# actually depend on the deployment target. +OSX_DEPLOYMENT_ARGS := -mmacosx-version-min=10.4 +IOS_DEPLOYMENT_ARGS := -mios-version-min=1.0 +IOS6_DEPLOYMENT_ARGS := -mios-version-min=6.0 +IOSSIM_DEPLOYMENT_ARGS := -mios-simulator-version-min=1.0 + +# Use our stub SDK as the sysroot to support more portable building. +OSX_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin +IOS_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin +IOS6_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin +IOSSIM_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin + +CFLAGS.eprintf := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.10.4 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +# FIXME: We can't build ASAN with our stub SDK yet. +CFLAGS.asan_osx_dynamic := \ + $(CFLAGS) -mmacosx-version-min=10.6 -fno-builtin \ + -gline-tables-only \ + -DMAC_INTERPOSE_FUNCTIONS=1 \ + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 + +CFLAGS.asan_iossim_dynamic := \ + $(CFLAGS) -mios-simulator-version-min=7.0 \ + -isysroot $(IOSSIM_SDK_PATH) \ + -fno-builtin \ + -gline-tables-only \ + -DMAC_INTERPOSE_FUNCTIONS=1 \ + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 + +CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.6 -fno-builtin + +CFLAGS.ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.ios.armv7f := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.osx.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.osx.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.armv7 := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.armv7f := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.armv7k := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS) +CFLAGS.cc_kext.armv7s := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS) +CFLAGS.cc_kext_ios5.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.cc_kext_ios5.armv7f := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.cc_kext_ios5.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.cc_kext_ios5.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.profile_osx.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.profile_osx.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.armv7f := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) +CFLAGS.profile_ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) + +# Configure the asan_osx_dynamic library to be built shared. +SHARED_LIBRARY.asan_osx_dynamic := 1 +LDFLAGS.asan_osx_dynamic := -lstdc++ -undefined dynamic_lookup + +# Configure the asan_iossim_dynamic library to be built shared. +SHARED_LIBRARY.asan_iossim_dynamic := 1 +# configure+make uses Clang, so we're using isysroot instead of --sysroot +# or -Wl,-syslibroot. +LDFLAGS.asan_iossim_dynamic := -undefined dynamic_lookup \ + -Wl,-ios_simulator_version_min,7.0.0 \ + -mios-simulator-version-min=7.0 -isysroot $(IOSSIM_SDK_PATH) + +FUNCTIONS.eprintf := eprintf +FUNCTIONS.10.4 := eprintf floatundidf floatundisf floatundixf + +FUNCTIONS.ios := divmodsi4 udivmodsi4 mulosi4 mulodi4 muloti4 +# On x86, the divmod functions reference divsi. +FUNCTIONS.ios.i386 := $(FUNCTIONS.ios) \ + divsi3 udivsi3 +FUNCTIONS.ios.x86_64 := $(FUNCTIONS.ios) \ + divsi3 udivsi3 + +FUNCTIONS.osx := mulosi4 mulodi4 muloti4 + +FUNCTIONS.profile_osx := GCDAProfiling +FUNCTIONS.profile_ios := GCDAProfiling + +FUNCTIONS.asan_osx_dynamic := $(AsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) \ + $(AsanDynamicFunctions) + +FUNCTIONS.asan_iossim_dynamic := $(AsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) \ + $(AsanDynamicFunctions) + +FUNCTIONS.ubsan_osx := $(UbsanFunctions) $(UbsanCXXFunctions) \ + $(SanitizerCommonFunctions) + +CCKEXT_COMMON_FUNCTIONS := \ + absvdi2 \ + absvsi2 \ + addvdi3 \ + addvsi3 \ + ashldi3 \ + ashrdi3 \ + bswapdi2 \ + bswapsi2 \ + clzdi2 \ + clzsi2 \ + cmpdi2 \ + ctzdi2 \ + ctzsi2 \ + divdc3 \ + divdi3 \ + divsc3 \ + divmodsi4 \ + udivmodsi4 \ + do_global_dtors \ + eprintf \ + ffsdi2 \ + fixdfdi \ + fixsfdi \ + fixunsdfdi \ + fixunsdfsi \ + fixunssfdi \ + fixunssfsi \ + floatdidf \ + floatdisf \ + floatundidf \ + floatundisf \ + gcc_bcmp \ + lshrdi3 \ + moddi3 \ + muldc3 \ + muldi3 \ + mulsc3 \ + mulvdi3 \ + mulvsi3 \ + negdi2 \ + negvdi2 \ + negvsi2 \ + paritydi2 \ + paritysi2 \ + popcountdi2 \ + popcountsi2 \ + powidf2 \ + powisf2 \ + subvdi3 \ + subvsi3 \ + ucmpdi2 \ + udiv_w_sdiv \ + udivdi3 \ + udivmoddi4 \ + umoddi3 + +CCKEXT_ARM_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \ + adddf3 \ + addsf3 \ + aeabi_cdcmpeq \ + aeabi_cdrcmple \ + aeabi_cfcmpeq \ + aeabi_cfrcmple \ + aeabi_dcmpeq \ + aeabi_dcmpge \ + aeabi_dcmpgt \ + aeabi_dcmple \ + aeabi_dcmplt \ + aeabi_drsub \ + aeabi_fcmpeq \ + aeabi_fcmpge \ + aeabi_fcmpgt \ + aeabi_fcmple \ + aeabi_fcmplt \ + aeabi_frsub \ + aeabi_idivmod \ + aeabi_uidivmod \ + cmpdf2 \ + cmpsf2 \ + div0 \ + divdf3 \ + divsf3 \ + divsi3 \ + extendsfdf2 \ + ffssi2 \ + fixdfsi \ + fixsfsi \ + floatsidf \ + floatsisf \ + floatunsidf \ + floatunsisf \ + comparedf2 \ + comparesf2 \ + modsi3 \ + muldf3 \ + mulsf3 \ + negdf2 \ + negsf2 \ + subdf3 \ + subsf3 \ + switch16 \ + switch32 \ + switch8 \ + switchu8 \ + truncdfsf2 \ + udivsi3 \ + umodsi3 \ + unorddf2 \ + unordsf2 + +CCKEXT_ARMVFP_FUNCTIONS := $(CCKEXT_ARM_FUNCTIONS) \ + adddf3vfp \ + addsf3vfp \ + divdf3vfp \ + divsf3vfp \ + eqdf2vfp \ + eqsf2vfp \ + extendsfdf2vfp \ + fixdfsivfp \ + fixsfsivfp \ + fixunsdfsivfp \ + fixunssfsivfp \ + floatsidfvfp \ + floatsisfvfp \ + floatunssidfvfp \ + floatunssisfvfp \ + gedf2vfp \ + gesf2vfp \ + gtdf2vfp \ + gtsf2vfp \ + ledf2vfp \ + lesf2vfp \ + ltdf2vfp \ + ltsf2vfp \ + muldf3vfp \ + mulsf3vfp \ + nedf2vfp \ + nesf2vfp \ + subdf3vfp \ + subsf3vfp \ + truncdfsf2vfp \ + unorddf2vfp \ + unordsf2vfp + +FUNCTIONS.cc_kext.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext.armv7f := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext_ios5.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext_ios5.armv7f := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext_ios5.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS) +FUNCTIONS.cc_kext_ios5.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS) + +CCKEXT_X86_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \ + divxc3 \ + fixunsxfdi \ + fixunsxfsi \ + fixxfdi \ + floatdixf \ + floatundixf \ + mulxc3 \ + powixf2 + +FUNCTIONS.cc_kext.i386 := $(CCKEXT_X86_FUNCTIONS) \ + ffssi2 \ + i686.get_pc_thunk.eax \ + i686.get_pc_thunk.ebp \ + i686.get_pc_thunk.ebx \ + i686.get_pc_thunk.ecx \ + i686.get_pc_thunk.edi \ + i686.get_pc_thunk.edx \ + i686.get_pc_thunk.esi + +FUNCTIONS.cc_kext.x86_64 := $(CCKEXT_X86_FUNCTIONS) \ + absvti2 \ + addvti3 \ + ashlti3 \ + ashrti3 \ + clzti2 \ + cmpti2 \ + ctzti2 \ + divti3 \ + ffsti2 \ + fixdfti \ + fixsfti \ + fixunsdfti \ + fixunssfti \ + fixunsxfti \ + fixxfti \ + floattidf \ + floattisf \ + floattixf \ + floatuntidf \ + floatuntisf \ + floatuntixf \ + lshrti3 \ + modti3 \ + multi3 \ + mulvti3 \ + negti2 \ + negvti2 \ + parityti2 \ + popcountti2 \ + subvti3 \ + ucmpti2 \ + udivmodti4 \ + udivti3 \ + umodti3 + +# FIXME: Currently, compiler-rt is missing implementations for a number of the +# functions that need to go into libcc_kext.a. Filter them out for now. +CCKEXT_MISSING_FUNCTIONS := \ + cmpdf2 cmpsf2 div0 \ + ffssi2 \ + udiv_w_sdiv unorddf2 unordsf2 bswapdi2 \ + bswapsi2 \ + gcc_bcmp \ + do_global_dtors \ + i686.get_pc_thunk.eax i686.get_pc_thunk.ebp i686.get_pc_thunk.ebx \ + i686.get_pc_thunk.ecx i686.get_pc_thunk.edi i686.get_pc_thunk.edx \ + i686.get_pc_thunk.esi \ + aeabi_cdcmpeq aeabi_cdrcmple aeabi_cfcmpeq aeabi_cfrcmple aeabi_dcmpeq \ + aeabi_dcmpge aeabi_dcmpgt aeabi_dcmple aeabi_dcmplt aeabi_drsub aeabi_fcmpeq \ + aeabi_fcmpge aeabi_fcmpgt aeabi_fcmple aeabi_fcmplt aeabi_frsub aeabi_idivmod \ + aeabi_uidivmod + +FUNCTIONS.cc_kext.armv7 := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7)) +FUNCTIONS.cc_kext.armv7f := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7f)) +FUNCTIONS.cc_kext.armv7k := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7k)) +FUNCTIONS.cc_kext.armv7s := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7s)) +FUNCTIONS.cc_kext_ios5.armv7 := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7)) +FUNCTIONS.cc_kext_ios5.armv7f := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7f)) +FUNCTIONS.cc_kext_ios5.armv7k := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7k)) +FUNCTIONS.cc_kext_ios5.armv7s := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7s)) +FUNCTIONS.cc_kext.i386 := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.i386)) +FUNCTIONS.cc_kext.x86_64 := \ + $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.x86_64)) + +KERNEL_USE.cc_kext := 1 +KERNEL_USE.cc_kext_ios5 := 1 + +VISIBILITY_HIDDEN := 1 + +SHARED_LIBRARY_SUFFIX := dylib diff --git a/projects/compiler-rt/make/platform/clang_darwin_embedded.mk b/projects/compiler-rt/make/platform/clang_darwin_embedded.mk new file mode 100644 index 00000000..3cea2e45 --- /dev/null +++ b/projects/compiler-rt/make/platform/clang_darwin_embedded.mk @@ -0,0 +1,250 @@ +# These are the functions which clang needs when it is targetting a previous +# version of the OS. The issue is that the backend may use functions which were +# not present in the libgcc that shipped on the platform. In such cases, we link +# with a version of the library which contains private_extern definitions of all +# the extra functions which might be referenced. + +Description := Static runtime libraries for embedded clang/Darwin + +XCRun = \ + $(shell \ + result=`xcrun -find $(1) 2> /dev/null`; \ + if [ "$$?" != "0" ]; then result=$(1); fi; \ + echo $$result) + +### + +CC := $(call XCRun,clang) +AR := $(call XCRun,ar) +RANLIB := $(call XCRun,ranlib) +STRIP := $(call XCRun,strip) +LIPO := $(call XCRun,lipo) +DSYMUTIL := $(call XCRun,dsymutil) + +Configs := +UniversalArchs := + +# Soft-float version of the runtime. No floating-point instructions will be used +# and the ABI (out of necessity) passes floating values in normal registers: +# non-VFP variant of the AAPCS. +Configs += soft_static +UniversalArchs.soft_static := armv6m armv7m armv7em armv7 + +# Hard-float version of the runtime. On ARM VFP instructions and registers are +# allowed, and floating point values get passed in them. VFP variant of the +# AAPCS. +Configs += hard_static +UniversalArchs.hard_static := armv7em armv7 i386 x86_64 + +Configs += soft_pic +UniversalArchs.soft_pic := armv6m armv7m armv7em armv7 + +Configs += hard_pic +UniversalArchs.hard_pic := armv7em armv7 i386 x86_64 + +CFLAGS := -Wall -Werror -Oz -fomit-frame-pointer -ffreestanding + +PIC_CFLAGS := -fPIC +STATIC_CFLAGS := -static + +CFLAGS_SOFT := -mfloat-abi=soft +CFLAGS_HARD := -mfloat-abi=hard + +CFLAGS_ARMV7 := -target thumbv7-apple-darwin-eabi +CFLAGS_I386 := -march=pentium + +CFLAGS.soft_static := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_SOFT) +CFLAGS.hard_static := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_HARD) +CFLAGS.soft_pic := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_SOFT) +CFLAGS.hard_pic := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_HARD) + +CFLAGS.soft_static.armv7 := $(CFLAGS.soft_static) $(CFLAGS_ARMV7) +CFLAGS.hard_static.armv7 := $(CFLAGS.hard_static) $(CFLAGS_ARMV7) +CFLAGS.soft_pic.armv7 := $(CFLAGS.soft_pic) $(CFLAGS_ARMV7) +CFLAGS.hard_pic.armv7 := $(CFLAGS.hard_pic) $(CFLAGS_ARMV7) + +# x86 platforms ignore -mfloat-abi options and complain about doing so. Despite +# this they're hard-float. +CFLAGS.hard_static.i386 := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_I386) +CFLAGS.hard_pic.i386 := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_I386) +CFLAGS.hard_static.x86_64 := $(CFLAGS) $(STATIC_CFLAGS) +CFLAGS.hard_pic.x86_64 := $(CFLAGS) $(PIC_CFLAGS) + +# Functions not wanted: +# + eprintf is obsolete anyway +# + *vfp: designed for Thumb1 CPUs with VFPv2 + +COMMON_FUNCTIONS := \ + absvdi2 \ + absvsi2 \ + addvdi3 \ + addvsi3 \ + ashldi3 \ + ashrdi3 \ + bswapdi2 \ + bswapsi2 \ + clzdi2 \ + clzsi2 \ + cmpdi2 \ + ctzdi2 \ + ctzsi2 \ + divdc3 \ + divdi3 \ + divsc3 \ + divmodsi4 \ + udivmodsi4 \ + do_global_dtors \ + ffsdi2 \ + fixdfdi \ + fixsfdi \ + fixunsdfdi \ + fixunsdfsi \ + fixunssfdi \ + fixunssfsi \ + floatdidf \ + floatdisf \ + floatundidf \ + floatundisf \ + gcc_bcmp \ + lshrdi3 \ + moddi3 \ + muldc3 \ + muldi3 \ + mulsc3 \ + mulvdi3 \ + mulvsi3 \ + negdi2 \ + negvdi2 \ + negvsi2 \ + paritydi2 \ + paritysi2 \ + popcountdi2 \ + popcountsi2 \ + powidf2 \ + powisf2 \ + subvdi3 \ + subvsi3 \ + ucmpdi2 \ + udiv_w_sdiv \ + udivdi3 \ + udivmoddi4 \ + umoddi3 \ + adddf3 \ + addsf3 \ + cmpdf2 \ + cmpsf2 \ + div0 \ + divdf3 \ + divsf3 \ + divsi3 \ + extendsfdf2 \ + ffssi2 \ + fixdfsi \ + fixsfsi \ + floatsidf \ + floatsisf \ + floatunsidf \ + floatunsisf \ + comparedf2 \ + comparesf2 \ + modsi3 \ + muldf3 \ + mulsf3 \ + negdf2 \ + negsf2 \ + subdf3 \ + subsf3 \ + truncdfsf2 \ + udivsi3 \ + umodsi3 \ + unorddf2 \ + unordsf2 + +ARM_FUNCTIONS := \ + aeabi_cdcmpeq \ + aeabi_cdrcmple \ + aeabi_cfcmpeq \ + aeabi_cfrcmple \ + aeabi_dcmpeq \ + aeabi_dcmpge \ + aeabi_dcmpgt \ + aeabi_dcmple \ + aeabi_dcmplt \ + aeabi_drsub \ + aeabi_fcmpeq \ + aeabi_fcmpge \ + aeabi_fcmpgt \ + aeabi_fcmple \ + aeabi_fcmplt \ + aeabi_frsub \ + aeabi_idivmod \ + aeabi_uidivmod \ + +# ARM Assembly implementation which requires Thumb2 (i.e. won't work on v6M). +THUMB2_FUNCTIONS := \ + switch16 \ + switch32 \ + switch8 \ + switchu8 \ + +I386_FUNCTIONS := \ + i686.get_pc_thunk.eax \ + i686.get_pc_thunk.ebp \ + i686.get_pc_thunk.ebx \ + i686.get_pc_thunk.ecx \ + i686.get_pc_thunk.edi \ + i686.get_pc_thunk.edx \ + i686.get_pc_thunk.esi + +# FIXME: Currently, compiler-rt is missing implementations for a number of the +# functions. Filter them out for now. +MISSING_FUNCTIONS := \ + cmpdf2 cmpsf2 div0 \ + ffssi2 \ + udiv_w_sdiv unorddf2 unordsf2 bswapdi2 \ + bswapsi2 \ + gcc_bcmp \ + do_global_dtors \ + i686.get_pc_thunk.eax i686.get_pc_thunk.ebp i686.get_pc_thunk.ebx \ + i686.get_pc_thunk.ecx i686.get_pc_thunk.edi i686.get_pc_thunk.edx \ + i686.get_pc_thunk.esi \ + aeabi_cdcmpeq aeabi_cdrcmple aeabi_cfcmpeq aeabi_cfrcmple aeabi_dcmpeq \ + aeabi_dcmpge aeabi_dcmpgt aeabi_dcmple aeabi_dcmplt aeabi_drsub \ + aeabi_fcmpeq \ aeabi_fcmpge aeabi_fcmpgt aeabi_fcmple aeabi_fcmplt \ + aeabi_frsub aeabi_idivmod aeabi_uidivmod + +FUNCTIONS_ARMV6M := $(COMMON_FUNCTIONS) $(ARM_FUNCTIONS) +FUNCTIONS_ARM_ALL := $(COMMON_FUNCTIONS) $(ARM_FUNCTIONS) $(THUMB2_FUNCTIONS) +FUNCTIONS_I386 := $(COMMON_FUNCTIONS) $(I386_FUNCTIONS) +FUNCTIONS_X86_64 := $(COMMON_FUNCTIONS) + +FUNCTIONS_ARMV6M := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_ARMV6M)) +FUNCTIONS_ARM_ALL := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_ARM_ALL)) +FUNCTIONS_I386 := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_I386)) +FUNCTIONS_X86_64 := \ + $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_X86_64)) + +FUNCTIONS.soft_static.armv6m := $(FUNCTIONS_ARMV6M) +FUNCTIONS.soft_pic.armv6m := $(FUNCTIONS_ARMV6M) + +FUNCTIONS.soft_static.armv7m := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.soft_pic.armv7m := $(FUNCTIONS_ARM_ALL) + +FUNCTIONS.soft_static.armv7em := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_static.armv7em := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.soft_pic.armv7em := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_pic.armv7em := $(FUNCTIONS_ARM_ALL) + +FUNCTIONS.soft_static.armv7 := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_static.armv7 := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.soft_pic.armv7 := $(FUNCTIONS_ARM_ALL) +FUNCTIONS.hard_pic.armv7 := $(FUNCTIONS_ARM_ALL) + +FUNCTIONS.hard_static.i386 := $(FUNCTIONS_I386) +FUNCTIONS.hard_pic.i386 := $(FUNCTIONS_I386) + +FUNCTIONS.hard_static.x86_64 := $(FUNCTIONS_X86_64) +FUNCTIONS.hard_pic.x86_64 := $(FUNCTIONS_X86_64) diff --git a/projects/compiler-rt/make/platform/clang_darwin_test_input.c b/projects/compiler-rt/make/platform/clang_darwin_test_input.c new file mode 100644 index 00000000..b7074b85 --- /dev/null +++ b/projects/compiler-rt/make/platform/clang_darwin_test_input.c @@ -0,0 +1,6 @@ +/* Include the headers we use in int_lib.h, to verify that they work. */ + +#include +#include +#include +#include diff --git a/projects/compiler-rt/make/platform/clang_linux.mk b/projects/compiler-rt/make/platform/clang_linux.mk new file mode 100644 index 00000000..5796e867 --- /dev/null +++ b/projects/compiler-rt/make/platform/clang_linux.mk @@ -0,0 +1,155 @@ +Description := Static runtime libraries for clang/Linux. + +### + +CC := clang +Arch := unknown +Configs := + +# We don't currently have any general purpose way to target architectures other +# than the compiler defaults (because there is no generalized way to invoke +# cross compilers). For now, we just find the target architecture of the +# compiler and only define configurations we know that compiler can generate. +CompilerTargetTriple := $(shell \ + $(CC) -v 2>&1 | grep 'Target:' | cut -d' ' -f2) +ifeq ($(CompilerTargetTriple),) +$(error "unable to infer compiler target triple for $(CC)") +endif + +# Only define configs if we detected a linux target. +ifneq ($(findstring -linux-,$(CompilerTargetTriple)),) + +# Define configs only if arch in triple is i386 or x86_64 +CompilerTargetArch := $(firstword $(subst -, ,$(CompilerTargetTriple))) +ifeq ($(call contains,i386 x86_64,$(CompilerTargetArch)),true) + +# TryCompile compiler source flags +# Returns exit code of running a compiler invocation. +TryCompile = \ + $(shell \ + cflags=""; \ + for flag in $(3); do \ + cflags="$$cflags $$flag"; \ + done; \ + $(1) $$cflags $(2) -o /dev/null > /dev/null 2> /dev/null ; \ + echo $$?) + +test_source = $(ProjSrcRoot)/make/platform/clang_linux_test_input.c +ifeq ($(CompilerTargetArch),i386) + SupportedArches := i386 + ifeq ($(call TryCompile,$(CC),$(test_source),-m64),0) + SupportedArches += x86_64 + endif +else + SupportedArches := x86_64 + ifeq ($(call TryCompile,$(CC),$(test_source),-m32),0) + SupportedArches += i386 + endif +endif + +# Build runtime libraries for i386. +ifeq ($(call contains,$(SupportedArches),i386),true) +Configs += full-i386 profile-i386 san-i386 asan-i386 ubsan-i386 ubsan_cxx-i386 +Arch.full-i386 := i386 +Arch.profile-i386 := i386 +Arch.san-i386 := i386 +Arch.asan-i386 := i386 +Arch.ubsan-i386 := i386 +Arch.ubsan_cxx-i386 := i386 +endif + +# Build runtime libraries for x86_64. +ifeq ($(call contains,$(SupportedArches),x86_64),true) +Configs += full-x86_64 profile-x86_64 san-x86_64 asan-x86_64 tsan-x86_64 \ + msan-x86_64 ubsan-x86_64 ubsan_cxx-x86_64 dfsan-x86_64 lsan-x86_64 +Arch.full-x86_64 := x86_64 +Arch.profile-x86_64 := x86_64 +Arch.san-x86_64 := x86_64 +Arch.asan-x86_64 := x86_64 +Arch.tsan-x86_64 := x86_64 +Arch.msan-x86_64 := x86_64 +Arch.ubsan-x86_64 := x86_64 +Arch.ubsan_cxx-x86_64 := x86_64 +Arch.dfsan-x86_64 := x86_64 +Arch.lsan-x86_64 := x86_64 +endif + +endif + +ifneq ($(LLVM_ANDROID_TOOLCHAIN_DIR),) +Configs += asan-arm-android +Arch.asan-arm-android := arm-android +endif + +endif + +### + +CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer +SANITIZER_CFLAGS := -fPIE -fno-builtin -gline-tables-only + +CFLAGS.full-i386 := $(CFLAGS) -m32 +CFLAGS.full-x86_64 := $(CFLAGS) -m64 +CFLAGS.profile-i386 := $(CFLAGS) -m32 +CFLAGS.profile-x86_64 := $(CFLAGS) -m64 +CFLAGS.san-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti +CFLAGS.san-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti +CFLAGS.asan-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti \ + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 +CFLAGS.asan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti \ + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=1 +CFLAGS.tsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti +CFLAGS.msan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti +CFLAGS.ubsan-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti +CFLAGS.ubsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti +CFLAGS.ubsan_cxx-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) +CFLAGS.ubsan_cxx-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) +CFLAGS.dfsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) +CFLAGS.lsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti + +SHARED_LIBRARY.asan-arm-android := 1 +ANDROID_COMMON_FLAGS := -target arm-linux-androideabi \ + --sysroot=$(LLVM_ANDROID_TOOLCHAIN_DIR)/sysroot \ + -B$(LLVM_ANDROID_TOOLCHAIN_DIR) +CFLAGS.asan-arm-android := $(CFLAGS) -fPIC -fno-builtin \ + $(ANDROID_COMMON_FLAGS) -mllvm -arm-enable-ehabi -fno-rtti +LDFLAGS.asan-arm-android := $(LDFLAGS) $(ANDROID_COMMON_FLAGS) -ldl \ + -Wl,-soname=libclang_rt.asan-arm-android.so + +# Use our stub SDK as the sysroot to support more portable building. For now we +# just do this for the core module, because the stub SDK doesn't have +# enough support to build the sanitizers or profile runtimes. +CFLAGS.full-i386 += --sysroot=$(ProjSrcRoot)/SDKs/linux +CFLAGS.full-x86_64 += --sysroot=$(ProjSrcRoot)/SDKs/linux + +FUNCTIONS.full-i386 := $(CommonFunctions) $(ArchFunctions.i386) +FUNCTIONS.full-x86_64 := $(CommonFunctions) $(ArchFunctions.x86_64) +FUNCTIONS.profile-i386 := GCDAProfiling +FUNCTIONS.profile-x86_64 := GCDAProfiling +FUNCTIONS.san-i386 := $(SanitizerCommonFunctions) +FUNCTIONS.san-x86_64 := $(SanitizerCommonFunctions) +FUNCTIONS.asan-i386 := $(AsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) +FUNCTIONS.asan-x86_64 := $(AsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) $(LsanCommonFunctions) +FUNCTIONS.asan-arm-android := $(AsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) +FUNCTIONS.tsan-x86_64 := $(TsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) +FUNCTIONS.msan-x86_64 := $(MsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) +FUNCTIONS.ubsan-i386 := $(UbsanFunctions) +FUNCTIONS.ubsan-x86_64 := $(UbsanFunctions) +FUNCTIONS.ubsan_cxx-i386 := $(UbsanCXXFunctions) +FUNCTIONS.ubsan_cxx-x86_64 := $(UbsanCXXFunctions) +FUNCTIONS.dfsan-x86_64 := $(DfsanFunctions) $(SanitizerCommonFunctions) +FUNCTIONS.lsan-x86_64 := $(LsanFunctions) $(InterceptionFunctions) \ + $(SanitizerCommonFunctions) + +# Always use optimized variants. +OPTIMIZED := 1 + +# We don't need to use visibility hidden on Linux. +VISIBILITY_HIDDEN := 0 + +SHARED_LIBRARY_SUFFIX := so diff --git a/projects/compiler-rt/make/platform/clang_linux_test_input.c b/projects/compiler-rt/make/platform/clang_linux_test_input.c new file mode 100644 index 00000000..e65ce986 --- /dev/null +++ b/projects/compiler-rt/make/platform/clang_linux_test_input.c @@ -0,0 +1,4 @@ +// This file is used to check if we can produce working executables +// for i386 and x86_64 archs on Linux. +#include +int main(){} diff --git a/projects/compiler-rt/make/platform/darwin_bni.mk b/projects/compiler-rt/make/platform/darwin_bni.mk new file mode 100644 index 00000000..03e8d290 --- /dev/null +++ b/projects/compiler-rt/make/platform/darwin_bni.mk @@ -0,0 +1,117 @@ + +Description := Target for Darwin using an Apple-style build. + +Configs := Debug Release Profile Static + +# We override this with RC_ARCHS because B&I may want to build on an ARCH we +# haven't explicitly defined support for. If all goes well, this will just work +# and the resulting lib will just have generic versions for anything unknown. +UniversalArchs := $(RC_ARCHS) + +ifneq (,$(SDKROOT)) + override CC := $(shell xcrun -sdk $(SDKROOT) -find clang || echo "false") + AR := $(shell xcrun -sdk $(SDKROOT) -find ar || echo "false") + RANLIB := $(shell xcrun -sdk $(SDKROOT) -find ranlib || echo "false") + STRIP := $(shell xcrun -sdk $(SDKROOT) -find strip || echo "false") + LIPO := $(shell xcrun -sdk $(SDKROOT) -find lipo || echo "false") + DSYMUTIL := $(shell xcrun -sdk $(SDKROOT) -find dsymutil || echo "false") +endif + +ifneq ($(IPHONEOS_DEPLOYMENT_TARGET),) + DEPLOYMENT_FLAGS := -miphoneos-version-min=$(IPHONEOS_DEPLOYMENT_TARGET) +else + ifneq ($(MACOSX_DEPLOYMENT_TARGET),) + DEPLOYMENT_FLAGS := -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) + endif +endif + +ifneq (,$(SDKROOT)) + DEPLOYMENT_FLAGS += -isysroot $(SDKROOT) +endif + +CFLAGS := -Wall -Os -fomit-frame-pointer -g $(DEPLOYMENT_FLAGS) +CFLAGS.Static := $(CFLAGS) -static +DYLIB_FLAGS := $(DEPLOYMENT_FLAGS) \ + -Xarch_arm -Wl,-alias_list,$(SRCROOT)/lib/arm/softfloat-alias.list + +VISIBILITY_HIDDEN := 0 +VISIBILITY_HIDDEN.Static := 1 + + +FUNCTIONS := absvdi2 absvsi2 addvdi3 addvsi3 ashldi3 ashrdi3 \ + clzdi2 clzsi2 cmpdi2 ctzdi2 ctzsi2 \ + divdc3 divdi3 divsc3 ffsdi2 \ + fixdfdi fixsfdi fixunsdfdi fixunsdfsi fixunssfdi \ + fixunssfsi floatdidf floatdisf floatundidf floatundisf \ + gcc_personality_v0 lshrdi3 moddi3 muldc3 muldi3 mulosi4 \ + mulodi4 muloti4 mulsc3 mulvdi3 mulvsi3 negdi2 negvdi2 negvsi2 \ + paritydi2 paritysi2 popcountdi2 popcountsi2 powidf2 \ + powisf2 subvdi3 subvsi3 ucmpdi2 udivdi3 \ + udivmoddi4 umoddi3 apple_versioning eprintf atomic + +FUNCTIONS.i386 := $(FUNCTIONS) \ + divxc3 fixunsxfdi fixunsxfsi fixxfdi floatdixf \ + floatundixf mulxc3 powixf2 clear_cache \ + enable_execute_stack +FUNCTIONS.ppc := $(FUNCTIONS) \ + divtc3 fixtfdi fixunstfdi floatditf floatunditf \ + gcc_qadd gcc_qdiv gcc_qmul gcc_qsub multc3 \ + powitf2 restFP saveFP trampoline_setup \ + clear_cache enable_execute_stack +FUNCTIONS.x86_64 := $(FUNCTIONS) \ + absvti2 addvti3 ashlti3 ashrti3 clzti2 cmpti2 \ + ctzti2 divti3 divxc3 ffsti2 fixdfti fixsfti \ + fixunsdfti fixunssfti fixunsxfdi fixunsxfsi \ + fixunsxfti fixxfdi fixxfti floatdixf floattidf \ + floattisf floattixf floatundixf floatuntidf \ + floatuntisf floatuntixf lshrti3 modti3 multi3 \ + muloti4 mulvti3 mulxc3 negti2 negvti2 parityti2 \ + popcountti2 powixf2 subvti3 ucmpti2 udivmodti4 \ + udivti3 umodti3 clear_cache enable_execute_stack + +FUNCTIONS.armv4t := $(FUNCTIONS) + +FUNCTIONS.armv5 := $(FUNCTIONS) \ + adddf3 addsf3 bswapdi2 bswapsi2 \ + comparedf2 comparesf2 extendsfdf2 \ + divdf3 divsf3 \ + fixdfsi fixsfsi fixunsdfsi fixunssfsi \ + floatsidf floatsisf floatunsidf floatunsisf \ + muldf3 mulsf3 \ + negdf2 negsf2 \ + truncdfsf2 \ + modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 \ + switch8 switchu8 switch16 switch32 \ + sync_synchronize + +FUNCTIONS.armv6 := $(FUNCTIONS) \ + comparedf2 comparesf2 \ + adddf3vfp addsf3vfp bswapdi2 bswapsi2 divdf3vfp \ + divsf3vfp eqdf2vfp eqsf2vfp extendsfdf2vfp \ + fixdfsivfp fixsfsivfp fixunsdfsivfp fixunssfsivfp \ + floatsidfvfp floatsisfvfp floatunssidfvfp floatunssisfvfp \ + gedf2vfp gesf2vfp gtdf2vfp gtsf2vfp \ + ledf2vfp lesf2vfp ltdf2vfp ltsf2vfp \ + muldf3vfp mulsf3vfp \ + nedf2vfp nesf2vfp \ + subdf3vfp subsf3vfp truncdfsf2vfp unorddf2vfp unordsf2vfp \ + modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 \ + switch8 switchu8 switch16 switch32 \ + restore_vfp_d8_d15_regs save_vfp_d8_d15_regs \ + sync_synchronize + +FUNCTIONS.armv7 := $(FUNCTIONS) \ + comparedf2 comparesf2 \ + adddf3vfp addsf3vfp bswapdi2 bswapsi2 divdf3vfp \ + divsf3vfp eqdf2vfp eqsf2vfp extendsfdf2vfp \ + fixdfsivfp fixsfsivfp fixunsdfsivfp fixunssfsivfp \ + floatsidfvfp floatsisfvfp floatunssidfvfp floatunssisfvfp \ + gedf2vfp gesf2vfp gtdf2vfp gtsf2vfp \ + ledf2vfp lesf2vfp ltdf2vfp ltsf2vfp \ + muldf3vfp mulsf3vfp \ + nedf2vfp nesf2vfp \ + subdf3vfp subsf3vfp truncdfsf2vfp unorddf2vfp unordsf2vfp \ + modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 + +FUNCTIONS.armv7s := $(FUNCTIONS.armv7) + diff --git a/projects/compiler-rt/make/platform/darwin_fat.mk b/projects/compiler-rt/make/platform/darwin_fat.mk new file mode 100644 index 00000000..54936a39 --- /dev/null +++ b/projects/compiler-rt/make/platform/darwin_fat.mk @@ -0,0 +1,56 @@ +# Configurations to build +# +# This section must define: +# Description - A description of this target. +# Configs - The names of each configuration to build; this is used to build +# multiple libraries inside a single configuration file (for +# example, Debug and Release builds, or builds with and without +# software floating point). +# +# This section must define one of: +# UniveralArchs - A list of architectures to build for, when using universal build +# support (e.g., on Darwin). This should only be used to build fat +# libraries, simply building multiple libraries for different +# architectures should do so using distinct configs, with the +# appropriate choices for CC and CFLAGS. +# +# Arch - The target architecture; this must match the compiler-rt name for the +# architecture and is used to find the appropriate function +# implementations. +# +# When not universal builds, this section may define: +# Arch. - Set the target architecture on a per-config basis. + +Description := Target for building universal libraries for Darwin. + +Configs := Debug Release Profile +UniversalArchs := i386 x86_64 + +# Platform Options +# +# This section may override any of the variables in make/options.mk, using: +#