diff options
author | andrew <andrew@FreeBSD.org> | 2012-07-30 10:58:13 +0000 |
---|---|---|
committer | andrew <andrew@FreeBSD.org> | 2012-07-30 10:58:13 +0000 |
commit | cfeab007a554034f0b3ab4a677cf9dd2696c12f9 (patch) | |
tree | 40cc44a3d02ed86de24f2117a55680e4f0eb01a0 /lib/sanitizer_common | |
parent | 07af089f1449ec5506ca7ede5b593e11a0f48603 (diff) | |
download | FreeBSD-src-cfeab007a554034f0b3ab4a677cf9dd2696c12f9.zip FreeBSD-src-cfeab007a554034f0b3ab4a677cf9dd2696c12f9.tar.gz |
Import compiler-rt r160957.
Diffstat (limited to 'lib/sanitizer_common')
32 files changed, 4131 insertions, 0 deletions
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt new file mode 100644 index 0000000..d797a56 --- /dev/null +++ b/lib/sanitizer_common/CMakeLists.txt @@ -0,0 +1,35 @@ +# 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_flags.cc + sanitizer_libc.cc + sanitizer_linux.cc + sanitizer_mac.cc + sanitizer_posix.cc + sanitizer_printf.cc + sanitizer_symbolizer.cc + sanitizer_win.cc + ) + +set(SANITIZER_CFLAGS "-fPIC -fno-exceptions -funwind-tables -fvisibility=hidden") + +set(SANITIZER_COMMON_DEFINITIONS + SANITIZER_HAS_EXCEPTIONS=1) + +if(CAN_TARGET_X86_64) + add_library(RTSanitizerCommon.x86_64 OBJECT ${SANITIZER_SOURCES}) + set_property(TARGET RTSanitizerCommon.x86_64 PROPERTY COMPILE_FLAGS + "${SANITIZER_CFLAGS} ${TARGET_X86_64_CFLAGS}") + set_property(TARGET RTSanitizerCommon.x86_64 APPEND PROPERTY COMPILE_DEFINITIONS + ${SANITIZER_COMMON_DEFINITIONS}) +endif() +if(CAN_TARGET_I386) + add_library(RTSanitizerCommon.i386 OBJECT ${SANITIZER_SOURCES}) + set_property(TARGET RTSanitizerCommon.i386 PROPERTY COMPILE_FLAGS + "${SANITIZER_CFLAGS} ${TARGET_I386_CFLAGS}") + set_property(TARGET RTSanitizerCommon.i386 APPEND PROPERTY COMPILE_DEFINITIONS + ${SANITIZER_COMMON_DEFINITIONS}) +endif() diff --git a/lib/sanitizer_common/Makefile.mk b/lib/sanitizer_common/Makefile.mk new file mode 100644 index 0000000..da83c2d --- /dev/null +++ b/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/lib/sanitizer_common/sanitizer_allocator.cc b/lib/sanitizer_common/sanitizer_allocator.cc new file mode 100644 index 0000000..816fddf --- /dev/null +++ b/lib/sanitizer_common/sanitizer_allocator.cc @@ -0,0 +1,59 @@ +//===-- 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 that is used inside run-times. +//===----------------------------------------------------------------------===// +#include "sanitizer_common.h" + +// FIXME: We should probably use more low-level allocator that would +// mmap some pages and split them into chunks to fulfill requests. +#ifdef __linux__ +extern "C" void *__libc_malloc(__sanitizer::uptr size); +extern "C" void __libc_free(void *ptr); +# define LIBC_MALLOC __libc_malloc +# define LIBC_FREE __libc_free +#else // __linux__ +# include <stdlib.h> +# define LIBC_MALLOC malloc +# define LIBC_FREE free +#endif // __linux__ + +namespace __sanitizer { + +const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; + +void *InternalAlloc(uptr size) { + if (size + sizeof(u64) < size) + return 0; + void *p = LIBC_MALLOC(size + sizeof(u64)); + if (p == 0) + return 0; + ((u64*)p)[0] = kBlockMagic; + return (char*)p + sizeof(u64); +} + +void InternalFree(void *addr) { + if (addr == 0) + return; + addr = (char*)addr - sizeof(u64); + CHECK_EQ(((u64*)addr)[0], kBlockMagic); + ((u64*)addr)[0] = 0; + LIBC_FREE(addr); +} + +void *InternalAllocBlock(void *p) { + CHECK_NE(p, (void*)0); + u64 *pp = (u64*)((uptr)p & ~0x7); + for (; pp[0] != kBlockMagic; pp--) {} + return pp + 1; +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_allocator64.h b/lib/sanitizer_common/sanitizer_allocator64.h new file mode 100644 index 0000000..eb79a12 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_allocator64.h @@ -0,0 +1,488 @@ +//===-- sanitizer_allocator64.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Specialized allocator which works only in 64-bit address space. +// To be used by ThreadSanitizer, MemorySanitizer and possibly other tools. +// The main feature of this allocator is that the header is located far away +// from the user memory region, so that the tool does not use extra shadow +// for the header. +// +// Status: not yet ready. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_ALLOCATOR_H +#define SANITIZER_ALLOCATOR_H + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_list.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +// Maps size class id to size and back. +class DefaultSizeClassMap { + private: + // Here we use a spline composed of 5 polynomials of oder 1. + // The first size class is l0, then the classes go with step s0 + // untill they reach l1, after which they go with step s1 and so on. + // Steps should be powers of two for cheap division. + // The size of the last size class should be a power of two. + // There should be at most 256 size classes. + static const uptr l0 = 1 << 4; + static const uptr l1 = 1 << 9; + static const uptr l2 = 1 << 12; + static const uptr l3 = 1 << 15; + static const uptr l4 = 1 << 18; + static const uptr l5 = 1 << 21; + + static const uptr s0 = 1 << 4; + static const uptr s1 = 1 << 6; + static const uptr s2 = 1 << 9; + static const uptr s3 = 1 << 12; + static const uptr s4 = 1 << 15; + + static const uptr u0 = 0 + (l1 - l0) / s0; + static const uptr u1 = u0 + (l2 - l1) / s1; + static const uptr u2 = u1 + (l3 - l2) / s2; + static const uptr u3 = u2 + (l4 - l3) / s3; + static const uptr u4 = u3 + (l5 - l4) / s4; + + public: + static const uptr kNumClasses = u4 + 1; + static const uptr kMaxSize = l5; + static const uptr kMinSize = l0; + + COMPILER_CHECK(kNumClasses <= 256); + COMPILER_CHECK((kMaxSize & (kMaxSize - 1)) == 0); + + static uptr Size(uptr class_id) { + if (class_id <= u0) return l0 + s0 * (class_id - 0); + if (class_id <= u1) return l1 + s1 * (class_id - u0); + if (class_id <= u2) return l2 + s2 * (class_id - u1); + if (class_id <= u3) return l3 + s3 * (class_id - u2); + if (class_id <= u4) return l4 + s4 * (class_id - u3); + return 0; + } + static uptr ClassID(uptr size) { + if (size <= l1) return 0 + (size - l0 + s0 - 1) / s0; + if (size <= l2) return u0 + (size - l1 + s1 - 1) / s1; + if (size <= l3) return u1 + (size - l2 + s2 - 1) / s2; + if (size <= l4) return u2 + (size - l3 + s3 - 1) / s3; + if (size <= l5) return u3 + (size - l4 + s4 - 1) / s4; + return 0; + } +}; + +struct AllocatorListNode { + AllocatorListNode *next; +}; + +typedef IntrusiveList<AllocatorListNode> AllocatorFreeList; + + +// 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. +// +// 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 <gap> MetaChunkN ... MetaChunk1 +template <const uptr kSpaceBeg, const uptr kSpaceSize, + const uptr kMetadataSize, class SizeClassMap> +class SizeClassAllocator64 { + public: + void Init() { + CHECK_EQ(AllocBeg(), reinterpret_cast<uptr>(MmapFixedNoReserve( + AllocBeg(), AllocSize()))); + } + + bool CanAllocate(uptr size, uptr alignment) { + return size <= SizeClassMap::kMaxSize && + alignment <= SizeClassMap::kMaxSize; + } + + void *Allocate(uptr size, uptr alignment) { + CHECK(CanAllocate(size, alignment)); + return AllocateBySizeClass(SizeClassMap::ClassID(size)); + } + + void Deallocate(void *p) { + CHECK(PointerIsMine(p)); + DeallocateBySizeClass(p, GetSizeClass(p)); + } + + // Allocate several chunks of the given class_id. + void BulkAllocate(uptr class_id, AllocatorFreeList *free_list) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + if (region->free_list.empty()) { + PopulateFreeList(class_id, region); + } + CHECK(!region->free_list.empty()); + // Just take as many chunks as we have in the free list now. + // FIXME: this might be too much. + free_list->append_front(®ion->free_list); + CHECK(region->free_list.empty()); + } + + // Swallow the entire free_list for the given class_id. + void BulkDeallocate(uptr class_id, AllocatorFreeList *free_list) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + region->free_list.append_front(free_list); + } + + bool PointerIsMine(void *p) { + return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize; + } + uptr GetSizeClass(void *p) { + return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClasses; + } + + uptr GetActuallyAllocatedSize(void *p) { + CHECK(PointerIsMine(p)); + return SizeClassMap::Size(GetSizeClass(p)); + } + + uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } + + void *GetMetaData(void *p) { + uptr class_id = GetSizeClass(p); + uptr chunk_idx = GetChunkIdx(reinterpret_cast<uptr>(p), class_id); + return reinterpret_cast<void*>(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() { + UnmapOrDie(reinterpret_cast<void*>(AllocBeg()), AllocSize()); + } + + static const uptr kNumClasses = 256; // Power of two <= 256 + + private: + COMPILER_CHECK(kNumClasses <= SizeClassMap::kNumClasses); + static const uptr kRegionSize = kSpaceSize / kNumClasses; + COMPILER_CHECK((kRegionSize >> 32) > 0); // kRegionSize must be >= 2^32. + // 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 << 18; + + struct RegionInfo { + SpinMutex mutex; + AllocatorFreeList free_list; + uptr allocated_user; // Bytes allocated for user memory. + uptr allocated_meta; // Bytes allocated for metadata. + char padding[kCacheLineSize - 3 * sizeof(uptr) - sizeof(AllocatorFreeList)]; + }; + COMPILER_CHECK(sizeof(RegionInfo) == kCacheLineSize); + + uptr AdditionalSize() { + uptr res = sizeof(RegionInfo) * kNumClasses; + CHECK_EQ(res % kPageSize, 0); + return res; + } + uptr AllocBeg() { return kSpaceBeg - AdditionalSize(); } + uptr AllocSize() { return kSpaceSize + AdditionalSize(); } + + RegionInfo *GetRegionInfo(uptr class_id) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg); + return ®ions[-1 - class_id]; + } + + uptr GetChunkIdx(uptr chunk, uptr class_id) { + u32 offset = chunk % kRegionSize; + // Here we divide by a non-constant. This is costly. + // We require that kRegionSize is at least 2^32 so that offset is 32-bit. + // We save 2x by using 32-bit div, but may need to use a 256-way switch. + return offset / (u32)SizeClassMap::Size(class_id); + } + + void PopulateFreeList(uptr class_id, RegionInfo *region) { + uptr size = SizeClassMap::Size(class_id); + uptr beg_idx = region->allocated_user; + uptr end_idx = beg_idx + kPopulateSize; + region->free_list.clear(); + uptr region_beg = kSpaceBeg + kRegionSize * class_id; + uptr idx = beg_idx; + uptr i = 0; + do { // do-while loop because we need to put at least one item. + uptr p = region_beg + idx; + region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p)); + idx += size; + i++; + } while (idx < end_idx); + region->allocated_user += idx - beg_idx; + region->allocated_meta += i * kMetadataSize; + CHECK_LT(region->allocated_user + region->allocated_meta, kRegionSize); + } + + void *AllocateBySizeClass(uptr class_id) { + CHECK_LT(class_id, kNumClasses); + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + if (region->free_list.empty()) { + PopulateFreeList(class_id, region); + } + CHECK(!region->free_list.empty()); + AllocatorListNode *node = region->free_list.front(); + region->free_list.pop_front(); + return reinterpret_cast<void*>(node); + } + + void DeallocateBySizeClass(void *p, uptr class_id) { + RegionInfo *region = GetRegionInfo(class_id); + SpinMutexLock l(®ion->mutex); + region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p)); + } +}; + +// Objects of this type should be used as local caches for SizeClassAllocator64. +// Since the typical use of this class is to have one object per thread in TLS, +// is has to be POD. +template<const uptr kNumClasses, class SizeClassAllocator> +struct SizeClassAllocatorLocalCache { + // Don't need to call Init if the object is a global (i.e. zero-initialized). + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + + void *Allocate(SizeClassAllocator *allocator, uptr class_id) { + CHECK_LT(class_id, kNumClasses); + AllocatorFreeList *free_list = &free_lists_[class_id]; + if (free_list->empty()) + allocator->BulkAllocate(class_id, free_list); + CHECK(!free_list->empty()); + void *res = free_list->front(); + free_list->pop_front(); + return res; + } + + void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { + CHECK_LT(class_id, kNumClasses); + free_lists_[class_id].push_front(reinterpret_cast<AllocatorListNode*>(p)); + } + + void Drain(SizeClassAllocator *allocator) { + for (uptr i = 0; i < kNumClasses; i++) { + allocator->BulkDeallocate(i, &free_lists_[i]); + CHECK(free_lists_[i].empty()); + } + } + + // private: + AllocatorFreeList free_lists_[kNumClasses]; +}; + +// 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). +// The result is always page-aligned. +class LargeMmapAllocator { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + void *Allocate(uptr size, uptr alignment) { + CHECK_LE(alignment, kPageSize); // Not implemented. Do we need it? + uptr map_size = RoundUpMapSize(size); + void *map = MmapOrDie(map_size, "LargeMmapAllocator"); + void *res = reinterpret_cast<void*>(reinterpret_cast<uptr>(map) + + kPageSize); + Header *h = GetHeader(res); + h->size = size; + { + SpinMutexLock l(&mutex_); + h->next = list_; + h->prev = 0; + if (list_) + list_->prev = h; + list_ = h; + } + return res; + } + + void Deallocate(void *p) { + Header *h = GetHeader(p); + uptr map_size = RoundUpMapSize(h->size); + { + SpinMutexLock l(&mutex_); + Header *prev = h->prev; + Header *next = h->next; + if (prev) + prev->next = next; + if (next) + next->prev = prev; + if (h == list_) + list_ = next; + } + UnmapOrDie(h, map_size); + } + + uptr TotalMemoryUsed() { + SpinMutexLock l(&mutex_); + uptr res = 0; + for (Header *l = list_; l; l = l->next) { + res += RoundUpMapSize(l->size); + } + return res; + } + + bool PointerIsMine(void *p) { + // Fast check. + if ((reinterpret_cast<uptr>(p) % kPageSize) != 0) return false; + SpinMutexLock l(&mutex_); + for (Header *l = list_; l; l = l->next) { + if (GetUser(l) == p) return true; + } + return false; + } + + uptr GetActuallyAllocatedSize(void *p) { + return RoundUpMapSize(GetHeader(p)->size) - kPageSize; + } + + // At least kPageSize/2 metadata bytes is available. + void *GetMetaData(void *p) { + return GetHeader(p) + 1; + } + + private: + struct Header { + uptr size; + Header *next; + Header *prev; + }; + + Header *GetHeader(void *p) { + return reinterpret_cast<Header*>(reinterpret_cast<uptr>(p) - kPageSize); + } + + void *GetUser(Header *h) { + return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + kPageSize); + } + + uptr RoundUpMapSize(uptr size) { + return RoundUpTo(size, kPageSize) + kPageSize; + } + + Header *list_; + 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 <class PrimaryAllocator, class AllocatorCache, + class SecondaryAllocator> // NOLINT +class CombinedAllocator { + public: + void Init() { + primary_.Init(); + secondary_.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 (alignment > 8) + size = RoundUpTo(size, alignment); + void *res; + if (primary_.CanAllocate(size, alignment)) + res = cache->Allocate(&primary_, primary_.ClassID(size)); + else + res = secondary_.Allocate(size, alignment); + if (alignment > 8) + CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0); + if (cleared) + 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(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); + } + + void *GetMetaData(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetMetaData(p); + return secondary_.GetMetaData(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 SwallowCache(AllocatorCache *cache) { + cache->Drain(&primary_); + } + + private: + PrimaryAllocator primary_; + SecondaryAllocator secondary_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_ALLOCATOR_H diff --git a/lib/sanitizer_common/sanitizer_atomic.h b/lib/sanitizer_common/sanitizer_atomic.h new file mode 100644 index 0000000..61e6dfd --- /dev/null +++ b/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/lib/sanitizer_common/sanitizer_atomic_clang.h b/lib/sanitizer_common/sanitizer_atomic_clang.h new file mode 100644 index 0000000..af70441 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_atomic_clang.h @@ -0,0 +1,122 @@ +//===-- 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<typename T> +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; + 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<typename T> +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<typename T> +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<typename T> +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<typename T> +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<typename T> +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<typename T> +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/lib/sanitizer_common/sanitizer_atomic_msvc.h b/lib/sanitizer_common/sanitizer_atomic_msvc.h new file mode 100644 index 0000000..2a15b59 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_atomic_msvc.h @@ -0,0 +1,112 @@ +//===-- 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) + +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<typename T> +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; + 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<typename T> +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); +} + +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; +} + +} // namespace __sanitizer + +#endif // SANITIZER_ATOMIC_CLANG_H diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc new file mode 100644 index 0000000..6dd1ff9 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_common.cc @@ -0,0 +1,100 @@ +//===-- 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_libc.h" + +namespace __sanitizer { + +void RawWrite(const char *buffer) { + static const char *kRawWriteError = "RawWrite can't output requested buffer!"; + uptr length = (uptr)internal_strlen(buffer); + if (length != internal_write(2, buffer, length)) { + internal_write(2, kRawWriteError, internal_strlen(kRawWriteError)); + Die(); + } +} + +uptr ReadFileToBuffer(const char *file_name, char **buff, + uptr *buff_size, uptr max_len) { + const uptr kMinFileLen = kPageSize; + 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) { + fd_t fd = internal_open(file_name, /*write*/ false); + if (fd == kInvalidFd) return 0; + 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 + kPageSize <= size) { + uptr just_read = internal_read(fd, *buff + read_len, kPageSize); + 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; +} + +// We don't want to use std::sort to avoid including <algorithm>, as +// we may end up with two implementation of std::sort - one in instrumented +// code, and the other in runtime. +// qsort() from stdlib won't work as it calls malloc(), which results +// in deadlock in ASan allocator. +// We re-implement in-place sorting w/o recursion as straightforward heapsort. +void SortArray(uptr *array, uptr size) { + 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 (array[j] > array[p]) + Swap(array[j], array[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(array[0], array[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 && array[left] > array[max_ind]) + max_ind = left; + if (right < i && array[right] > array[max_ind]) + max_ind = right; + if (max_ind != j) + Swap(array[j], array[max_ind]); + else + break; + } + } +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h new file mode 100644 index 0000000..4c7c1e9 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_common.h @@ -0,0 +1,123 @@ +//===-- 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" + +namespace __sanitizer { + +// Constants. +const uptr kWordSize = __WORDSIZE / 8; +const uptr kWordSizeInBits = 8 * kWordSize; +const uptr kPageSizeBits = 12; +const uptr kPageSize = 1UL << kPageSizeBits; +const uptr kCacheLineSize = 64; +#ifndef _WIN32 +const uptr kMmapGranularity = kPageSize; +#else +const uptr kMmapGranularity = 1UL << 16; +#endif + +// Threads +int GetPid(); +uptr GetThreadSelf(); +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom); + +// Memory management +void *MmapOrDie(uptr size, const char *mem_type); +void UnmapOrDie(void *addr, uptr size); +void *MmapFixedNoReserve(uptr fixed_addr, uptr size); +void *Mprotect(uptr fixed_addr, uptr size); +// Used to check if we can map shadow memory to a fixed location. +bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); + +// Internal allocator +void *InternalAlloc(uptr size); +void InternalFree(void *p); +// Given the pointer p into a valid allocated block, +// returns a pointer to the beginning of the block. +void *InternalAllocBlock(void *p); + +// IO +void RawWrite(const char *buffer); +void Printf(const char *format, ...); +void Report(const char *format, ...); + +// 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); + +const char *GetEnv(const char *name); +const char *GetPwd(); + +// Other +void DisableCoreDumper(); +void DumpProcessMap(); +void SleepForSeconds(int seconds); +void SleepForMillis(int millis); +void NORETURN Exit(int exitcode); +void NORETURN Abort(); +int Atexit(void (*function)(void)); +void SortArray(uptr *array, uptr size); + +// Math +INLINE bool IsPowerOfTwo(uptr x) { + return (x & (x - 1)) == 0; +} +INLINE uptr RoundUpTo(uptr size, uptr boundary) { + CHECK(IsPowerOfTwo(boundary)); + return (size + boundary - 1) & ~(boundary - 1); +} +// Don't use std::min, std::max or std::swap, to minimize dependency +// on libstdc++. +template<class T> T Min(T a, T b) { return a < b ? a : b; } +template<class T> T Max(T a, T b) { return a > b ? a : b; } +template<class T> 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 __WORDSIZE == 64 +# define FIRST_32_SECOND_64(a, b) (b) +#else +# define FIRST_32_SECOND_64(a, b) (a) +#endif + +} // namespace __sanitizer + +#endif // SANITIZER_COMMON_H diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc new file mode 100644 index 0000000..cdeeb78 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_flags.cc @@ -0,0 +1,82 @@ +//===-- 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 { + +static char *GetFlagValue(const char *env, const char *name) { + if (env == 0) + return 0; + const char *pos = internal_strstr(env, name); + const char *end; + if (pos == 0) + return 0; + pos += internal_strlen(name); + 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 { + end = internal_strchr(pos, ' '); + } + if (end == 0) + end = pos + internal_strlen(pos); + } + int len = end - pos; + char *f = (char*)InternalAlloc(len + 1); + internal_memcpy(f, pos, len); + f[len] = '\0'; + return f; +} + +void ParseFlag(const char *env, bool *flag, const char *name) { + char *val = GetFlagValue(env, name); + if (val == 0) + return; + if (0 == internal_strcmp(val, "0") || + 0 == internal_strcmp(val, "no") || + 0 == internal_strcmp(val, "false")) + *flag = false; + if (0 == internal_strcmp(val, "1") || + 0 == internal_strcmp(val, "yes") || + 0 == internal_strcmp(val, "true")) + *flag = true; + InternalFree(val); +} + +void ParseFlag(const char *env, int *flag, const char *name) { + char *val = GetFlagValue(env, name); + if (val == 0) + return; + *flag = internal_atoll(val); + InternalFree(val); +} + +void ParseFlag(const char *env, const char **flag, const char *name) { + const char *val = GetFlagValue(env, name); + if (val == 0) + return; + *flag = val; +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_flags.h b/lib/sanitizer_common/sanitizer_flags.h new file mode 100644 index 0000000..b7ce452 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_flags.h @@ -0,0 +1,27 @@ +//===-- 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); + +} // namespace __sanitizer + +#endif // SANITIZER_FLAGS_H diff --git a/lib/sanitizer_common/sanitizer_interface_defs.h b/lib/sanitizer_common/sanitizer_interface_defs.h new file mode 100644 index 0000000..2395ea5 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_interface_defs.h @@ -0,0 +1,56 @@ +//===-- sanitizer_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. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer. +// It contains basic macro and types. +// NOTE: This file may be included into user code. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_INTERFACE_DEFS_H +#define SANITIZER_INTERFACE_DEFS_H + +// ----------- ATTENTION ------------- +// This header should NOT include any other headers to avoid portability issues. + +#if defined(_WIN32) +// FIXME find out what we need on Windows. __declspec(dllexport) ? +# define SANITIZER_INTERFACE_ATTRIBUTE +# 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 + +// __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 { + +typedef unsigned long uptr; // NOLINT +typedef signed long sptr; // NOLINT +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 + +} // namespace __sanitizer + +#endif // SANITIZER_INTERFACE_DEFS_H diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h new file mode 100644 index 0000000..b8cf61f --- /dev/null +++ b/lib/sanitizer_common/sanitizer_internal_defs.h @@ -0,0 +1,163 @@ +//===-- 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_interface_defs.h" +using namespace __sanitizer; // NOLINT +// ----------- ATTENTION ------------- +// This header should NOT include any other headers to avoid portability issues. + +// Common defs. +#define INLINE static inline +#define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +#define WEAK SANITIZER_WEAK_ATTRIBUTE + +// Platform-specific defs. +#if defined(_WIN32) +typedef unsigned long DWORD; // NOLINT +# define ALWAYS_INLINE __declspec(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 +#else // _WIN32 +# define ALWAYS_INLINE __attribute__((always_inline)) +# define ALIAS(x) __attribute__((alias(x))) +# 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 +# ifdef __cplusplus +# define NOTHROW throw() +# else +# define NOTHROW __attribute__((__nothrow__)) +#endif +#endif // _WIN32 + +// We have no equivalent of these on Windows. +#ifndef _WIN32 +# define LIKELY(x) __builtin_expect(!!(x), 1) +# define UNLIKELY(x) __builtin_expect(!!(x), 0) +# define UNUSED __attribute__((unused)) +# define USED __attribute__((used)) +#endif + +#if defined(_WIN32) +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); + +// If __WORDSIZE was undefined by the platform, define it in terms of the +// compiler built-ins __LP64__ and _WIN64. +#ifndef __WORDSIZE +# if __LP64__ || defined(_WIN64) +# define __WORDSIZE 64 +# else +# define __WORDSIZE 32 +# endif +#endif // __WORDSIZE + +// NOTE: Functions below must be defined in each run-time. +namespace __sanitizer { +void NORETURN Die(); +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 UNIMPLEMENTED() CHECK("unimplemented" && 0) + +#define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__) + +#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). +#if __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 // __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)) + +#endif // SANITIZER_DEFS_H diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc new file mode 100644 index 0000000..c433242 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_libc.cc @@ -0,0 +1,182 @@ +//===-- 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_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_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; +} + +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_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]; + for (; i < n; i++) + dst[i] = '\0'; + 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); + } +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h new file mode 100644 index 0000000..8da4286c --- /dev/null +++ b/lib/sanitizer_common/sanitizer_libc.h @@ -0,0 +1,69 @@ +//===-- 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. +// NOTE: This file may be included into user code. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LIBC_H +#define SANITIZER_LIBC_H + +// ----------- ATTENTION ------------- +// This header should NOT include any other headers from sanitizer runtime. +#include "sanitizer_interface_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); +// 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); +int internal_strcmp(const char *s1, const char *s2); +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); + +// Memory +void *internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset); +int internal_munmap(void *addr, uptr length); + +// I/O +typedef int fd_t; +const fd_t kInvalidFd = -1; +int internal_close(fd_t fd); +fd_t internal_open(const char *filename, bool write); +uptr internal_read(fd_t fd, void *buf, uptr count); +uptr internal_write(fd_t fd, const void *buf, uptr count); +uptr internal_filesize(fd_t fd); // -1 on error. +int internal_dup2(int oldfd, int newfd); +int internal_snprintf(char *buffer, uptr length, const char *format, ...); + +// Threading +int internal_sched_yield(); + +} // namespace __sanitizer + +#endif // SANITIZER_LIBC_H diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc new file mode 100644 index 0000000..70e2eb3 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_linux.cc @@ -0,0 +1,348 @@ +//===-- 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. +//===----------------------------------------------------------------------===// +#ifdef __linux__ + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_symbolizer.h" + +#include <elf.h> +#include <fcntl.h> +#include <link.h> +#include <pthread.h> +#include <sched.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +namespace __sanitizer { + +// --------------- sanitizer_libc.h +void *internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset) { +#if __WORDSIZE == 64 + return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset); +#else + return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); +#endif +} + +int internal_munmap(void *addr, uptr length) { + return syscall(__NR_munmap, addr, length); +} + +int internal_close(fd_t fd) { + return syscall(__NR_close, fd); +} + +fd_t internal_open(const char *filename, bool write) { + return syscall(__NR_open, filename, + write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + return (uptr)syscall(__NR_read, fd, buf, count); +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + return (uptr)syscall(__NR_write, fd, buf, count); +} + +uptr internal_filesize(fd_t fd) { +#if __WORDSIZE == 64 + struct stat st; + if (syscall(__NR_fstat, fd, &st)) + return -1; +#else + struct stat64 st; + if (syscall(__NR_fstat64, fd, &st)) + return -1; +#endif + return (uptr)st.st_size; +} + +int internal_dup2(int oldfd, int newfd) { + return syscall(__NR_dup2, oldfd, newfd); +} + +int internal_sched_yield() { + return syscall(__NR_sched_yield); +} + +// ----------------- sanitizer_common.h +void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, + uptr *stack_bottom) { + static const uptr kMaxThreadStackSize = 256 * (1 << 20); // 256M + 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. + ProcessMaps proc_maps; + uptr start, end, offset; + uptr prev_end = 0; + while (proc_maps.Next(&start, &end, &offset, 0, 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); + + *stack_top = (uptr)stackaddr + stacksize; + *stack_bottom = (uptr)stackaddr; + CHECK(stacksize < kMaxThreadStackSize); // Sanity check. +} + +// 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. +} + +// ------------------ sanitizer_symbolizer.h +typedef ElfW(Ehdr) Elf_Ehdr; +typedef ElfW(Shdr) Elf_Shdr; +typedef ElfW(Phdr) Elf_Phdr; + +bool FindDWARFSection(uptr object_file_addr, const char *section_name, + DWARFSection *section) { + Elf_Ehdr *exe = (Elf_Ehdr*)object_file_addr; + Elf_Shdr *sections = (Elf_Shdr*)(object_file_addr + exe->e_shoff); + uptr section_names = object_file_addr + + sections[exe->e_shstrndx].sh_offset; + for (int i = 0; i < exe->e_shnum; i++) { + Elf_Shdr *current_section = §ions[i]; + const char *current_name = (const char*)section_names + + current_section->sh_name; + if (IsFullNameOfDWARFSection(current_name, section_name)) { + section->data = (const char*)object_file_addr + + current_section->sh_offset; + section->size = current_section->sh_size; + return true; + } + } + return false; +} + +#ifdef ANDROID +uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) { + UNIMPLEMENTED(); +} +#else // ANDROID +struct DlIteratePhdrData { + ModuleDIContext *modules; + uptr current_n; + uptr max_n; +}; + +static const uptr kMaxPathLength = 512; + +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; + char *module_name = 0; + if (data->current_n == 0) { + // First module is the binary itself. + module_name = (char*)InternalAlloc(kMaxPathLength); + uptr module_name_len = readlink("/proc/self/exe", + module_name, kMaxPathLength); + CHECK_NE(module_name_len, (uptr)-1); + CHECK_LT(module_name_len, kMaxPathLength); + module_name[module_name_len] = '\0'; + } else if (info->dlpi_name) { + module_name = internal_strdup(info->dlpi_name); + } + if (module_name == 0 || module_name[0] == '\0') + return 0; + void *mem = &data->modules[data->current_n]; + ModuleDIContext *cur_module = new(mem) ModuleDIContext(module_name, + 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); + } + } + InternalFree(module_name); + return 0; +} + +uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) { + CHECK(modules); + DlIteratePhdrData data = {modules, 0, max_modules}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return data.current_n; +} +#endif // ANDROID + +// ----------------- sanitizer_procmaps.h +ProcessMaps::ProcessMaps() { + proc_self_maps_buff_len_ = + ReadFileToBuffer("/proc/self/maps", &proc_self_maps_buff_, + &proc_self_maps_buff_mmaped_size_, 1 << 26); + CHECK_GT(proc_self_maps_buff_len_, 0); + // internal_write(2, proc_self_maps_buff_, proc_self_maps_buff_len_); + Reset(); +} + +ProcessMaps::~ProcessMaps() { + UnmapOrDie(proc_self_maps_buff_, proc_self_maps_buff_mmaped_size_); +} + +void ProcessMaps::Reset() { + current_ = proc_self_maps_buff_; +} + +// 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 IsOnOf(char c, char c1, char c2) { + return c == c1 || c == c2; +} + +static bool IsDecimal(char c) { + return c >= '0' && c <= '9'; +} + +bool ProcessMaps::Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size) { + char *last = proc_self_maps_buff_ + proc_self_maps_buff_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_++, ' '); + CHECK(IsOnOf(*current_++, '-', 'r')); + CHECK(IsOnOf(*current_++, '-', 'w')); + CHECK(IsOnOf(*current_++, '-', 'x')); + CHECK(IsOnOf(*current_++, 's', 'p')); + CHECK_EQ(*current_++, ' '); + *offset = ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + ParseHex(¤t_); + CHECK_EQ(*current_++, ':'); + ParseHex(¤t_); + CHECK_EQ(*current_++, ' '); + while (IsDecimal(*current_)) + current_++; + 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 ProcessMaps. +bool ProcessMaps::GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], + uptr filename_size) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); +} + +} // namespace __sanitizer + +#endif // __linux__ diff --git a/lib/sanitizer_common/sanitizer_list.h b/lib/sanitizer_common/sanitizer_list.h new file mode 100644 index 0000000..ef98eee --- /dev/null +++ b/lib/sanitizer_common/sanitizer_list.h @@ -0,0 +1,120 @@ +//===-- 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<class Item> +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<Item> *l) { + CHECK_NE(this, l); + if (empty()) { + *this = *l; + } else if (!l->empty()) { + l->last_->next = first_; + first_ = l->first_; + size_ += l->size(); + } + l->clear(); + } + + void append_back(IntrusiveList<Item> *l) { + CHECK_NE(this, l); + 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/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc new file mode 100644 index 0000000..e64c2de --- /dev/null +++ b/lib/sanitizer_common/sanitizer_mac.cc @@ -0,0 +1,243 @@ +//===-- 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. +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_symbolizer.h" + +#include <crt_externs.h> // for _NSGetEnviron +#include <fcntl.h> +#include <mach-o/dyld.h> +#include <mach-o/loader.h> +#include <pthread.h> +#include <sched.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace __sanitizer { + +// ---------------------- sanitizer_libc.h +void *internal_mmap(void *addr, size_t length, int prot, int flags, + int fd, u64 offset) { + return mmap(addr, length, prot, flags, fd, offset); +} + +int internal_munmap(void *addr, uptr length) { + return munmap(addr, length); +} + +int internal_close(fd_t fd) { + return close(fd); +} + +fd_t internal_open(const char *filename, bool write) { + return 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_filesize(fd_t fd) { + struct stat st = {}; + if (fstat(fd, &st)) + return -1; + return (uptr)st.st_size; +} + +int internal_dup2(int oldfd, int newfd) { + return dup2(oldfd, newfd); +} + +int internal_sched_yield() { + return sched_yield(); +} + +// ----------------- sanitizer_common.h +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(); + 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; +} + +// ------------------ sanitizer_symbolizer.h +bool FindDWARFSection(uptr object_file_addr, const char *section_name, + DWARFSection *section) { + UNIMPLEMENTED(); + return false; +} + +uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) { + UNIMPLEMENTED(); + return 0; +}; + +// ----------------- sanitizer_procmaps.h + +ProcessMaps::ProcessMaps() { + Reset(); +} + +ProcessMaps::~ProcessMaps() { +} + +// 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 ProcessMaps::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 ProcessMaps state. + current_image_ = _dyld_image_count(); + current_load_cmd_count_ = -1; + current_load_cmd_addr_ = 0; + current_magic_ = 0; +} + +// 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<u32 kLCSegment, typename SegmentCommand> +bool ProcessMaps::NextSegmentLoad( + uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size) { + 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) *offset = sc->fileoff; + if (filename) { + internal_strncpy(filename, _dyld_get_image_name(current_image_), + filename_size); + } + return true; + } + return false; +} + +bool ProcessMaps::Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size) { + 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; + 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<LC_SEGMENT_64, struct segment_command_64>( + start, end, offset, filename, filename_size)) + return true; + break; + } +#endif + case MH_MAGIC: { + if (NextSegmentLoad<LC_SEGMENT, struct segment_command>( + start, end, offset, filename, filename_size)) + 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 ProcessMaps::GetObjectNameAndOffset(uptr addr, uptr *offset, + char filename[], + uptr filename_size) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); +} + +} // namespace __sanitizer + +#endif // __APPLE__ diff --git a/lib/sanitizer_common/sanitizer_mutex.h b/lib/sanitizer_common/sanitizer_mutex.h new file mode 100644 index 0000000..ca3e2f9 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_mutex.h @@ -0,0 +1,100 @@ +//===-- 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 SpinMutex { + public: + SpinMutex() { + atomic_store(&state_, 0, memory_order_relaxed); + } + + void Lock() { + if (atomic_exchange(&state_, 1, memory_order_acquire) == 0) + return; + LockSlow(); + } + + void Unlock() { + atomic_store(&state_, 0, memory_order_release); + } + + 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; + } + } + + SpinMutex(const SpinMutex&); + void operator=(const SpinMutex&); +}; + +template<typename MutexType> +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<typename MutexType> +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<SpinMutex> SpinMutexLock; + +} // namespace __sanitizer + +#endif // SANITIZER_MUTEX_H diff --git a/lib/sanitizer_common/sanitizer_placement_new.h b/lib/sanitizer_common/sanitizer_placement_new.h new file mode 100644 index 0000000..f133a6f --- /dev/null +++ b/lib/sanitizer_common/sanitizer_placement_new.h @@ -0,0 +1,33 @@ +//===-- 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" + +namespace __sanitizer { +#if (__WORDSIZE == 64) || defined(__APPLE__) +typedef uptr operator_new_ptr_type; +#else +typedef u32 operator_new_ptr_type; +#endif +} // namespace __sanitizer + +inline void *operator new(__sanitizer::operator_new_ptr_type sz, void *p) { + return p; +} + +#endif // SANITIZER_PLACEMENT_NEW_H diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc new file mode 100644 index 0000000..4caee3b --- /dev/null +++ b/lib/sanitizer_common/sanitizer_posix.cc @@ -0,0 +1,164 @@ +//===-- 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. +//===----------------------------------------------------------------------===// +#if defined(__linux__) || defined(__APPLE__) + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_procmaps.h" + +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +namespace __sanitizer { + +// ------------- sanitizer_common.h + +int GetPid() { + return getpid(); +} + +uptr GetThreadSelf() { + return (uptr)pthread_self(); +} + +void *MmapOrDie(uptr size, const char *mem_type) { + size = RoundUpTo(size, kPageSize); + void *res = internal_mmap(0, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (res == (void*)-1) { + Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n", + size, size, mem_type); + CHECK("unable to mmap" && 0); + } + return res; +} + +void UnmapOrDie(void *addr, uptr size) { + if (!addr || !size) return; + int res = internal_munmap(addr, size); + if (res != 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) { + return internal_mmap((void*)fixed_addr, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, + -1, 0); +} + +void *Mprotect(uptr fixed_addr, uptr size) { + return 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) { + fd_t fd = internal_open(file_name, false); + CHECK_NE(fd, kInvalidFd); + uptr fsize = internal_filesize(fd); + CHECK_NE(fsize, (uptr)-1); + CHECK_GT(fsize, 0); + *buff_size = RoundUpTo(fsize, kPageSize); + void *map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); + return (map == MAP_FAILED) ? 0 : 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) { + ProcessMaps procmaps; + uptr start, end; + while (procmaps.Next(&start, &end, + /*offset*/0, /*filename*/0, /*filename_size*/0)) { + if (!IntervalsAreSeparate(start, end, range_start, range_end)) + return false; + } + return true; +} + +void DumpProcessMap() { + ProcessMaps proc_maps; + 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)) { + 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"); +} + +void DisableCoreDumper() { + struct rlimit nocore; + nocore.rlim_cur = 0; + nocore.rlim_max = 0; + setrlimit(RLIMIT_CORE, &nocore); +} + +void SleepForSeconds(int seconds) { + sleep(seconds); +} + +void SleepForMillis(int millis) { + usleep(millis * 1000); +} + +void Exit(int exitcode) { + _exit(exitcode); +} + +void Abort() { + abort(); +} + +int Atexit(void (*function)(void)) { +#ifndef SANITIZER_GO + return atexit(function); +#else + return 0; +#endif +} + +} // namespace __sanitizer + +#endif // __linux__ || __APPLE_ diff --git a/lib/sanitizer_common/sanitizer_printf.cc b/lib/sanitizer_common/sanitizer_printf.cc new file mode 100644 index 0000000..7b70c3a --- /dev/null +++ b/lib/sanitizer_common/sanitizer_printf.cc @@ -0,0 +1,185 @@ +//===-- 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 <stdio.h> +#include <stdarg.h> + +namespace __sanitizer { + +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. +static int AppendUnsigned(char **buff, const char *buff_end, u64 num, + u8 base, u8 minimal_num_length) { + uptr const kMaxLen = 30; + RAW_CHECK(base == 10 || base == 16); + RAW_CHECK(minimal_num_length < kMaxLen); + uptr num_buffer[kMaxLen]; + uptr pos = 0; + do { + RAW_CHECK_MSG(pos < kMaxLen, "appendNumber buffer overflow"); + num_buffer[pos++] = num % base; + num /= base; + } while (num > 0); + while (pos < minimal_num_length) num_buffer[pos++] = 0; + int result = 0; + while (pos-- > 0) { + uptr digit = num_buffer[pos]; + result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit + : 'a' + digit - 10); + } + return result; +} + +static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num) { + int result = 0; + if (num < 0) { + result += AppendChar(buff, buff_end, '-'); + num = -num; + } + result += AppendUnsigned(buff, buff_end, (u64)num, 10, 0); + return result; +} + +static int AppendString(char **buff, const char *buff_end, const char *s) { + if (s == 0) + s = "<null>"; + 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, + (__WORDSIZE == 64) ? 12 : 8); + return result; +} + +int VSNPrintf(char *buff, int buff_length, + const char *format, va_list args) { + static const char *kPrintfFormatsHelp = "Supported Printf formats: " + "%%[z]{d,u,x}; %%p; %%s\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_z = (*cur == 'z'); + cur += have_z; + s64 dval; + u64 uval; + switch (*cur) { + case 'd': { + dval = have_z ? va_arg(args, sptr) + : va_arg(args, int); + result += AppendSignedDecimal(&buff, buff_end, dval); + break; + } + case 'u': + case 'x': { + uval = have_z ? va_arg(args, uptr) + : va_arg(args, unsigned); + result += AppendUnsigned(&buff, buff_end, uval, + (*cur == 'u') ? 10 : 16, 0); + break; + } + case 'p': { + RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp); + result += AppendPointer(&buff, buff_end, va_arg(args, uptr)); + break; + } + case 's': { + RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp); + result += AppendString(&buff, buff_end, va_arg(args, char*)); + break; + } + case '%' : { + RAW_CHECK_MSG(!have_z, 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; +} + +void Printf(const char *format, ...) { + const int kLen = 1024 * 4; + char *buffer = (char*)MmapOrDie(kLen, __FUNCTION__); + va_list args; + va_start(args, format); + int needed_length = VSNPrintf(buffer, kLen, format, args); + va_end(args); + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n"); + RawWrite(buffer); + UnmapOrDie(buffer, kLen); +} + +// 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; +} + +// Like Printf, but prints the current PID before the output string. +void Report(const char *format, ...) { + const int kLen = 1024 * 4; + char *buffer = (char*)MmapOrDie(kLen, __FUNCTION__); + int needed_length = internal_snprintf(buffer, kLen, "==%d== ", GetPid()); + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); + va_list args; + va_start(args, format); + needed_length += VSNPrintf(buffer + needed_length, kLen - needed_length, + format, args); + va_end(args); + RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); + RawWrite(buffer); + UnmapOrDie(buffer, kLen); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_procmaps.h b/lib/sanitizer_common/sanitizer_procmaps.h new file mode 100644 index 0000000..e7f9cac --- /dev/null +++ b/lib/sanitizer_common/sanitizer_procmaps.h @@ -0,0 +1,82 @@ +//===-- 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" + +namespace __sanitizer { + +class ProcessMaps { + public: + ProcessMaps(); + bool Next(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size); + 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); + ~ProcessMaps(); + + private: + // 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) { + Reset(); + uptr start, end, file_offset; + for (int i = 0; Next(&start, &end, &file_offset, filename, filename_size); + 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 defined __linux__ + char *proc_self_maps_buff_; + uptr proc_self_maps_buff_mmaped_size_; + uptr proc_self_maps_buff_len_; + char *current_; +#elif defined __APPLE__ + template<u32 kLCSegment, typename SegmentCommand> + bool NextSegmentLoad(uptr *start, uptr *end, uptr *offset, + char filename[], uptr filename_size); + int current_image_; + u32 current_magic_; + int current_load_cmd_count_; + char *current_load_cmd_addr_; +#endif +}; + +} // namespace __sanitizer + +#endif // SANITIZER_PROCMAPS_H diff --git a/lib/sanitizer_common/sanitizer_symbolizer.cc b/lib/sanitizer_common/sanitizer_symbolizer.cc new file mode 100644 index 0000000..85eb076 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer.cc @@ -0,0 +1,144 @@ +//===-- 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 is a stub for LLVM-based symbolizer. +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. See sanitizer.h for details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +bool IsFullNameOfDWARFSection(const char *full_name, const char *short_name) { + // Skip "__DWARF," prefix. + if (0 == internal_strncmp(full_name, "__DWARF,", 8)) { + full_name += 8; + } + // Skip . and _ prefices. + while (*full_name == '.' || *full_name == '_') { + full_name++; + } + return 0 == internal_strcmp(full_name, short_name); +} + +void AddressInfo::Clear() { + InternalFree(module); + InternalFree(function); + InternalFree(file); + internal_memset(this, 0, sizeof(AddressInfo)); +} + +ModuleDIContext::ModuleDIContext(const char *module_name, uptr base_address) { + full_name_ = internal_strdup(module_name); + short_name_ = internal_strrchr(module_name, '/'); + if (short_name_ == 0) { + short_name_ = full_name_; + } else { + short_name_++; + } + base_address_ = base_address; + n_ranges_ = 0; + mapped_addr_ = 0; + mapped_size_ = 0; +} + +void ModuleDIContext::addAddressRange(uptr beg, uptr end) { + CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); + ranges_[n_ranges_].beg = beg; + ranges_[n_ranges_].end = end; + n_ranges_++; +} + +bool ModuleDIContext::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; +} + +void ModuleDIContext::getAddressInfo(AddressInfo *info) { + info->module = internal_strdup(full_name_); + info->module_offset = info->address - base_address_; + if (mapped_addr_ == 0) + CreateDIContext(); + // FIXME: Use the actual debug info context here. + info->function = 0; + info->file = 0; + info->line = 0; + info->column = 0; +} + +void ModuleDIContext::CreateDIContext() { + mapped_addr_ = (uptr)MapFileToMemory(full_name_, &mapped_size_); + CHECK(mapped_addr_); + DWARFSection debug_info; + DWARFSection debug_abbrev; + DWARFSection debug_line; + DWARFSection debug_aranges; + DWARFSection debug_str; + FindDWARFSection(mapped_addr_, "debug_info", &debug_info); + FindDWARFSection(mapped_addr_, "debug_abbrev", &debug_abbrev); + FindDWARFSection(mapped_addr_, "debug_line", &debug_line); + FindDWARFSection(mapped_addr_, "debug_aranges", &debug_aranges); + FindDWARFSection(mapped_addr_, "debug_str", &debug_str); + // FIXME: Construct actual debug info context using mapped_addr, + // mapped_size and pointers to DWARF sections in memory. +} + +class Symbolizer { + public: + uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { + if (max_frames == 0) + return 0; + AddressInfo *info = &frames[0]; + info->Clear(); + info->address = addr; + ModuleDIContext *module = FindModuleForAddress(addr); + if (module) { + module->getAddressInfo(info); + return 1; + } + return 0; + } + + private: + ModuleDIContext *FindModuleForAddress(uptr address) { + if (modules_ == 0) { + modules_ = (ModuleDIContext*)InternalAlloc( + kMaxNumberOfModuleContexts * sizeof(ModuleDIContext)); + CHECK(modules_); + n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts); + CHECK_GT(n_modules_, 0); + CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); + } + for (uptr i = 0; i < n_modules_; i++) { + if (modules_[i].containsAddress(address)) { + return &modules_[i]; + } + } + return 0; + } + static const uptr kMaxNumberOfModuleContexts = 4096; + // Array of module debug info contexts is leaked. + ModuleDIContext *modules_; + uptr n_modules_; +}; + +static Symbolizer symbolizer; // Linker initialized. + +uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames) { + return symbolizer.SymbolizeCode(address, frames, max_frames); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_symbolizer.h b/lib/sanitizer_common/sanitizer_symbolizer.h new file mode 100644 index 0000000..c813e80 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer.h @@ -0,0 +1,100 @@ +//===-- 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 intended to be used by both +// AddressSanitizer and ThreadSanitizer to symbolize a given +// address. It is an analogue of addr2line utility and allows to map +// instruction address to a location in source code at run-time. +// +// Symbolizer is planned to use debug information (in DWARF format) +// in a binary via interface defined in "llvm/DebugInfo/DIContext.h" +// +// Symbolizer code should be called from the run-time library of +// dynamic tools, and generally should not call memory allocation +// routines or other system library functions intercepted by those tools. +// Instead, Symbolizer code should use their replacements, defined in +// "compiler-rt/lib/sanitizer_common/sanitizer_libc.h". +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_H +#define SANITIZER_SYMBOLIZER_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +// WARNING: Do not include system headers here. See details above. + +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(); +}; + +// 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. +// This function should NOT be called from two threads simultaneously. +uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames); + +// Debug info routines +struct DWARFSection { + const char *data; + uptr size; + DWARFSection() { + data = 0; + size = 0; + } +}; +// Returns true on success. +bool FindDWARFSection(uptr object_file_addr, const char *section_name, + DWARFSection *section); +bool IsFullNameOfDWARFSection(const char *full_name, const char *short_name); + +class ModuleDIContext { + public: + ModuleDIContext(const char *module_name, uptr base_address); + void addAddressRange(uptr beg, uptr end); + bool containsAddress(uptr address) const; + void getAddressInfo(AddressInfo *info); + + const char *full_name() const { return full_name_; } + + private: + void CreateDIContext(); + + struct AddressRange { + uptr beg; + uptr end; + }; + char *full_name_; + char *short_name_; + uptr base_address_; + static const uptr kMaxNumberOfAddressRanges = 8; + AddressRange ranges_[kMaxNumberOfAddressRanges]; + uptr n_ranges_; + uptr mapped_addr_; + uptr mapped_size_; +}; + +// OS-dependent function that gets the linked list of all loaded modules. +uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules); + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_H diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc new file mode 100644 index 0000000..c68a1fe --- /dev/null +++ b/lib/sanitizer_common/sanitizer_win.cc @@ -0,0 +1,200 @@ +//===-- 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. +//===----------------------------------------------------------------------===// +#ifdef _WIN32 +#include <windows.h> + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +// --------------------- sanitizer_common.h +int GetPid() { + return GetProcessId(GetCurrentProcess()); +} + +uptr GetThreadSelf() { + return GetCurrentThreadId(); +} + +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) { + return VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +} + +void *Mprotect(uptr fixed_addr, uptr size) { + return VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); +} + +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(); +} + +const char *GetEnv(const char *name) { + static char env_buffer[32767] = {}; + + // Note: this implementation stores the result in a static buffer so we only + // allow it to be called just once. + static bool called_once = false; + if (called_once) + UNIMPLEMENTED(); + called_once = true; + + DWORD rv = GetEnvironmentVariableA(name, env_buffer, sizeof(env_buffer)); + if (rv > 0 && rv < sizeof(env_buffer)) + return env_buffer; + return 0; +} + +const char *GetPwd() { + UNIMPLEMENTED(); + return 0; +} + +void DumpProcessMap() { + UNIMPLEMENTED(); +} + +void DisableCoreDumper() { + UNIMPLEMENTED(); +} + +void SleepForSeconds(int seconds) { + Sleep(seconds * 1000); +} + +void SleepForMillis(int millis) { + Sleep(millis); +} + +void Exit(int exitcode) { + _exit(exitcode); +} + +void Abort() { + abort(); + _exit(-1); // abort is not NORETURN on Windows. +} + +int Atexit(void (*function)(void)) { + return atexit(function); +} + +// ------------------ sanitizer_symbolizer.h +bool FindDWARFSection(uptr object_file_addr, const char *section_name, + DWARFSection *section) { + UNIMPLEMENTED(); + return false; +} + +uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) { + UNIMPLEMENTED(); +}; + +// ------------------ sanitizer_libc.h +void *internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset) { + UNIMPLEMENTED(); + return 0; +} + +int internal_munmap(void *addr, uptr length) { + UNIMPLEMENTED(); + return 0; +} + +int internal_close(fd_t fd) { + UNIMPLEMENTED(); + return 0; +} + +fd_t internal_open(const char *filename, bool write) { + UNIMPLEMENTED(); + return 0; +} + +uptr internal_read(fd_t fd, void *buf, uptr count) { + UNIMPLEMENTED(); + return 0; +} + +uptr internal_write(fd_t fd, const void *buf, uptr count) { + if (fd != 2) + 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_filesize(fd_t fd) { + UNIMPLEMENTED(); + return 0; +} + +int internal_dup2(int oldfd, int newfd) { + UNIMPLEMENTED(); + return 0; +} + +int internal_sched_yield() { + UNIMPLEMENTED(); + return 0; +} + +} // namespace __sanitizer + +#endif // _WIN32 diff --git a/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc new file mode 100644 index 0000000..1410f26 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc @@ -0,0 +1,257 @@ +//===-- sanitizer_allocator64_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_allocator64.h. +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_allocator64.h" +#include "gtest/gtest.h" + +#include <algorithm> +#include <vector> + +static const uptr kAllocatorSpace = 0x600000000000ULL; +static const uptr kAllocatorSize = 0x10000000000; // 1T. + +typedef DefaultSizeClassMap SCMap; +typedef + SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 16, SCMap> Allocator; +typedef SizeClassAllocatorLocalCache<Allocator::kNumClasses, Allocator> + AllocatorCache; + +TEST(SanitizerCommon, DefaultSizeClassMap) { +#if 0 + for (uptr i = 0; i < SCMap::kNumClasses; i++) { + // printf("% 3ld: % 5ld (%4lx); ", i, SCMap::Size(i), SCMap::Size(i)); + printf("c%ld => %ld ", i, SCMap::Size(i)); + if ((i % 8) == 7) + printf("\n"); + } + printf("\n"); +#endif + + for (uptr c = 0; c < SCMap::kNumClasses; c++) { + uptr s = SCMap::Size(c); + CHECK_EQ(SCMap::ClassID(s), c); + if (c != SCMap::kNumClasses - 1) + CHECK_EQ(SCMap::ClassID(s + 1), c + 1); + CHECK_EQ(SCMap::ClassID(s - 1), c); + if (c) + CHECK_GT(SCMap::Size(c), SCMap::Size(c-1)); + } + CHECK_EQ(SCMap::ClassID(SCMap::kMaxSize + 1), 0); + + for (uptr s = 1; s <= SCMap::kMaxSize; s++) { + uptr c = SCMap::ClassID(s); + CHECK_LT(c, SCMap::kNumClasses); + CHECK_GE(SCMap::Size(c), s); + if (c > 0) + CHECK_LT(SCMap::Size(c-1), s); + } +} + +TEST(SanitizerCommon, SizeClassAllocator64) { + Allocator a; + a.Init(); + + static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, + 50000, 60000, 100000, 300000, 500000, 1000000, 2000000}; + + std::vector<void *> allocated; + + uptr last_total_allocated = 0; + for (int i = 0; i < 5; i++) { + // Allocate a bunch of chunks. + for (uptr s = 0; s < sizeof(sizes) /sizeof(sizes[0]); s++) { + uptr size = sizes[s]; + // printf("s = %ld\n", size); + uptr n_iter = std::max((uptr)2, 1000000 / size); + for (uptr i = 0; i < n_iter; i++) { + void *x = a.Allocate(size, 1); + allocated.push_back(x); + CHECK(a.PointerIsMine(x)); + CHECK_GE(a.GetActuallyAllocatedSize(x), size); + uptr class_id = a.GetSizeClass(x); + CHECK_EQ(class_id, SCMap::ClassID(size)); + uptr *metadata = reinterpret_cast<uptr*>(a.GetMetaData(x)); + metadata[0] = reinterpret_cast<uptr>(x) + 1; + metadata[1] = 0xABCD; + } + } + // Deallocate all. + for (uptr i = 0; i < allocated.size(); i++) { + void *x = allocated[i]; + uptr *metadata = reinterpret_cast<uptr*>(a.GetMetaData(x)); + CHECK_EQ(metadata[0], reinterpret_cast<uptr>(x) + 1); + CHECK_EQ(metadata[1], 0xABCD); + a.Deallocate(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); + } + + a.TestOnlyUnmap(); +} + + +TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { + Allocator a; + a.Init(); + static volatile void *sink; + + const uptr kNumAllocs = 10000; + void *allocated[kNumAllocs]; + for (uptr i = 0; i < kNumAllocs; i++) { + uptr size = (i % 4096) + 1; + void *x = a.Allocate(size, 1); + allocated[i] = x; + } + // Get Metadata kNumAllocs^2 times. + for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { + sink = a.GetMetaData(allocated[i % kNumAllocs]); + } + for (uptr i = 0; i < kNumAllocs; i++) { + a.Deallocate(allocated[i]); + } + + a.TestOnlyUnmap(); + (void)sink; +} + +void FailInAssertionOnOOM() { + Allocator a; + a.Init(); + const uptr size = 1 << 20; + for (int i = 0; i < 1000000; i++) { + a.Allocate(size, 1); + } + + a.TestOnlyUnmap(); +} + +TEST(SanitizerCommon, SizeClassAllocator64Overflow) { + EXPECT_DEATH(FailInAssertionOnOOM(), + "allocated_user.*allocated_meta.*kRegionSize"); +} + +TEST(SanitizerCommon, LargeMmapAllocator) { + LargeMmapAllocator a; + a.Init(); + + static const int kNumAllocs = 100; + void *allocated[kNumAllocs]; + static const uptr size = 1000; + // Allocate some. + for (int i = 0; i < kNumAllocs; i++) { + allocated[i] = a.Allocate(size, 1); + } + // Deallocate all. + CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); + for (int i = 0; i < kNumAllocs; i++) { + void *p = allocated[i]; + CHECK(a.PointerIsMine(p)); + a.Deallocate(p); + } + // Check that non left. + CHECK_EQ(a.TotalMemoryUsed(), 0); + + // Allocate some more, also add metadata. + for (int i = 0; i < kNumAllocs; i++) { + void *x = a.Allocate(size, 1); + CHECK_GE(a.GetActuallyAllocatedSize(x), size); + uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x)); + *meta = i; + allocated[i] = x; + } + CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); + // Deallocate all in reverse order. + for (int i = 0; i < kNumAllocs; i++) { + int idx = kNumAllocs - i - 1; + void *p = allocated[idx]; + uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(p)); + CHECK_EQ(*meta, idx); + CHECK(a.PointerIsMine(p)); + a.Deallocate(p); + } + CHECK_EQ(a.TotalMemoryUsed(), 0); +} + +TEST(SanitizerCommon, CombinedAllocator) { + typedef Allocator PrimaryAllocator; + typedef LargeMmapAllocator SecondaryAllocator; + typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, + SecondaryAllocator> Allocator; + + AllocatorCache cache; + Allocator a; + a.Init(); + cache.Init(); + const uptr kNumAllocs = 100000; + const uptr kNumIter = 10; + for (uptr iter = 0; iter < kNumIter; iter++) { + std::vector<void*> 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<uptr*>(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<uptr*>(a.GetMetaData(x)); + CHECK_NE(*meta, 0); + CHECK(a.PointerIsMine(x)); + *meta = 0; + a.Deallocate(&cache, x); + } + allocated.clear(); + a.SwallowCache(&cache); + } + a.TestOnlyUnmap(); +} + +static THREADLOCAL AllocatorCache static_allocator_cache; + +TEST(SanitizerCommon, SizeClassAllocatorLocalCache) { + static_allocator_cache.Init(); + + Allocator a; + AllocatorCache cache; + + a.Init(); + cache.Init(); + + const uptr kNumAllocs = 10000; + const int kNumIter = 100; + uptr saved_total = 0; + for (int i = 0; i < kNumIter; i++) { + void *allocated[kNumAllocs]; + for (uptr i = 0; i < kNumAllocs; i++) { + allocated[i] = cache.Allocate(&a, 0); + } + for (uptr i = 0; i < kNumAllocs; i++) { + cache.Deallocate(&a, 0, allocated[i]); + } + cache.Drain(&a); + uptr total_allocated = a.TotalMemoryUsed(); + if (saved_total) + CHECK_EQ(saved_total, total_allocated); + saved_total = total_allocated; + } + + a.TestOnlyUnmap(); +} diff --git a/lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc b/lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc new file mode 100644 index 0000000..cff7823 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc @@ -0,0 +1,99 @@ +//===-- sanitizer_allocator64_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. +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_allocator64.h" +#include <stddef.h> +#include <stdio.h> +#include <unistd.h> +#include <assert.h> + +namespace { +static const uptr kAllocatorSpace = 0x600000000000ULL; +static const uptr kAllocatorSize = 0x10000000000; // 1T. + +typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 16, + DefaultSizeClassMap> PrimaryAllocator; +typedef SizeClassAllocatorLocalCache<PrimaryAllocator::kNumClasses, + PrimaryAllocator> AllocatorCache; +typedef LargeMmapAllocator SecondaryAllocator; +typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, + SecondaryAllocator> Allocator; + +static THREADLOCAL AllocatorCache cache; +static Allocator allocator; + +static int inited = 0; + +__attribute__((constructor)) +void Init() { + if (inited) return; + inited = true; // this must happen before any threads are created. + allocator.Init(); +} + +} // namespace + +namespace __sanitizer { +void NORETURN Die() { + _exit(77); +} +void NORETURN CheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + fprintf(stderr, "CheckFailed: %s:%d %s (%lld %lld)\n", + file, line, cond, v1, v2); + Die(); +} +} + +#if 1 +extern "C" { +void *malloc(size_t size) { + Init(); + assert(inited); + return allocator.Allocate(&cache, size, 8); +} + +void free(void *p) { + assert(inited); + allocator.Deallocate(&cache, p); +} + +void *calloc(size_t nmemb, size_t size) { + assert(inited); + return allocator.Allocate(&cache, nmemb * size, 8, /*cleared=*/true); +} + +void *realloc(void *p, size_t new_size) { + assert(inited); + return allocator.Reallocate(&cache, p, new_size, 8); +} + +void *memalign() { assert(0); } + +int posix_memalign(void **memptr, size_t alignment, size_t size) { + *memptr = allocator.Allocate(&cache, size, alignment); + CHECK_EQ(((uptr)*memptr & (alignment - 1)), 0); + return 0; +} + +void *valloc(size_t size) { + assert(inited); + return allocator.Allocate(&cache, size, kPageSize); +} + +void *pvalloc(size_t size) { + assert(inited); + if (size == 0) size = kPageSize; + return allocator.Allocate(&cache, size, kPageSize); +} +} +#endif diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc new file mode 100644 index 0000000..d6c7f56 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc @@ -0,0 +1,56 @@ +//===-- 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. +// +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_common.h" +#include "gtest/gtest.h" +#include <stdlib.h> + +namespace __sanitizer { + +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); + for (int i = 0; i < 10; i++) { + p[i] = 42; + EXPECT_EQ(p, InternalAllocBlock(p + i)); + } + for (int i = 0; i < 20; i++) { + ((char*)p2)[i] = 42; + EXPECT_EQ(p2, InternalAllocBlock(p2 + i)); + } + 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 = rand_r(&rnd) % 1000; + char *p = (char*)InternalAlloc(sz); + EXPECT_NE(p, (char*)0); + for (uptr j = 0; j < sz; j++) { + p[j] = 42; + EXPECT_EQ(p, InternalAllocBlock(p + j)); + } + ptrs[i] = p; + } + for (int i = 0; i < kCount; i++) { + InternalFree(ptrs[i]); + } +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc new file mode 100644 index 0000000..91570dc --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_common_test.cc @@ -0,0 +1,66 @@ +//===-- 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_common.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)); +} + +} // namespace sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/lib/sanitizer_common/tests/sanitizer_flags_test.cc new file mode 100644 index 0000000..4b273e5 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_flags_test.cc @@ -0,0 +1,72 @@ +//===-- 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 "gtest/gtest.h" + +#include "tsan_rtl.h" // FIXME: break dependency from TSan runtime. +using __tsan::ScopedInRtl; + +#include <string.h> + +namespace __sanitizer { + +static const char kFlagName[] = "flag_name"; + +template <typename T> +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_STREQ(final_value, flag); +} + +TEST(SanitizerCommon, BooleanFlags) { + ScopedInRtl in_rtl; + 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) { + ScopedInRtl in_rtl; + 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) { + ScopedInRtl in_rtl; + 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 qwe\" asd", "abc qwe"); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_list_test.cc b/lib/sanitizer_common/tests/sanitizer_list_test.cc new file mode 100644 index 0000000..d328fbf --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_list_test.cc @@ -0,0 +1,157 @@ +//===-- 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<ListItem> List; + +// Check that IntrusiveList can be made thread-local. +static THREADLOCAL 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()); +} + +} // namespace __sanitizer |