diff options
Diffstat (limited to 'contrib/llvm/lib/Support/Unix')
-rw-r--r-- | contrib/llvm/lib/Support/Unix/COM.inc | 27 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Host.inc | 49 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Memory.inc | 347 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Mutex.inc | 43 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Path.inc | 655 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Process.inc | 463 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Program.inc | 469 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/README.txt | 16 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/RWMutex.inc | 51 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Signals.inc | 443 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/ThreadLocal.inc | 69 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/TimeValue.inc | 54 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Unix.h | 68 | ||||
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Watchdog.inc | 32 |
14 files changed, 2786 insertions, 0 deletions
diff --git a/contrib/llvm/lib/Support/Unix/COM.inc b/contrib/llvm/lib/Support/Unix/COM.inc new file mode 100644 index 0000000..5b71de7 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/COM.inc @@ -0,0 +1,27 @@ +//===- llvm/Support/Unix/COM.inc - Unix COM Implementation -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix portion of COM support. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +namespace llvm { +namespace sys { + +InitializeCOMRAII::InitializeCOMRAII(COMThreadingMode Threading, + bool SpeedOverMemory) {} + +InitializeCOMRAII::~InitializeCOMRAII() {} +} +} diff --git a/contrib/llvm/lib/Support/Unix/Host.inc b/contrib/llvm/lib/Support/Unix/Host.inc new file mode 100644 index 0000000..4572171 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Host.inc @@ -0,0 +1,49 @@ +//===- llvm/Support/Unix/Host.inc -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the UNIX Host support. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +#include "Unix.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" +#include <cctype> +#include <string> +#include <sys/utsname.h> + +using namespace llvm; + +static std::string getOSVersion() { + struct utsname info; + + if (uname(&info)) + return ""; + + return info.release; +} + +std::string sys::getDefaultTargetTriple() { + std::string TargetTripleString(LLVM_DEFAULT_TARGET_TRIPLE); + + // On darwin, we want to update the version to match that of the + // target. + std::string::size_type DarwinDashIdx = TargetTripleString.find("-darwin"); + if (DarwinDashIdx != std::string::npos) { + TargetTripleString.resize(DarwinDashIdx + strlen("-darwin")); + TargetTripleString += getOSVersion(); + } + + return Triple::normalize(TargetTripleString); +} diff --git a/contrib/llvm/lib/Support/Unix/Memory.inc b/contrib/llvm/lib/Support/Unix/Memory.inc new file mode 100644 index 0000000..d703191 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Memory.inc @@ -0,0 +1,347 @@ +//===- Unix/Memory.cpp - Generic UNIX System Configuration ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines some functions for various memory management utilities. +// +//===----------------------------------------------------------------------===// + +#include "Unix.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Process.h" + +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif + +#ifdef __APPLE__ +#include <mach/mach.h> +#endif + +#if defined(__mips__) +# if defined(__OpenBSD__) +# include <mips64/sysarch.h> +# else +# include <sys/cachectl.h> +# endif +#endif + +#ifdef __APPLE__ +extern "C" void sys_icache_invalidate(const void *Addr, size_t len); +#else +extern "C" void __clear_cache(void *, void*); +#endif + +namespace { + +int getPosixProtectionFlags(unsigned Flags) { + switch (Flags) { + case llvm::sys::Memory::MF_READ: + return PROT_READ; + case llvm::sys::Memory::MF_WRITE: + return PROT_WRITE; + case llvm::sys::Memory::MF_READ|llvm::sys::Memory::MF_WRITE: + return PROT_READ | PROT_WRITE; + case llvm::sys::Memory::MF_READ|llvm::sys::Memory::MF_EXEC: + return PROT_READ | PROT_EXEC; + case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE | + llvm::sys::Memory::MF_EXEC: + return PROT_READ | PROT_WRITE | PROT_EXEC; + case llvm::sys::Memory::MF_EXEC: +#if defined(__FreeBSD__) + // On PowerPC, having an executable page that has no read permission + // can have unintended consequences. The function InvalidateInstruction- + // Cache uses instructions dcbf and icbi, both of which are treated by + // the processor as loads. If the page has no read permissions, + // executing these instructions will result in a segmentation fault. + // Somehow, this problem is not present on Linux, but it does happen + // on FreeBSD. + return PROT_READ | PROT_EXEC; +#else + return PROT_EXEC; +#endif + default: + llvm_unreachable("Illegal memory protection flag specified!"); + } + // Provide a default return value as required by some compilers. + return PROT_NONE; +} + +} // namespace + +namespace llvm { +namespace sys { + +MemoryBlock +Memory::allocateMappedMemory(size_t NumBytes, + const MemoryBlock *const NearBlock, + unsigned PFlags, + std::error_code &EC) { + EC = std::error_code(); + if (NumBytes == 0) + return MemoryBlock(); + + static const size_t PageSize = Process::getPageSize(); + const size_t NumPages = (NumBytes+PageSize-1)/PageSize; + + int fd = -1; +#ifdef NEED_DEV_ZERO_FOR_MMAP + static int zero_fd = open("/dev/zero", O_RDWR); + if (zero_fd == -1) { + EC = std::error_code(errno, std::generic_category()); + return MemoryBlock(); + } + fd = zero_fd; +#endif + + int MMFlags = MAP_PRIVATE | +#ifdef HAVE_MMAP_ANONYMOUS + MAP_ANONYMOUS +#else + MAP_ANON +#endif + ; // Ends statement above + + int Protect = getPosixProtectionFlags(PFlags); + + // Use any near hint and the page size to set a page-aligned starting address + uintptr_t Start = NearBlock ? reinterpret_cast<uintptr_t>(NearBlock->base()) + + NearBlock->size() : 0; + if (Start && Start % PageSize) + Start += PageSize - Start % PageSize; + + void *Addr = ::mmap(reinterpret_cast<void*>(Start), PageSize*NumPages, + Protect, MMFlags, fd, 0); + if (Addr == MAP_FAILED) { + if (NearBlock) //Try again without a near hint + return allocateMappedMemory(NumBytes, nullptr, PFlags, EC); + + EC = std::error_code(errno, std::generic_category()); + return MemoryBlock(); + } + + MemoryBlock Result; + Result.Address = Addr; + Result.Size = NumPages*PageSize; + + if (PFlags & MF_EXEC) + Memory::InvalidateInstructionCache(Result.Address, Result.Size); + + return Result; +} + +std::error_code +Memory::releaseMappedMemory(MemoryBlock &M) { + if (M.Address == nullptr || M.Size == 0) + return std::error_code(); + + if (0 != ::munmap(M.Address, M.Size)) + return std::error_code(errno, std::generic_category()); + + M.Address = nullptr; + M.Size = 0; + + return std::error_code(); +} + +std::error_code +Memory::protectMappedMemory(const MemoryBlock &M, unsigned Flags) { + static const size_t PageSize = Process::getPageSize(); + if (M.Address == nullptr || M.Size == 0) + return std::error_code(); + + if (!Flags) + return std::error_code(EINVAL, std::generic_category()); + + int Protect = getPosixProtectionFlags(Flags); + + int Result = ::mprotect((void*)((uintptr_t)M.Address & ~(PageSize-1)), PageSize*((M.Size+PageSize-1)/PageSize), Protect); + if (Result != 0) + return std::error_code(errno, std::generic_category()); + + if (Flags & MF_EXEC) + Memory::InvalidateInstructionCache(M.Address, M.Size); + + return std::error_code(); +} + +/// AllocateRWX - Allocate a slab of memory with read/write/execute +/// permissions. This is typically used for JIT applications where we want +/// to emit code to the memory then jump to it. Getting this type of memory +/// is very OS specific. +/// +MemoryBlock +Memory::AllocateRWX(size_t NumBytes, const MemoryBlock* NearBlock, + std::string *ErrMsg) { + if (NumBytes == 0) return MemoryBlock(); + + static const size_t PageSize = Process::getPageSize(); + size_t NumPages = (NumBytes+PageSize-1)/PageSize; + + int fd = -1; +#ifdef NEED_DEV_ZERO_FOR_MMAP + static int zero_fd = open("/dev/zero", O_RDWR); + if (zero_fd == -1) { + MakeErrMsg(ErrMsg, "Can't open /dev/zero device"); + return MemoryBlock(); + } + fd = zero_fd; +#endif + + int flags = MAP_PRIVATE | +#ifdef HAVE_MMAP_ANONYMOUS + MAP_ANONYMOUS +#else + MAP_ANON +#endif + ; + + void* start = NearBlock ? (unsigned char*)NearBlock->base() + + NearBlock->size() : nullptr; + +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) + void *pa = ::mmap(start, PageSize*NumPages, PROT_READ|PROT_EXEC, + flags, fd, 0); +#else + void *pa = ::mmap(start, PageSize*NumPages, PROT_READ|PROT_WRITE|PROT_EXEC, + flags, fd, 0); +#endif + if (pa == MAP_FAILED) { + if (NearBlock) //Try again without a near hint + return AllocateRWX(NumBytes, nullptr); + + MakeErrMsg(ErrMsg, "Can't allocate RWX Memory"); + return MemoryBlock(); + } + +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) + kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)pa, + (vm_size_t)(PageSize*NumPages), 0, + VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY); + if (KERN_SUCCESS != kr) { + MakeErrMsg(ErrMsg, "vm_protect max RX failed"); + return MemoryBlock(); + } + + kr = vm_protect(mach_task_self(), (vm_address_t)pa, + (vm_size_t)(PageSize*NumPages), 0, + VM_PROT_READ | VM_PROT_WRITE); + if (KERN_SUCCESS != kr) { + MakeErrMsg(ErrMsg, "vm_protect RW failed"); + return MemoryBlock(); + } +#endif + + MemoryBlock result; + result.Address = pa; + result.Size = NumPages*PageSize; + + return result; +} + +bool Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) { + if (M.Address == nullptr || M.Size == 0) return false; + if (0 != ::munmap(M.Address, M.Size)) + return MakeErrMsg(ErrMsg, "Can't release RWX Memory"); + return false; +} + +bool Memory::setWritable (MemoryBlock &M, std::string *ErrMsg) { +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) + if (M.Address == 0 || M.Size == 0) return false; + Memory::InvalidateInstructionCache(M.Address, M.Size); + kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address, + (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_WRITE); + return KERN_SUCCESS == kr; +#else + return true; +#endif +} + +bool Memory::setExecutable (MemoryBlock &M, std::string *ErrMsg) { + if (M.Address == 0 || M.Size == 0) return false; + Memory::InvalidateInstructionCache(M.Address, M.Size); +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) + kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)M.Address, + (vm_size_t)M.Size, 0, VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY); + return KERN_SUCCESS == kr; +#else + return true; +#endif +} + +bool Memory::setRangeWritable(const void *Addr, size_t Size) { +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) + kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr, + (vm_size_t)Size, 0, + VM_PROT_READ | VM_PROT_WRITE); + return KERN_SUCCESS == kr; +#else + return true; +#endif +} + +bool Memory::setRangeExecutable(const void *Addr, size_t Size) { +#if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) + kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)Addr, + (vm_size_t)Size, 0, + VM_PROT_READ | VM_PROT_EXECUTE | VM_PROT_COPY); + return KERN_SUCCESS == kr; +#else + return true; +#endif +} + +/// InvalidateInstructionCache - Before the JIT can run a block of code +/// that has been emitted it must invalidate the instruction cache on some +/// platforms. +void Memory::InvalidateInstructionCache(const void *Addr, + size_t Len) { + +// icache invalidation for PPC and ARM. +#if defined(__APPLE__) + +# if (defined(__POWERPC__) || defined (__ppc__) || \ + defined(_POWER) || defined(_ARCH_PPC) || defined(__arm__) || \ + defined(__arm64__)) + sys_icache_invalidate(const_cast<void *>(Addr), Len); +# endif + +#else + +# if (defined(__POWERPC__) || defined (__ppc__) || \ + defined(_POWER) || defined(_ARCH_PPC)) && defined(__GNUC__) + const size_t LineSize = 32; + + const intptr_t Mask = ~(LineSize - 1); + const intptr_t StartLine = ((intptr_t) Addr) & Mask; + const intptr_t EndLine = ((intptr_t) Addr + Len + LineSize - 1) & Mask; + + for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize) + asm volatile("dcbf 0, %0" : : "r"(Line)); + asm volatile("sync"); + + for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize) + asm volatile("icbi 0, %0" : : "r"(Line)); + asm volatile("isync"); +# elif (defined(__arm__) || defined(__aarch64__) || defined(__mips__)) && \ + defined(__GNUC__) + // FIXME: Can we safely always call this for __GNUC__ everywhere? + const char *Start = static_cast<const char *>(Addr); + const char *End = Start + Len; + __clear_cache(const_cast<char *>(Start), const_cast<char *>(End)); +# endif + +#endif // end apple + + ValgrindDiscardTranslations(Addr, Len); +} + +} // namespace sys +} // namespace llvm diff --git a/contrib/llvm/lib/Support/Unix/Mutex.inc b/contrib/llvm/lib/Support/Unix/Mutex.inc new file mode 100644 index 0000000..fe6b170 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Mutex.inc @@ -0,0 +1,43 @@ +//===- llvm/Support/Unix/Mutex.inc - Unix Mutex Implementation ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix specific (non-pthread) Mutex class. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +namespace llvm +{ +using namespace sys; + +MutexImpl::MutexImpl( bool recursive) +{ +} + +MutexImpl::~MutexImpl() +{ +} + +bool +MutexImpl::release() +{ + return true; +} + +bool +MutexImpl::tryacquire( void ) +{ + return true; +} + +} diff --git a/contrib/llvm/lib/Support/Unix/Path.inc b/contrib/llvm/lib/Support/Unix/Path.inc new file mode 100644 index 0000000..d85c37a --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Path.inc @@ -0,0 +1,655 @@ +//===- llvm/Support/Unix/Path.inc - Unix Path Implementation ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix specific implementation of the Path API. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +#include "Unix.h" +#include <limits.h> +#include <stdio.h> +#if HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#ifdef __APPLE__ +#include <mach-o/dyld.h> +#endif + +// Both stdio.h and cstdio are included via different pathes and +// stdcxx's cstdio doesn't include stdio.h, so it doesn't #undef the macros +// either. +#undef ferror +#undef feof + +// For GNU Hurd +#if defined(__GNU__) && !defined(PATH_MAX) +# define PATH_MAX 4096 +#endif + +using namespace llvm; + +namespace llvm { +namespace sys { +namespace fs { +#if defined(__FreeBSD__) || defined (__NetBSD__) || defined(__Bitrig__) || \ + defined(__OpenBSD__) || defined(__minix) || defined(__FreeBSD_kernel__) || \ + defined(__linux__) || defined(__CYGWIN__) || defined(__DragonFly__) +static int +test_dir(char ret[PATH_MAX], const char *dir, const char *bin) +{ + struct stat sb; + char fullpath[PATH_MAX]; + + snprintf(fullpath, PATH_MAX, "%s/%s", dir, bin); + if (!realpath(fullpath, ret)) + return 1; + if (stat(fullpath, &sb) != 0) + return 1; + + return 0; +} + +static char * +getprogpath(char ret[PATH_MAX], const char *bin) +{ + char *pv, *s, *t; + + /* First approach: absolute path. */ + if (bin[0] == '/') { + if (test_dir(ret, "/", bin) == 0) + return ret; + return nullptr; + } + + /* Second approach: relative path. */ + if (strchr(bin, '/')) { + char cwd[PATH_MAX]; + if (!getcwd(cwd, PATH_MAX)) + return nullptr; + if (test_dir(ret, cwd, bin) == 0) + return ret; + return nullptr; + } + + /* Third approach: $PATH */ + if ((pv = getenv("PATH")) == nullptr) + return nullptr; + s = pv = strdup(pv); + if (!pv) + return nullptr; + while ((t = strsep(&s, ":")) != nullptr) { + if (test_dir(ret, t, bin) == 0) { + free(pv); + return ret; + } + } + free(pv); + return nullptr; +} +#endif // __FreeBSD__ || __NetBSD__ || __FreeBSD_kernel__ + +/// GetMainExecutable - Return the path to the main executable, given the +/// value of argv[0] from program startup. +std::string getMainExecutable(const char *argv0, void *MainAddr) { +#if defined(__APPLE__) + // On OS X the executable path is saved to the stack by dyld. Reading it + // from there is much faster than calling dladdr, especially for large + // binaries with symbols. + char exe_path[MAXPATHLEN]; + uint32_t size = sizeof(exe_path); + if (_NSGetExecutablePath(exe_path, &size) == 0) { + char link_path[MAXPATHLEN]; + if (realpath(exe_path, link_path)) + return link_path; + } +#elif defined(__FreeBSD__) || defined (__NetBSD__) || defined(__Bitrig__) || \ + defined(__OpenBSD__) || defined(__minix) || defined(__DragonFly__) || \ + defined(__FreeBSD_kernel__) + char exe_path[PATH_MAX]; + + if (getprogpath(exe_path, argv0) != NULL) + return exe_path; +#elif defined(__linux__) || defined(__CYGWIN__) + char exe_path[MAXPATHLEN]; + StringRef aPath("/proc/self/exe"); + if (sys::fs::exists(aPath)) { + // /proc is not always mounted under Linux (chroot for example). + ssize_t len = readlink(aPath.str().c_str(), exe_path, sizeof(exe_path)); + if (len >= 0) + return std::string(exe_path, len); + } else { + // Fall back to the classical detection. + if (getprogpath(exe_path, argv0)) + return exe_path; + } +#elif defined(HAVE_DLFCN_H) + // Use dladdr to get executable path if available. + Dl_info DLInfo; + int err = dladdr(MainAddr, &DLInfo); + if (err == 0) + return ""; + + // If the filename is a symlink, we need to resolve and return the location of + // the actual executable. + char link_path[MAXPATHLEN]; + if (realpath(DLInfo.dli_fname, link_path)) + return link_path; +#else +#error GetMainExecutable is not implemented on this host yet. +#endif + return ""; +} + +TimeValue file_status::getLastModificationTime() const { + TimeValue Ret; + Ret.fromEpochTime(fs_st_mtime); + return Ret; +} + +UniqueID file_status::getUniqueID() const { + return UniqueID(fs_st_dev, fs_st_ino); +} + +std::error_code current_path(SmallVectorImpl<char> &result) { + result.clear(); + + const char *pwd = ::getenv("PWD"); + llvm::sys::fs::file_status PWDStatus, DotStatus; + if (pwd && llvm::sys::path::is_absolute(pwd) && + !llvm::sys::fs::status(pwd, PWDStatus) && + !llvm::sys::fs::status(".", DotStatus) && + PWDStatus.getUniqueID() == DotStatus.getUniqueID()) { + result.append(pwd, pwd + strlen(pwd)); + return std::error_code(); + } + +#ifdef MAXPATHLEN + result.reserve(MAXPATHLEN); +#else +// For GNU Hurd + result.reserve(1024); +#endif + + while (true) { + if (::getcwd(result.data(), result.capacity()) == nullptr) { + // See if there was a real error. + if (errno != ENOMEM) + return std::error_code(errno, std::generic_category()); + // Otherwise there just wasn't enough space. + result.reserve(result.capacity() * 2); + } else + break; + } + + result.set_size(strlen(result.data())); + return std::error_code(); +} + +std::error_code create_directory(const Twine &path, bool IgnoreExisting, + perms Perms) { + SmallString<128> path_storage; + StringRef p = path.toNullTerminatedStringRef(path_storage); + + if (::mkdir(p.begin(), Perms) == -1) { + if (errno != EEXIST || !IgnoreExisting) + return std::error_code(errno, std::generic_category()); + } + + return std::error_code(); +} + +// Note that we are using symbolic link because hard links are not supported by +// all filesystems (SMB doesn't). +std::error_code create_link(const Twine &to, const Twine &from) { + // Get arguments. + SmallString<128> from_storage; + SmallString<128> to_storage; + StringRef f = from.toNullTerminatedStringRef(from_storage); + StringRef t = to.toNullTerminatedStringRef(to_storage); + + if (::symlink(t.begin(), f.begin()) == -1) + return std::error_code(errno, std::generic_category()); + + return std::error_code(); +} + +std::error_code remove(const Twine &path, bool IgnoreNonExisting) { + SmallString<128> path_storage; + StringRef p = path.toNullTerminatedStringRef(path_storage); + + struct stat buf; + if (lstat(p.begin(), &buf) != 0) { + if (errno != ENOENT || !IgnoreNonExisting) + return std::error_code(errno, std::generic_category()); + return std::error_code(); + } + + // Note: this check catches strange situations. In all cases, LLVM should + // only be involved in the creation and deletion of regular files. This + // check ensures that what we're trying to erase is a regular file. It + // effectively prevents LLVM from erasing things like /dev/null, any block + // special file, or other things that aren't "regular" files. + if (!S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode) && !S_ISLNK(buf.st_mode)) + return make_error_code(errc::operation_not_permitted); + + if (::remove(p.begin()) == -1) { + if (errno != ENOENT || !IgnoreNonExisting) + return std::error_code(errno, std::generic_category()); + } + + return std::error_code(); +} + +std::error_code rename(const Twine &from, const Twine &to) { + // Get arguments. + SmallString<128> from_storage; + SmallString<128> to_storage; + StringRef f = from.toNullTerminatedStringRef(from_storage); + StringRef t = to.toNullTerminatedStringRef(to_storage); + + if (::rename(f.begin(), t.begin()) == -1) + return std::error_code(errno, std::generic_category()); + + return std::error_code(); +} + +std::error_code resize_file(int FD, uint64_t Size) { + if (::ftruncate(FD, Size) == -1) + return std::error_code(errno, std::generic_category()); + + return std::error_code(); +} + +static int convertAccessMode(AccessMode Mode) { + switch (Mode) { + case AccessMode::Exist: + return F_OK; + case AccessMode::Write: + return W_OK; + case AccessMode::Execute: + return R_OK | X_OK; // scripts also need R_OK. + } + llvm_unreachable("invalid enum"); +} + +std::error_code access(const Twine &Path, AccessMode Mode) { + SmallString<128> PathStorage; + StringRef P = Path.toNullTerminatedStringRef(PathStorage); + + if (::access(P.begin(), convertAccessMode(Mode)) == -1) + return std::error_code(errno, std::generic_category()); + + if (Mode == AccessMode::Execute) { + // Don't say that directories are executable. + struct stat buf; + if (0 != stat(P.begin(), &buf)) + return errc::permission_denied; + if (!S_ISREG(buf.st_mode)) + return errc::permission_denied; + } + + return std::error_code(); +} + +bool can_execute(const Twine &Path) { + return !access(Path, AccessMode::Execute); +} + +bool equivalent(file_status A, file_status B) { + assert(status_known(A) && status_known(B)); + return A.fs_st_dev == B.fs_st_dev && + A.fs_st_ino == B.fs_st_ino; +} + +std::error_code equivalent(const Twine &A, const Twine &B, bool &result) { + file_status fsA, fsB; + if (std::error_code ec = status(A, fsA)) + return ec; + if (std::error_code ec = status(B, fsB)) + return ec; + result = equivalent(fsA, fsB); + return std::error_code(); +} + +static std::error_code fillStatus(int StatRet, const struct stat &Status, + file_status &Result) { + if (StatRet != 0) { + std::error_code ec(errno, std::generic_category()); + if (ec == errc::no_such_file_or_directory) + Result = file_status(file_type::file_not_found); + else + Result = file_status(file_type::status_error); + return ec; + } + + file_type Type = file_type::type_unknown; + + if (S_ISDIR(Status.st_mode)) + Type = file_type::directory_file; + else if (S_ISREG(Status.st_mode)) + Type = file_type::regular_file; + else if (S_ISBLK(Status.st_mode)) + Type = file_type::block_file; + else if (S_ISCHR(Status.st_mode)) + Type = file_type::character_file; + else if (S_ISFIFO(Status.st_mode)) + Type = file_type::fifo_file; + else if (S_ISSOCK(Status.st_mode)) + Type = file_type::socket_file; + + perms Perms = static_cast<perms>(Status.st_mode); + Result = + file_status(Type, Perms, Status.st_dev, Status.st_ino, Status.st_mtime, + Status.st_uid, Status.st_gid, Status.st_size); + + return std::error_code(); +} + +std::error_code status(const Twine &Path, file_status &Result) { + SmallString<128> PathStorage; + StringRef P = Path.toNullTerminatedStringRef(PathStorage); + + struct stat Status; + int StatRet = ::stat(P.begin(), &Status); + return fillStatus(StatRet, Status, Result); +} + +std::error_code status(int FD, file_status &Result) { + struct stat Status; + int StatRet = ::fstat(FD, &Status); + return fillStatus(StatRet, Status, Result); +} + +std::error_code setLastModificationAndAccessTime(int FD, TimeValue Time) { +#if defined(HAVE_FUTIMENS) + timespec Times[2]; + Times[0].tv_sec = Time.toEpochTime(); + Times[0].tv_nsec = 0; + Times[1] = Times[0]; + if (::futimens(FD, Times)) + return std::error_code(errno, std::generic_category()); + return std::error_code(); +#elif defined(HAVE_FUTIMES) + timeval Times[2]; + Times[0].tv_sec = Time.toEpochTime(); + Times[0].tv_usec = 0; + Times[1] = Times[0]; + if (::futimes(FD, Times)) + return std::error_code(errno, std::generic_category()); + return std::error_code(); +#else +#warning Missing futimes() and futimens() + return make_error_code(errc::function_not_supported); +#endif +} + +std::error_code mapped_file_region::init(int FD, uint64_t Offset, + mapmode Mode) { + assert(Size != 0); + + int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE; + int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE); + Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset); + if (Mapping == MAP_FAILED) + return std::error_code(errno, std::generic_category()); + return std::error_code(); +} + +mapped_file_region::mapped_file_region(int fd, mapmode mode, uint64_t length, + uint64_t offset, std::error_code &ec) + : Size(length), Mapping() { + // Make sure that the requested size fits within SIZE_T. + if (length > std::numeric_limits<size_t>::max()) { + ec = make_error_code(errc::invalid_argument); + return; + } + + ec = init(fd, offset, mode); + if (ec) + Mapping = nullptr; +} + +mapped_file_region::~mapped_file_region() { + if (Mapping) + ::munmap(Mapping, Size); +} + +uint64_t mapped_file_region::size() const { + assert(Mapping && "Mapping failed but used anyway!"); + return Size; +} + +char *mapped_file_region::data() const { + assert(Mapping && "Mapping failed but used anyway!"); + return reinterpret_cast<char*>(Mapping); +} + +const char *mapped_file_region::const_data() const { + assert(Mapping && "Mapping failed but used anyway!"); + return reinterpret_cast<const char*>(Mapping); +} + +int mapped_file_region::alignment() { + return Process::getPageSize(); +} + +std::error_code detail::directory_iterator_construct(detail::DirIterState &it, + StringRef path){ + SmallString<128> path_null(path); + DIR *directory = ::opendir(path_null.c_str()); + if (!directory) + return std::error_code(errno, std::generic_category()); + + it.IterationHandle = reinterpret_cast<intptr_t>(directory); + // Add something for replace_filename to replace. + path::append(path_null, "."); + it.CurrentEntry = directory_entry(path_null.str()); + return directory_iterator_increment(it); +} + +std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) { + if (it.IterationHandle) + ::closedir(reinterpret_cast<DIR *>(it.IterationHandle)); + it.IterationHandle = 0; + it.CurrentEntry = directory_entry(); + return std::error_code(); +} + +std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { + errno = 0; + dirent *cur_dir = ::readdir(reinterpret_cast<DIR *>(it.IterationHandle)); + if (cur_dir == nullptr && errno != 0) { + return std::error_code(errno, std::generic_category()); + } else if (cur_dir != nullptr) { + StringRef name(cur_dir->d_name, NAMLEN(cur_dir)); + if ((name.size() == 1 && name[0] == '.') || + (name.size() == 2 && name[0] == '.' && name[1] == '.')) + return directory_iterator_increment(it); + it.CurrentEntry.replace_filename(name); + } else + return directory_iterator_destruct(it); + + return std::error_code(); +} + +std::error_code openFileForRead(const Twine &Name, int &ResultFD) { + SmallString<128> Storage; + StringRef P = Name.toNullTerminatedStringRef(Storage); + while ((ResultFD = open(P.begin(), O_RDONLY)) < 0) { + if (errno != EINTR) + return std::error_code(errno, std::generic_category()); + } + return std::error_code(); +} + +std::error_code openFileForWrite(const Twine &Name, int &ResultFD, + sys::fs::OpenFlags Flags, unsigned Mode) { + // Verify that we don't have both "append" and "excl". + assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) && + "Cannot specify both 'excl' and 'append' file creation flags!"); + + int OpenFlags = O_CREAT; + + if (Flags & F_RW) + OpenFlags |= O_RDWR; + else + OpenFlags |= O_WRONLY; + + if (Flags & F_Append) + OpenFlags |= O_APPEND; + else + OpenFlags |= O_TRUNC; + + if (Flags & F_Excl) + OpenFlags |= O_EXCL; + + SmallString<128> Storage; + StringRef P = Name.toNullTerminatedStringRef(Storage); + while ((ResultFD = open(P.begin(), OpenFlags, Mode)) < 0) { + if (errno != EINTR) + return std::error_code(errno, std::generic_category()); + } + return std::error_code(); +} + +} // end namespace fs + +namespace path { + +bool home_directory(SmallVectorImpl<char> &result) { + if (char *RequestedDir = getenv("HOME")) { + result.clear(); + result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return true; + } + + return false; +} + +static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) { + #if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR) + // On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR. + // macros defined in <unistd.h> on darwin >= 9 + int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR + : _CS_DARWIN_USER_CACHE_DIR; + size_t ConfLen = confstr(ConfName, nullptr, 0); + if (ConfLen > 0) { + do { + Result.resize(ConfLen); + ConfLen = confstr(ConfName, Result.data(), Result.size()); + } while (ConfLen > 0 && ConfLen != Result.size()); + + if (ConfLen > 0) { + assert(Result.back() == 0); + Result.pop_back(); + return true; + } + + Result.clear(); + } + #endif + return false; +} + +static bool getUserCacheDir(SmallVectorImpl<char> &Result) { + // First try using XDS_CACHE_HOME env variable, + // as specified in XDG Base Directory Specification at + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + if (const char *XdsCacheDir = std::getenv("XDS_CACHE_HOME")) { + Result.clear(); + Result.append(XdsCacheDir, XdsCacheDir + strlen(XdsCacheDir)); + return true; + } + + // Try Darwin configuration query + if (getDarwinConfDir(false, Result)) + return true; + + // Use "$HOME/.cache" if $HOME is available + if (home_directory(Result)) { + append(Result, ".cache"); + return true; + } + + return false; +} + +static const char *getEnvTempDir() { + // Check whether the temporary directory is specified by an environment + // variable. + const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; + for (const char *Env : EnvironmentVariables) { + if (const char *Dir = std::getenv(Env)) + return Dir; + } + + return nullptr; +} + +static const char *getDefaultTempDir(bool ErasedOnReboot) { +#ifdef P_tmpdir + if ((bool)P_tmpdir) + return P_tmpdir; +#endif + + if (ErasedOnReboot) + return "/tmp"; + return "/var/tmp"; +} + +void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) { + Result.clear(); + + if (ErasedOnReboot) { + // There is no env variable for the cache directory. + if (const char *RequestedDir = getEnvTempDir()) { + Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return; + } + } + + if (getDarwinConfDir(ErasedOnReboot, Result)) + return; + + const char *RequestedDir = getDefaultTempDir(ErasedOnReboot); + Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); +} + +} // end namespace path + +} // end namespace sys +} // end namespace llvm diff --git a/contrib/llvm/lib/Support/Unix/Process.inc b/contrib/llvm/lib/Support/Unix/Process.inc new file mode 100644 index 0000000..27083ee --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Process.inc @@ -0,0 +1,463 @@ +//===- Unix/Process.cpp - Unix Process Implementation --------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides the generic Unix implementation of the Process class. +// +//===----------------------------------------------------------------------===// + +#include "Unix.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/MutexGuard.h" +#include "llvm/Support/TimeValue.h" +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#if HAVE_SIGNAL_H +#include <signal.h> +#endif +// DragonFlyBSD, OpenBSD, and Bitrig have deprecated <malloc.h> for +// <stdlib.h> instead. Unix.h includes this for us already. +#if defined(HAVE_MALLOC_H) && !defined(__DragonFly__) && \ + !defined(__OpenBSD__) && !defined(__Bitrig__) +#include <malloc.h> +#endif +#if defined(HAVE_MALLCTL) +#include <malloc_np.h> +#endif +#ifdef HAVE_MALLOC_MALLOC_H +#include <malloc/malloc.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif +#ifdef HAVE_TERMIOS_H +# include <termios.h> +#endif + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +using namespace llvm; +using namespace sys; + +static std::pair<TimeValue, TimeValue> getRUsageTimes() { +#if defined(HAVE_GETRUSAGE) + struct rusage RU; + ::getrusage(RUSAGE_SELF, &RU); + return std::make_pair( + TimeValue( + static_cast<TimeValue::SecondsType>(RU.ru_utime.tv_sec), + static_cast<TimeValue::NanoSecondsType>( + RU.ru_utime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)), + TimeValue( + static_cast<TimeValue::SecondsType>(RU.ru_stime.tv_sec), + static_cast<TimeValue::NanoSecondsType>( + RU.ru_stime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND))); +#else +#warning Cannot get usage times on this platform + return std::make_pair(TimeValue(), TimeValue()); +#endif +} + +// On Cygwin, getpagesize() returns 64k(AllocationGranularity) and +// offset in mmap(3) should be aligned to the AllocationGranularity. +unsigned Process::getPageSize() { +#if defined(HAVE_GETPAGESIZE) + static const int page_size = ::getpagesize(); +#elif defined(HAVE_SYSCONF) + static long page_size = ::sysconf(_SC_PAGE_SIZE); +#else +#warning Cannot get the page size on this machine +#endif + return static_cast<unsigned>(page_size); +} + +size_t Process::GetMallocUsage() { +#if defined(HAVE_MALLINFO) + struct mallinfo mi; + mi = ::mallinfo(); + return mi.uordblks; +#elif defined(HAVE_MALLOC_ZONE_STATISTICS) && defined(HAVE_MALLOC_MALLOC_H) + malloc_statistics_t Stats; + malloc_zone_statistics(malloc_default_zone(), &Stats); + return Stats.size_in_use; // darwin +#elif defined(HAVE_MALLCTL) + size_t alloc, sz; + sz = sizeof(size_t); + if (mallctl("stats.allocated", &alloc, &sz, NULL, 0) == 0) + return alloc; + return 0; +#elif defined(HAVE_SBRK) + // Note this is only an approximation and more closely resembles + // the value returned by mallinfo in the arena field. + static char *StartOfMemory = reinterpret_cast<char*>(::sbrk(0)); + char *EndOfMemory = (char*)sbrk(0); + if (EndOfMemory != ((char*)-1) && StartOfMemory != ((char*)-1)) + return EndOfMemory - StartOfMemory; + return 0; +#else +#warning Cannot get malloc info on this platform + return 0; +#endif +} + +void Process::GetTimeUsage(TimeValue &elapsed, TimeValue &user_time, + TimeValue &sys_time) { + elapsed = TimeValue::now(); + std::tie(user_time, sys_time) = getRUsageTimes(); +} + +#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__) +#include <mach/mach.h> +#endif + +// Some LLVM programs such as bugpoint produce core files as a normal part of +// their operation. To prevent the disk from filling up, this function +// does what's necessary to prevent their generation. +void Process::PreventCoreFiles() { +#if HAVE_SETRLIMIT + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rlim); +#endif + +#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__) + // Disable crash reporting on Mac OS X 10.0-10.4 + + // get information about the original set of exception ports for the task + mach_msg_type_number_t Count = 0; + exception_mask_t OriginalMasks[EXC_TYPES_COUNT]; + exception_port_t OriginalPorts[EXC_TYPES_COUNT]; + exception_behavior_t OriginalBehaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t OriginalFlavors[EXC_TYPES_COUNT]; + kern_return_t err = + task_get_exception_ports(mach_task_self(), EXC_MASK_ALL, OriginalMasks, + &Count, OriginalPorts, OriginalBehaviors, + OriginalFlavors); + if (err == KERN_SUCCESS) { + // replace each with MACH_PORT_NULL. + for (unsigned i = 0; i != Count; ++i) + task_set_exception_ports(mach_task_self(), OriginalMasks[i], + MACH_PORT_NULL, OriginalBehaviors[i], + OriginalFlavors[i]); + } + + // Disable crash reporting on Mac OS X 10.5 + signal(SIGABRT, _exit); + signal(SIGILL, _exit); + signal(SIGFPE, _exit); + signal(SIGSEGV, _exit); + signal(SIGBUS, _exit); +#endif +} + +Optional<std::string> Process::GetEnv(StringRef Name) { + std::string NameStr = Name.str(); + const char *Val = ::getenv(NameStr.c_str()); + if (!Val) + return None; + return std::string(Val); +} + +std::error_code +Process::GetArgumentVector(SmallVectorImpl<const char *> &ArgsOut, + ArrayRef<const char *> ArgsIn, + SpecificBumpPtrAllocator<char> &) { + ArgsOut.append(ArgsIn.begin(), ArgsIn.end()); + + return std::error_code(); +} + +namespace { +class FDCloser { +public: + FDCloser(int &FD) : FD(FD), KeepOpen(false) {} + void keepOpen() { KeepOpen = true; } + ~FDCloser() { + if (!KeepOpen && FD >= 0) + ::close(FD); + } + +private: + FDCloser(const FDCloser &) = delete; + void operator=(const FDCloser &) = delete; + + int &FD; + bool KeepOpen; +}; +} + +std::error_code Process::FixupStandardFileDescriptors() { + int NullFD = -1; + FDCloser FDC(NullFD); + const int StandardFDs[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; + for (int StandardFD : StandardFDs) { + struct stat st; + errno = 0; + while (fstat(StandardFD, &st) < 0) { + assert(errno && "expected errno to be set if fstat failed!"); + // fstat should return EBADF if the file descriptor is closed. + if (errno == EBADF) + break; + // retry fstat if we got EINTR, otherwise bubble up the failure. + if (errno != EINTR) + return std::error_code(errno, std::generic_category()); + } + // if fstat succeeds, move on to the next FD. + if (!errno) + continue; + assert(errno == EBADF && "expected errno to have EBADF at this point!"); + + if (NullFD < 0) { + while ((NullFD = open("/dev/null", O_RDWR)) < 0) { + if (errno == EINTR) + continue; + return std::error_code(errno, std::generic_category()); + } + } + + if (NullFD == StandardFD) + FDC.keepOpen(); + else if (dup2(NullFD, StandardFD) < 0) + return std::error_code(errno, std::generic_category()); + } + return std::error_code(); +} + +std::error_code Process::SafelyCloseFileDescriptor(int FD) { + // Create a signal set filled with *all* signals. + sigset_t FullSet; + if (sigfillset(&FullSet) < 0) + return std::error_code(errno, std::generic_category()); + // Atomically swap our current signal mask with a full mask. + sigset_t SavedSet; +#if LLVM_ENABLE_THREADS + if (int EC = pthread_sigmask(SIG_SETMASK, &FullSet, &SavedSet)) + return std::error_code(EC, std::generic_category()); +#else + if (sigprocmask(SIG_SETMASK, &FullSet, &SavedSet) < 0) + return std::error_code(errno, std::generic_category()); +#endif + // Attempt to close the file descriptor. + // We need to save the error, if one occurs, because our subsequent call to + // pthread_sigmask might tamper with errno. + int ErrnoFromClose = 0; + if (::close(FD) < 0) + ErrnoFromClose = errno; + // Restore the signal mask back to what we saved earlier. + int EC = 0; +#if LLVM_ENABLE_THREADS + EC = pthread_sigmask(SIG_SETMASK, &SavedSet, nullptr); +#else + if (sigprocmask(SIG_SETMASK, &SavedSet, nullptr) < 0) + EC = errno; +#endif + // The error code from close takes precedence over the one from + // pthread_sigmask. + if (ErrnoFromClose) + return std::error_code(ErrnoFromClose, std::generic_category()); + return std::error_code(EC, std::generic_category()); +} + +bool Process::StandardInIsUserInput() { + return FileDescriptorIsDisplayed(STDIN_FILENO); +} + +bool Process::StandardOutIsDisplayed() { + return FileDescriptorIsDisplayed(STDOUT_FILENO); +} + +bool Process::StandardErrIsDisplayed() { + return FileDescriptorIsDisplayed(STDERR_FILENO); +} + +bool Process::FileDescriptorIsDisplayed(int fd) { +#if HAVE_ISATTY + return isatty(fd); +#else + // If we don't have isatty, just return false. + return false; +#endif +} + +static unsigned getColumns(int FileID) { + // If COLUMNS is defined in the environment, wrap to that many columns. + if (const char *ColumnsStr = std::getenv("COLUMNS")) { + int Columns = std::atoi(ColumnsStr); + if (Columns > 0) + return Columns; + } + + unsigned Columns = 0; + +#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_TERMIOS_H) + // Try to determine the width of the terminal. + struct winsize ws; + if (ioctl(FileID, TIOCGWINSZ, &ws) == 0) + Columns = ws.ws_col; +#endif + + return Columns; +} + +unsigned Process::StandardOutColumns() { + if (!StandardOutIsDisplayed()) + return 0; + + return getColumns(1); +} + +unsigned Process::StandardErrColumns() { + if (!StandardErrIsDisplayed()) + return 0; + + return getColumns(2); +} + +#ifdef HAVE_TERMINFO +// We manually declare these extern functions because finding the correct +// headers from various terminfo, curses, or other sources is harder than +// writing their specs down. +extern "C" int setupterm(char *term, int filedes, int *errret); +extern "C" struct term *set_curterm(struct term *termp); +extern "C" int del_curterm(struct term *termp); +extern "C" int tigetnum(char *capname); +#endif + +#ifdef HAVE_TERMINFO +static ManagedStatic<sys::Mutex> TermColorMutex; +#endif + +static bool terminalHasColors(int fd) { +#ifdef HAVE_TERMINFO + // First, acquire a global lock because these C routines are thread hostile. + MutexGuard G(*TermColorMutex); + + int errret = 0; + if (setupterm((char *)nullptr, fd, &errret) != 0) + // Regardless of why, if we can't get terminfo, we shouldn't try to print + // colors. + return false; + + // Test whether the terminal as set up supports color output. How to do this + // isn't entirely obvious. We can use the curses routine 'has_colors' but it + // would be nice to avoid a dependency on curses proper when we can make do + // with a minimal terminfo parsing library. Also, we don't really care whether + // the terminal supports the curses-specific color changing routines, merely + // if it will interpret ANSI color escape codes in a reasonable way. Thus, the + // strategy here is just to query the baseline colors capability and if it + // supports colors at all to assume it will translate the escape codes into + // whatever range of colors it does support. We can add more detailed tests + // here if users report them as necessary. + // + // The 'tigetnum' routine returns -2 or -1 on errors, and might return 0 if + // the terminfo says that no colors are supported. + bool HasColors = tigetnum(const_cast<char *>("colors")) > 0; + + // Now extract the structure allocated by setupterm and free its memory + // through a really silly dance. + struct term *termp = set_curterm((struct term *)nullptr); + (void)del_curterm(termp); // Drop any errors here. + + // Return true if we found a color capabilities for the current terminal. + if (HasColors) + return true; +#endif + + // Otherwise, be conservative. + return false; +} + +bool Process::FileDescriptorHasColors(int fd) { + // A file descriptor has colors if it is displayed and the terminal has + // colors. + return FileDescriptorIsDisplayed(fd) && terminalHasColors(fd); +} + +bool Process::StandardOutHasColors() { + return FileDescriptorHasColors(STDOUT_FILENO); +} + +bool Process::StandardErrHasColors() { + return FileDescriptorHasColors(STDERR_FILENO); +} + +void Process::UseANSIEscapeCodes(bool /*enable*/) { + // No effect. +} + +bool Process::ColorNeedsFlush() { + // No, we use ANSI escape sequences. + return false; +} + +const char *Process::OutputColor(char code, bool bold, bool bg) { + return colorcodes[bg?1:0][bold?1:0][code&7]; +} + +const char *Process::OutputBold(bool bg) { + return "\033[1m"; +} + +const char *Process::OutputReverse() { + return "\033[7m"; +} + +const char *Process::ResetColor() { + return "\033[0m"; +} + +#if !defined(HAVE_DECL_ARC4RANDOM) || !HAVE_DECL_ARC4RANDOM +static unsigned GetRandomNumberSeed() { + // Attempt to get the initial seed from /dev/urandom, if possible. + int urandomFD = open("/dev/urandom", O_RDONLY); + + if (urandomFD != -1) { + unsigned seed; + // Don't use a buffered read to avoid reading more data + // from /dev/urandom than we need. + int count = read(urandomFD, (void *)&seed, sizeof(seed)); + + close(urandomFD); + + // Return the seed if the read was successful. + if (count == sizeof(seed)) + return seed; + } + + // Otherwise, swizzle the current time and the process ID to form a reasonable + // seed. + TimeValue Now = TimeValue::now(); + return hash_combine(Now.seconds(), Now.nanoseconds(), ::getpid()); +} +#endif + +unsigned llvm::sys::Process::GetRandomNumber() { +#if defined(HAVE_DECL_ARC4RANDOM) && HAVE_DECL_ARC4RANDOM + return arc4random(); +#else + static int x = (::srand(GetRandomNumberSeed()), 0); + (void)x; + return ::rand(); +#endif +} diff --git a/contrib/llvm/lib/Support/Unix/Program.inc b/contrib/llvm/lib/Support/Unix/Program.inc new file mode 100644 index 0000000..a8d1fe3 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Program.inc @@ -0,0 +1,469 @@ +//===- llvm/Support/Unix/Program.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix specific portion of the Program class. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +#include "Unix.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#if HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#if HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif +#if HAVE_SIGNAL_H +#include <signal.h> +#endif +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_POSIX_SPAWN +#ifdef __sun__ +#define _RESTRICT_KYWD +#endif +#include <spawn.h> + +#if defined(__APPLE__) +#include <TargetConditionals.h> +#endif + +#if defined(__APPLE__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +#define USE_NSGETENVIRON 1 +#else +#define USE_NSGETENVIRON 0 +#endif + +#if !USE_NSGETENVIRON + extern char **environ; +#else +#include <crt_externs.h> // _NSGetEnviron +#endif +#endif + +namespace llvm { + +using namespace sys; + +ProcessInfo::ProcessInfo() : Pid(0), ReturnCode(0) {} + +ErrorOr<std::string> sys::findProgramByName(StringRef Name, + ArrayRef<StringRef> Paths) { + assert(!Name.empty() && "Must have a name!"); + // Use the given path verbatim if it contains any slashes; this matches + // the behavior of sh(1) and friends. + if (Name.find('/') != StringRef::npos) + return std::string(Name); + + SmallVector<StringRef, 16> EnvironmentPaths; + if (Paths.empty()) + if (const char *PathEnv = std::getenv("PATH")) { + SplitString(PathEnv, EnvironmentPaths, ":"); + Paths = EnvironmentPaths; + } + + for (auto Path : Paths) { + if (Path.empty()) + continue; + + // Check to see if this first directory contains the executable... + SmallString<128> FilePath(Path); + sys::path::append(FilePath, Name); + if (sys::fs::can_execute(FilePath.c_str())) + return std::string(FilePath.str()); // Found the executable! + } + return errc::no_such_file_or_directory; +} + +static bool RedirectIO(const StringRef *Path, int FD, std::string* ErrMsg) { + if (!Path) // Noop + return false; + std::string File; + if (Path->empty()) + // Redirect empty paths to /dev/null + File = "/dev/null"; + else + File = *Path; + + // Open the file + int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666); + if (InFD == -1) { + MakeErrMsg(ErrMsg, "Cannot open file '" + File + "' for " + + (FD == 0 ? "input" : "output")); + return true; + } + + // Install it as the requested FD + if (dup2(InFD, FD) == -1) { + MakeErrMsg(ErrMsg, "Cannot dup2"); + close(InFD); + return true; + } + close(InFD); // Close the original FD + return false; +} + +#ifdef HAVE_POSIX_SPAWN +static bool RedirectIO_PS(const std::string *Path, int FD, std::string *ErrMsg, + posix_spawn_file_actions_t *FileActions) { + if (!Path) // Noop + return false; + const char *File; + if (Path->empty()) + // Redirect empty paths to /dev/null + File = "/dev/null"; + else + File = Path->c_str(); + + if (int Err = posix_spawn_file_actions_addopen( + FileActions, FD, File, + FD == 0 ? O_RDONLY : O_WRONLY | O_CREAT, 0666)) + return MakeErrMsg(ErrMsg, "Cannot dup2", Err); + return false; +} +#endif + +static void TimeOutHandler(int Sig) { +} + +static void SetMemoryLimits (unsigned size) +{ +#if HAVE_SYS_RESOURCE_H && HAVE_GETRLIMIT && HAVE_SETRLIMIT + struct rlimit r; + __typeof__ (r.rlim_cur) limit = (__typeof__ (r.rlim_cur)) (size) * 1048576; + + // Heap size + getrlimit (RLIMIT_DATA, &r); + r.rlim_cur = limit; + setrlimit (RLIMIT_DATA, &r); +#ifdef RLIMIT_RSS + // Resident set size. + getrlimit (RLIMIT_RSS, &r); + r.rlim_cur = limit; + setrlimit (RLIMIT_RSS, &r); +#endif +#ifdef RLIMIT_AS // e.g. NetBSD doesn't have it. + // Don't set virtual memory limit if built with any Sanitizer. They need 80Tb + // of virtual memory for shadow memory mapping. +#if !LLVM_MEMORY_SANITIZER_BUILD && !LLVM_ADDRESS_SANITIZER_BUILD + // Virtual memory. + getrlimit (RLIMIT_AS, &r); + r.rlim_cur = limit; + setrlimit (RLIMIT_AS, &r); +#endif +#endif +#endif +} + +} + +static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, + const char **envp, const StringRef **redirects, + unsigned memoryLimit, std::string *ErrMsg) { + if (!llvm::sys::fs::exists(Program)) { + if (ErrMsg) + *ErrMsg = std::string("Executable \"") + Program.str() + + std::string("\" doesn't exist!"); + return false; + } + + // If this OS has posix_spawn and there is no memory limit being implied, use + // posix_spawn. It is more efficient than fork/exec. +#ifdef HAVE_POSIX_SPAWN + if (memoryLimit == 0) { + posix_spawn_file_actions_t FileActionsStore; + posix_spawn_file_actions_t *FileActions = nullptr; + + // If we call posix_spawn_file_actions_addopen we have to make sure the + // c strings we pass to it stay alive until the call to posix_spawn, + // so we copy any StringRefs into this variable. + std::string RedirectsStorage[3]; + + if (redirects) { + std::string *RedirectsStr[3] = {nullptr, nullptr, nullptr}; + for (int I = 0; I < 3; ++I) { + if (redirects[I]) { + RedirectsStorage[I] = *redirects[I]; + RedirectsStr[I] = &RedirectsStorage[I]; + } + } + + FileActions = &FileActionsStore; + posix_spawn_file_actions_init(FileActions); + + // Redirect stdin/stdout. + if (RedirectIO_PS(RedirectsStr[0], 0, ErrMsg, FileActions) || + RedirectIO_PS(RedirectsStr[1], 1, ErrMsg, FileActions)) + return false; + if (redirects[1] == nullptr || redirects[2] == nullptr || + *redirects[1] != *redirects[2]) { + // Just redirect stderr + if (RedirectIO_PS(RedirectsStr[2], 2, ErrMsg, FileActions)) + return false; + } else { + // If stdout and stderr should go to the same place, redirect stderr + // to the FD already open for stdout. + if (int Err = posix_spawn_file_actions_adddup2(FileActions, 1, 2)) + return !MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout", Err); + } + } + + if (!envp) +#if !USE_NSGETENVIRON + envp = const_cast<const char **>(environ); +#else + // environ is missing in dylibs. + envp = const_cast<const char **>(*_NSGetEnviron()); +#endif + + // Explicitly initialized to prevent what appears to be a valgrind false + // positive. + pid_t PID = 0; + int Err = posix_spawn(&PID, Program.str().c_str(), FileActions, + /*attrp*/nullptr, const_cast<char **>(args), + const_cast<char **>(envp)); + + if (FileActions) + posix_spawn_file_actions_destroy(FileActions); + + if (Err) + return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err); + + PI.Pid = PID; + + return true; + } +#endif + + // Create a child process. + int child = fork(); + switch (child) { + // An error occurred: Return to the caller. + case -1: + MakeErrMsg(ErrMsg, "Couldn't fork"); + return false; + + // Child process: Execute the program. + case 0: { + // Redirect file descriptors... + if (redirects) { + // Redirect stdin + if (RedirectIO(redirects[0], 0, ErrMsg)) { return false; } + // Redirect stdout + if (RedirectIO(redirects[1], 1, ErrMsg)) { return false; } + if (redirects[1] && redirects[2] && + *(redirects[1]) == *(redirects[2])) { + // If stdout and stderr should go to the same place, redirect stderr + // to the FD already open for stdout. + if (-1 == dup2(1,2)) { + MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout"); + return false; + } + } else { + // Just redirect stderr + if (RedirectIO(redirects[2], 2, ErrMsg)) { return false; } + } + } + + // Set memory limits + if (memoryLimit!=0) { + SetMemoryLimits(memoryLimit); + } + + // Execute! + std::string PathStr = Program; + if (envp != nullptr) + execve(PathStr.c_str(), + const_cast<char **>(args), + const_cast<char **>(envp)); + else + execv(PathStr.c_str(), + const_cast<char **>(args)); + // If the execve() failed, we should exit. Follow Unix protocol and + // return 127 if the executable was not found, and 126 otherwise. + // Use _exit rather than exit so that atexit functions and static + // object destructors cloned from the parent process aren't + // redundantly run, and so that any data buffered in stdio buffers + // cloned from the parent aren't redundantly written out. + _exit(errno == ENOENT ? 127 : 126); + } + + // Parent process: Break out of the switch to do our processing. + default: + break; + } + + PI.Pid = child; + + return true; +} + +namespace llvm { + +ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, + bool WaitUntilTerminates, std::string *ErrMsg) { + struct sigaction Act, Old; + assert(PI.Pid && "invalid pid to wait on, process not started?"); + + int WaitPidOptions = 0; + pid_t ChildPid = PI.Pid; + if (WaitUntilTerminates) { + SecondsToWait = 0; + } else if (SecondsToWait) { + // Install a timeout handler. The handler itself does nothing, but the + // simple fact of having a handler at all causes the wait below to return + // with EINTR, unlike if we used SIG_IGN. + memset(&Act, 0, sizeof(Act)); + Act.sa_handler = TimeOutHandler; + sigemptyset(&Act.sa_mask); + sigaction(SIGALRM, &Act, &Old); + alarm(SecondsToWait); + } else if (SecondsToWait == 0) + WaitPidOptions = WNOHANG; + + // Parent process: Wait for the child process to terminate. + int status; + ProcessInfo WaitResult; + + do { + WaitResult.Pid = waitpid(ChildPid, &status, WaitPidOptions); + } while (WaitUntilTerminates && WaitResult.Pid == -1 && errno == EINTR); + + if (WaitResult.Pid != PI.Pid) { + if (WaitResult.Pid == 0) { + // Non-blocking wait. + return WaitResult; + } else { + if (SecondsToWait && errno == EINTR) { + // Kill the child. + kill(PI.Pid, SIGKILL); + + // Turn off the alarm and restore the signal handler + alarm(0); + sigaction(SIGALRM, &Old, nullptr); + + // Wait for child to die + if (wait(&status) != ChildPid) + MakeErrMsg(ErrMsg, "Child timed out but wouldn't die"); + else + MakeErrMsg(ErrMsg, "Child timed out", 0); + + WaitResult.ReturnCode = -2; // Timeout detected + return WaitResult; + } else if (errno != EINTR) { + MakeErrMsg(ErrMsg, "Error waiting for child process"); + WaitResult.ReturnCode = -1; + return WaitResult; + } + } + } + + // We exited normally without timeout, so turn off the timer. + if (SecondsToWait && !WaitUntilTerminates) { + alarm(0); + sigaction(SIGALRM, &Old, nullptr); + } + + // Return the proper exit status. Detect error conditions + // so we can return -1 for them and set ErrMsg informatively. + int result = 0; + if (WIFEXITED(status)) { + result = WEXITSTATUS(status); + WaitResult.ReturnCode = result; + + if (result == 127) { + if (ErrMsg) + *ErrMsg = llvm::sys::StrError(ENOENT); + WaitResult.ReturnCode = -1; + return WaitResult; + } + if (result == 126) { + if (ErrMsg) + *ErrMsg = "Program could not be executed"; + WaitResult.ReturnCode = -1; + return WaitResult; + } + } else if (WIFSIGNALED(status)) { + if (ErrMsg) { + *ErrMsg = strsignal(WTERMSIG(status)); +#ifdef WCOREDUMP + if (WCOREDUMP(status)) + *ErrMsg += " (core dumped)"; +#endif + } + // Return a special value to indicate that the process received an unhandled + // signal during execution as opposed to failing to execute. + WaitResult.ReturnCode = -2; + } + return WaitResult; +} + + std::error_code sys::ChangeStdinToBinary(){ + // Do nothing, as Unix doesn't differentiate between text and binary. + return std::error_code(); +} + + std::error_code sys::ChangeStdoutToBinary(){ + // Do nothing, as Unix doesn't differentiate between text and binary. + return std::error_code(); +} + +std::error_code +llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, + WindowsEncodingMethod Encoding /*unused*/) { + std::error_code EC; + llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::OpenFlags::F_Text); + + if (EC) + return EC; + + OS << Contents; + + if (OS.has_error()) + return make_error_code(errc::io_error); + + return EC; +} + +bool llvm::sys::argumentsFitWithinSystemLimits(ArrayRef<const char*> Args) { + static long ArgMax = sysconf(_SC_ARG_MAX); + + // System says no practical limit. + if (ArgMax == -1) + return true; + + // Conservatively account for space required by environment variables. + long HalfArgMax = ArgMax / 2; + + size_t ArgLength = 0; + for (ArrayRef<const char*>::iterator I = Args.begin(), E = Args.end(); + I != E; ++I) { + ArgLength += strlen(*I) + 1; + if (ArgLength > size_t(HalfArgMax)) { + return false; + } + } + return true; +} +} diff --git a/contrib/llvm/lib/Support/Unix/README.txt b/contrib/llvm/lib/Support/Unix/README.txt new file mode 100644 index 0000000..3d547c2 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/README.txt @@ -0,0 +1,16 @@ +llvm/lib/Support/Unix README +=========================== + +This directory provides implementations of the lib/System classes that +are common to two or more variants of UNIX. For example, the directory +structure underneath this directory could look like this: + +Unix - only code that is truly generic to all UNIX platforms + Posix - code that is specific to Posix variants of UNIX + SUS - code that is specific to the Single Unix Specification + SysV - code that is specific to System V variants of UNIX + +As a rule, only those directories actually needing to be created should be +created. Also, further subdirectories could be created to reflect versions of +the various standards. For example, under SUS there could be v1, v2, and v3 +subdirectories to reflect the three major versions of SUS. diff --git a/contrib/llvm/lib/Support/Unix/RWMutex.inc b/contrib/llvm/lib/Support/Unix/RWMutex.inc new file mode 100644 index 0000000..85a1043 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/RWMutex.inc @@ -0,0 +1,51 @@ +//= llvm/Support/Unix/RWMutex.inc - Unix Reader/Writer Mutual Exclusion Lock =// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix specific (non-pthread) RWMutex class. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Mutex.h" + +namespace llvm { + +using namespace sys; + +// This naive implementation treats readers the same as writers. This +// will therefore deadlock if a thread tries to acquire a read lock +// multiple times. + +RWMutexImpl::RWMutexImpl() : data_(new MutexImpl(false)) { } + +RWMutexImpl::~RWMutexImpl() { + delete static_cast<MutexImpl *>(data_); +} + +bool RWMutexImpl::reader_acquire() { + return static_cast<MutexImpl *>(data_)->acquire(); +} + +bool RWMutexImpl::reader_release() { + return static_cast<MutexImpl *>(data_)->release(); +} + +bool RWMutexImpl::writer_acquire() { + return static_cast<MutexImpl *>(data_)->acquire(); +} + +bool RWMutexImpl::writer_release() { + return static_cast<MutexImpl *>(data_)->release(); +} + +} diff --git a/contrib/llvm/lib/Support/Unix/Signals.inc b/contrib/llvm/lib/Support/Unix/Signals.inc new file mode 100644 index 0000000..061cdb3 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Signals.inc @@ -0,0 +1,443 @@ +//===- Signals.cpp - Generic Unix Signals Implementation -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines some helpful functions for dealing with the possibility of +// Unix signals occurring while your program is running. +// +//===----------------------------------------------------------------------===// + +#include "Unix.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/UniqueLock.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <string> +#if HAVE_EXECINFO_H +# include <execinfo.h> // For backtrace(). +#endif +#if HAVE_SIGNAL_H +#include <signal.h> +#endif +#if HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#if HAVE_CXXABI_H +#include <cxxabi.h> +#endif +#if HAVE_DLFCN_H +#include <dlfcn.h> +#endif +#if HAVE_MACH_MACH_H +#include <mach/mach.h> +#endif +#if HAVE_LINK_H +#include <link.h> +#endif + +using namespace llvm; + +static RETSIGTYPE SignalHandler(int Sig); // defined below. + +static ManagedStatic<SmartMutex<true> > SignalsMutex; + +/// InterruptFunction - The function to call if ctrl-c is pressed. +static void (*InterruptFunction)() = nullptr; + +static ManagedStatic<std::vector<std::string>> FilesToRemove; + +// IntSigs - Signals that represent requested termination. There's no bug +// or failure, or if there is, it's not our direct responsibility. For whatever +// reason, our continued execution is no longer desirable. +static const int IntSigs[] = { + SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2 +}; + +// KillSigs - Signals that represent that we have a bug, and our prompt +// termination has been ordered. +static const int KillSigs[] = { + SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGQUIT +#ifdef SIGSYS + , SIGSYS +#endif +#ifdef SIGXCPU + , SIGXCPU +#endif +#ifdef SIGXFSZ + , SIGXFSZ +#endif +#ifdef SIGEMT + , SIGEMT +#endif +}; + +static unsigned NumRegisteredSignals = 0; +static struct { + struct sigaction SA; + int SigNo; +} RegisteredSignalInfo[array_lengthof(IntSigs) + array_lengthof(KillSigs)]; + + +static void RegisterHandler(int Signal) { + assert(NumRegisteredSignals < array_lengthof(RegisteredSignalInfo) && + "Out of space for signal handlers!"); + + struct sigaction NewHandler; + + NewHandler.sa_handler = SignalHandler; + NewHandler.sa_flags = SA_NODEFER|SA_RESETHAND; + sigemptyset(&NewHandler.sa_mask); + + // Install the new handler, save the old one in RegisteredSignalInfo. + sigaction(Signal, &NewHandler, + &RegisteredSignalInfo[NumRegisteredSignals].SA); + RegisteredSignalInfo[NumRegisteredSignals].SigNo = Signal; + ++NumRegisteredSignals; +} + +static void RegisterHandlers() { + // We need to dereference the signals mutex during handler registration so + // that we force its construction. This is to prevent the first use being + // during handling an actual signal because you can't safely call new in a + // signal handler. + *SignalsMutex; + + // If the handlers are already registered, we're done. + if (NumRegisteredSignals != 0) return; + + for (auto S : IntSigs) RegisterHandler(S); + for (auto S : KillSigs) RegisterHandler(S); +} + +static void UnregisterHandlers() { + // Restore all of the signal handlers to how they were before we showed up. + for (unsigned i = 0, e = NumRegisteredSignals; i != e; ++i) + sigaction(RegisteredSignalInfo[i].SigNo, + &RegisteredSignalInfo[i].SA, nullptr); + NumRegisteredSignals = 0; +} + + +/// RemoveFilesToRemove - Process the FilesToRemove list. This function +/// should be called with the SignalsMutex lock held. +/// NB: This must be an async signal safe function. It cannot allocate or free +/// memory, even in debug builds. +static void RemoveFilesToRemove() { + // Avoid constructing ManagedStatic in the signal handler. + // If FilesToRemove is not constructed, there are no files to remove. + if (!FilesToRemove.isConstructed()) + return; + + // We avoid iterators in case of debug iterators that allocate or release + // memory. + std::vector<std::string>& FilesToRemoveRef = *FilesToRemove; + for (unsigned i = 0, e = FilesToRemoveRef.size(); i != e; ++i) { + const char *path = FilesToRemoveRef[i].c_str(); + + // Get the status so we can determine if it's a file or directory. If we + // can't stat the file, ignore it. + struct stat buf; + if (stat(path, &buf) != 0) + continue; + + // If this is not a regular file, ignore it. We want to prevent removal of + // special files like /dev/null, even if the compiler is being run with the + // super-user permissions. + if (!S_ISREG(buf.st_mode)) + continue; + + // Otherwise, remove the file. We ignore any errors here as there is nothing + // else we can do. + unlink(path); + } +} + +// SignalHandler - The signal handler that runs. +static RETSIGTYPE SignalHandler(int Sig) { + // Restore the signal behavior to default, so that the program actually + // crashes when we return and the signal reissues. This also ensures that if + // we crash in our signal handler that the program will terminate immediately + // instead of recursing in the signal handler. + UnregisterHandlers(); + + // Unmask all potentially blocked kill signals. + sigset_t SigMask; + sigfillset(&SigMask); + sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); + + { + unique_lock<SmartMutex<true>> Guard(*SignalsMutex); + RemoveFilesToRemove(); + + if (std::find(std::begin(IntSigs), std::end(IntSigs), Sig) + != std::end(IntSigs)) { + if (InterruptFunction) { + void (*IF)() = InterruptFunction; + Guard.unlock(); + InterruptFunction = nullptr; + IF(); // run the interrupt function. + return; + } + + Guard.unlock(); + raise(Sig); // Execute the default handler. + return; + } + } + + // Otherwise if it is a fault (like SEGV) run any handler. + llvm::sys::RunSignalHandlers(); + +#ifdef __s390__ + // On S/390, certain signals are delivered with PSW Address pointing to + // *after* the faulting instruction. Simply returning from the signal + // handler would continue execution after that point, instead of + // re-raising the signal. Raise the signal manually in those cases. + if (Sig == SIGILL || Sig == SIGFPE || Sig == SIGTRAP) + raise(Sig); +#endif +} + +void llvm::sys::RunInterruptHandlers() { + sys::SmartScopedLock<true> Guard(*SignalsMutex); + RemoveFilesToRemove(); +} + +void llvm::sys::SetInterruptFunction(void (*IF)()) { + { + sys::SmartScopedLock<true> Guard(*SignalsMutex); + InterruptFunction = IF; + } + RegisterHandlers(); +} + +// RemoveFileOnSignal - The public API +bool llvm::sys::RemoveFileOnSignal(StringRef Filename, + std::string* ErrMsg) { + { + sys::SmartScopedLock<true> Guard(*SignalsMutex); + FilesToRemove->push_back(Filename); + } + + RegisterHandlers(); + return false; +} + +// DontRemoveFileOnSignal - The public API +void llvm::sys::DontRemoveFileOnSignal(StringRef Filename) { + sys::SmartScopedLock<true> Guard(*SignalsMutex); + std::vector<std::string>::reverse_iterator RI = + std::find(FilesToRemove->rbegin(), FilesToRemove->rend(), Filename); + std::vector<std::string>::iterator I = FilesToRemove->end(); + if (RI != FilesToRemove->rend()) + I = FilesToRemove->erase(RI.base()-1); +} + +/// AddSignalHandler - Add a function to be called when a signal is delivered +/// to the process. The handler can have a cookie passed to it to identify +/// what instance of the handler it is. +void llvm::sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) { + CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie)); + RegisterHandlers(); +} + +#if defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) && HAVE_LINK_H && \ + (defined(__linux__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__NetBSD__)) +struct DlIteratePhdrData { + void **StackTrace; + int depth; + bool first; + const char **modules; + intptr_t *offsets; + const char *main_exec_name; +}; + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData*)arg; + const char *name = data->first ? data->main_exec_name : info->dlpi_name; + data->first = false; + for (int i = 0; i < info->dlpi_phnum; i++) { + const auto *phdr = &info->dlpi_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + intptr_t beg = info->dlpi_addr + phdr->p_vaddr; + intptr_t end = beg + phdr->p_memsz; + for (int j = 0; j < data->depth; j++) { + if (data->modules[j]) + continue; + intptr_t addr = (intptr_t)data->StackTrace[j]; + if (beg <= addr && addr < end) { + data->modules[j] = name; + data->offsets[j] = addr - info->dlpi_addr; + } + } + } + return 0; +} + +/// If this is an ELF platform, we can find all loaded modules and their virtual +/// addresses with dl_iterate_phdr. +static bool findModulesAndOffsets(void **StackTrace, int Depth, + const char **Modules, intptr_t *Offsets, + const char *MainExecutableName, + StringSaver &StrPool) { + DlIteratePhdrData data = {StackTrace, Depth, true, + Modules, Offsets, MainExecutableName}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return true; +} +#else +/// This platform does not have dl_iterate_phdr, so we do not yet know how to +/// find all loaded DSOs. +static bool findModulesAndOffsets(void **StackTrace, int Depth, + const char **Modules, intptr_t *Offsets, + const char *MainExecutableName, + StringSaver &StrPool) { + return false; +} +#endif // defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) && ... + +// PrintStackTrace - In the case of a program crash or fault, print out a stack +// trace so that the user has an indication of why and where we died. +// +// On glibc systems we have the 'backtrace' function, which works nicely, but +// doesn't demangle symbols. +void llvm::sys::PrintStackTrace(raw_ostream &OS) { +#if defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) + static void* StackTrace[256]; + // Use backtrace() to output a backtrace on Linux systems with glibc. + int depth = backtrace(StackTrace, + static_cast<int>(array_lengthof(StackTrace))); + if (printSymbolizedStackTrace(StackTrace, depth, OS)) + return; +#if HAVE_DLFCN_H && __GNUG__ + int width = 0; + for (int i = 0; i < depth; ++i) { + Dl_info dlinfo; + dladdr(StackTrace[i], &dlinfo); + const char* name = strrchr(dlinfo.dli_fname, '/'); + + int nwidth; + if (!name) nwidth = strlen(dlinfo.dli_fname); + else nwidth = strlen(name) - 1; + + if (nwidth > width) width = nwidth; + } + + for (int i = 0; i < depth; ++i) { + Dl_info dlinfo; + dladdr(StackTrace[i], &dlinfo); + + OS << format("%-2d", i); + + const char* name = strrchr(dlinfo.dli_fname, '/'); + if (!name) OS << format(" %-*s", width, dlinfo.dli_fname); + else OS << format(" %-*s", width, name+1); + + OS << format(" %#0*lx", (int)(sizeof(void*) * 2) + 2, + (unsigned long)StackTrace[i]); + + if (dlinfo.dli_sname != nullptr) { + OS << ' '; +# if HAVE_CXXABI_H + int res; + char* d = abi::__cxa_demangle(dlinfo.dli_sname, nullptr, nullptr, &res); +# else + char* d = NULL; +# endif + if (!d) OS << dlinfo.dli_sname; + else OS << d; + free(d); + + // FIXME: When we move to C++11, use %t length modifier. It's not in + // C++03 and causes gcc to issue warnings. Losing the upper 32 bits of + // the stack offset for a stack dump isn't likely to cause any problems. + OS << format(" + %u",(unsigned)((char*)StackTrace[i]- + (char*)dlinfo.dli_saddr)); + } + OS << '\n'; + } +#else + backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); +#endif +#endif +} + +static void PrintStackTraceSignalHandler(void *) { + PrintStackTrace(llvm::errs()); +} + +void llvm::sys::DisableSystemDialogsOnCrash() {} + +/// PrintStackTraceOnErrorSignal - When an error signal (such as SIGABRT or +/// SIGSEGV) is delivered to the process, print a stack trace and then exit. +void llvm::sys::PrintStackTraceOnErrorSignal(bool DisableCrashReporting) { + AddSignalHandler(PrintStackTraceSignalHandler, nullptr); + +#if defined(__APPLE__) && defined(ENABLE_CRASH_OVERRIDES) + // Environment variable to disable any kind of crash dialog. + if (DisableCrashReporting || getenv("LLVM_DISABLE_CRASH_REPORT")) { + mach_port_t self = mach_task_self(); + + exception_mask_t mask = EXC_MASK_CRASH; + + kern_return_t ret = task_set_exception_ports(self, + mask, + MACH_PORT_NULL, + EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + THREAD_STATE_NONE); + (void)ret; + } +#endif +} + + +/***/ + +// On Darwin, raise sends a signal to the main thread instead of the current +// thread. This has the unfortunate effect that assert() and abort() will end up +// bypassing our crash recovery attempts. We work around this for anything in +// the same linkage unit by just defining our own versions of the assert handler +// and abort. + +#if defined(__APPLE__) && defined(ENABLE_CRASH_OVERRIDES) + +#include <signal.h> +#include <pthread.h> + +int raise(int sig) { + return pthread_kill(pthread_self(), sig); +} + +void __assert_rtn(const char *func, + const char *file, + int line, + const char *expr) { + if (func) + fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", + expr, func, file, line); + else + fprintf(stderr, "Assertion failed: (%s), file %s, line %d.\n", + expr, file, line); + abort(); +} + +void abort() { + raise(SIGABRT); + usleep(1000); + __builtin_trap(); +} + +#endif diff --git a/contrib/llvm/lib/Support/Unix/ThreadLocal.inc b/contrib/llvm/lib/Support/Unix/ThreadLocal.inc new file mode 100644 index 0000000..31c3f38 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/ThreadLocal.inc @@ -0,0 +1,69 @@ +//=== llvm/Support/Unix/ThreadLocal.inc - Unix Thread Local Data -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix specific (non-pthread) ThreadLocal class. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +#if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_GETSPECIFIC) + +#include <cassert> +#include <pthread.h> +#include <stdlib.h> + +namespace llvm { +using namespace sys; + +ThreadLocalImpl::ThreadLocalImpl() : data() { + static_assert(sizeof(pthread_key_t) <= sizeof(data), "size too big"); + pthread_key_t* key = reinterpret_cast<pthread_key_t*>(&data); + int errorcode = pthread_key_create(key, nullptr); + assert(errorcode == 0); + (void) errorcode; +} + +ThreadLocalImpl::~ThreadLocalImpl() { + pthread_key_t* key = reinterpret_cast<pthread_key_t*>(&data); + int errorcode = pthread_key_delete(*key); + assert(errorcode == 0); + (void) errorcode; +} + +void ThreadLocalImpl::setInstance(const void* d) { + pthread_key_t* key = reinterpret_cast<pthread_key_t*>(&data); + int errorcode = pthread_setspecific(*key, d); + assert(errorcode == 0); + (void) errorcode; +} + +void *ThreadLocalImpl::getInstance() { + pthread_key_t* key = reinterpret_cast<pthread_key_t*>(&data); + return pthread_getspecific(*key); +} + +void ThreadLocalImpl::removeInstance() { + setInstance(nullptr); +} + +} +#else +namespace llvm { +using namespace sys; +ThreadLocalImpl::ThreadLocalImpl() : data() { } +ThreadLocalImpl::~ThreadLocalImpl() { } +void ThreadLocalImpl::setInstance(const void* d) { data = const_cast<void*>(d);} +void *ThreadLocalImpl::getInstance() { return data; } +void ThreadLocalImpl::removeInstance() { setInstance(0); } +} +#endif diff --git a/contrib/llvm/lib/Support/Unix/TimeValue.inc b/contrib/llvm/lib/Support/Unix/TimeValue.inc new file mode 100644 index 0000000..042e0da --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/TimeValue.inc @@ -0,0 +1,54 @@ +//===- Unix/TimeValue.cpp - Unix TimeValue Implementation -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Unix specific portion of the TimeValue class. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on *all* UNIX variants. +//===----------------------------------------------------------------------===// + +#include "Unix.h" + +namespace llvm { + using namespace sys; + +std::string TimeValue::str() const { + time_t OurTime = time_t(this->toEpochTime()); + struct tm Storage; + struct tm *LT = ::localtime_r(&OurTime, &Storage); + assert(LT); + char Buffer1[sizeof("YYYY-MM-DD HH:MM:SS")]; + strftime(Buffer1, sizeof(Buffer1), "%Y-%m-%d %H:%M:%S", LT); + char Buffer2[sizeof("YYYY-MM-DD HH:MM:SS.MMMUUUNNN")]; + snprintf(Buffer2, sizeof(Buffer2), "%s.%.9u", Buffer1, this->nanoseconds()); + return std::string(Buffer2); +} + +TimeValue TimeValue::now() { + struct timeval the_time; + timerclear(&the_time); + if (0 != ::gettimeofday(&the_time,nullptr)) { + // This is *really* unlikely to occur because the only gettimeofday + // errors concern the timezone parameter which we're passing in as 0. + // In the unlikely case it does happen, just return MinTime, no error + // message needed. + return MinTime(); + } + + return TimeValue( + static_cast<TimeValue::SecondsType>( the_time.tv_sec + + PosixZeroTimeSeconds ), + static_cast<TimeValue::NanoSecondsType>( the_time.tv_usec * + NANOSECONDS_PER_MICROSECOND ) ); +} + +} diff --git a/contrib/llvm/lib/Support/Unix/Unix.h b/contrib/llvm/lib/Support/Unix/Unix.h new file mode 100644 index 0000000..871e612 --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Unix.h @@ -0,0 +1,68 @@ +//===- llvm/Support/Unix/Unix.h - Common Unix Include File -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines things specific to Unix implementations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_SUPPORT_UNIX_UNIX_H +#define LLVM_LIB_SUPPORT_UNIX_UNIX_H + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only generic UNIX code that +//=== is guaranteed to work on all UNIX variants. +//===----------------------------------------------------------------------===// + +#include "llvm/Config/config.h" // Get autoconf configuration settings +#include "llvm/Support/Errno.h" +#include <algorithm> +#include <assert.h> +#include <cerrno> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <string> +#include <sys/types.h> +#include <sys/wait.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif +#include <time.h> + +#ifdef HAVE_DLFCN_H +# include <dlfcn.h> +#endif + +/// This function builds an error message into \p ErrMsg using the \p prefix +/// string and the Unix error number given by \p errnum. If errnum is -1, the +/// default then the value of errno is used. +/// @brief Make an error message +/// +/// If the error number can be converted to a string, it will be +/// separated from prefix by ": ". +static inline bool MakeErrMsg( + std::string* ErrMsg, const std::string& prefix, int errnum = -1) { + if (!ErrMsg) + return true; + if (errnum == -1) + errnum = errno; + *ErrMsg = prefix + ": " + llvm::sys::StrError(errnum); + return true; +} + +#endif diff --git a/contrib/llvm/lib/Support/Unix/Watchdog.inc b/contrib/llvm/lib/Support/Unix/Watchdog.inc new file mode 100644 index 0000000..5d89c0e --- /dev/null +++ b/contrib/llvm/lib/Support/Unix/Watchdog.inc @@ -0,0 +1,32 @@ +//===--- Unix/Watchdog.inc - Unix Watchdog Implementation -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides the generic Unix implementation of the Watchdog class. +// +//===----------------------------------------------------------------------===// + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +namespace llvm { + namespace sys { + Watchdog::Watchdog(unsigned int seconds) { +#ifdef HAVE_UNISTD_H + alarm(seconds); +#endif + } + + Watchdog::~Watchdog() { +#ifdef HAVE_UNISTD_H + alarm(0); +#endif + } + } +} |