diff options
Diffstat (limited to 'contrib/llvm/lib/Support/Windows/Program.inc')
-rw-r--r-- | contrib/llvm/lib/Support/Windows/Program.inc | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/contrib/llvm/lib/Support/Windows/Program.inc b/contrib/llvm/lib/Support/Windows/Program.inc new file mode 100644 index 0000000..dc09738 --- /dev/null +++ b/contrib/llvm/lib/Support/Windows/Program.inc @@ -0,0 +1,462 @@ +//===- Win32/Program.cpp - Win32 Program 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 Win32 specific implementation of the Program class. +// +//===----------------------------------------------------------------------===// + +#include "Windows.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/FileSystem.h" +#include <cstdio> +#include <fcntl.h> +#include <io.h> +#include <malloc.h> + +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only Win32 specific code +//=== and must not be UNIX code +//===----------------------------------------------------------------------===// + +namespace llvm { +using namespace sys; + +ProcessInfo::ProcessInfo() : ProcessHandle(0), Pid(0), ReturnCode(0) {} + +// This function just uses the PATH environment variable to find the program. +std::string sys::FindProgramByName(const std::string &progName) { + // Check some degenerate cases + if (progName.length() == 0) // no program + return ""; + std::string temp = progName; + // Return paths with slashes verbatim. + if (progName.find('\\') != std::string::npos || + progName.find('/') != std::string::npos) + return temp; + + // At this point, the file name is valid and does not contain slashes. + // Let Windows search for it. + SmallVector<wchar_t, MAX_PATH> progNameUnicode; + if (windows::UTF8ToUTF16(progName, progNameUnicode)) + return ""; + + SmallVector<wchar_t, MAX_PATH> buffer; + DWORD len = MAX_PATH; + do { + buffer.reserve(len); + len = ::SearchPathW(NULL, progNameUnicode.data(), L".exe", + buffer.capacity(), buffer.data(), NULL); + + // See if it wasn't found. + if (len == 0) + return ""; + + // Buffer was too small; grow and retry. + } while (len > buffer.capacity()); + + buffer.set_size(len); + SmallVector<char, MAX_PATH> result; + if (windows::UTF16ToUTF8(buffer.begin(), buffer.size(), result)) + return ""; + + return std::string(result.data(), result.size()); +} + +static HANDLE RedirectIO(const StringRef *path, int fd, std::string* ErrMsg) { + HANDLE h; + if (path == 0) { + if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd), + GetCurrentProcess(), &h, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + return INVALID_HANDLE_VALUE; + return h; + } + + std::string fname; + if (path->empty()) + fname = "NUL"; + else + fname = *path; + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = 0; + sa.bInheritHandle = TRUE; + + SmallVector<wchar_t, 128> fnameUnicode; + if (windows::UTF8ToUTF16(fname, fnameUnicode)) + return INVALID_HANDLE_VALUE; + + h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ, + FILE_SHARE_READ, &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + MakeErrMsg(ErrMsg, std::string(fname) + ": Can't open file for " + + (fd ? "input: " : "output: ")); + } + + return h; +} + +/// ArgNeedsQuotes - Check whether argument needs to be quoted when calling +/// CreateProcess. +static bool ArgNeedsQuotes(const char *Str) { + return Str[0] == '\0' || strpbrk(Str, "\t \"&\'()*<>\\`^|") != 0; +} + +/// CountPrecedingBackslashes - Returns the number of backslashes preceding Cur +/// in the C string Start. +static unsigned int CountPrecedingBackslashes(const char *Start, + const char *Cur) { + unsigned int Count = 0; + --Cur; + while (Cur >= Start && *Cur == '\\') { + ++Count; + --Cur; + } + return Count; +} + +/// EscapePrecedingEscapes - Append a backslash to Dst for every backslash +/// preceding Cur in the Start string. Assumes Dst has enough space. +static char *EscapePrecedingEscapes(char *Dst, const char *Start, + const char *Cur) { + unsigned PrecedingEscapes = CountPrecedingBackslashes(Start, Cur); + while (PrecedingEscapes > 0) { + *Dst++ = '\\'; + --PrecedingEscapes; + } + return Dst; +} + +/// ArgLenWithQuotes - Check whether argument needs to be quoted when calling +/// CreateProcess and returns length of quoted arg with escaped quotes +static unsigned int ArgLenWithQuotes(const char *Str) { + const char *Start = Str; + bool Quoted = ArgNeedsQuotes(Str); + unsigned int len = Quoted ? 2 : 0; + + while (*Str != '\0') { + if (*Str == '\"') { + // We need to add a backslash, but ensure that it isn't escaped. + unsigned PrecedingEscapes = CountPrecedingBackslashes(Start, Str); + len += PrecedingEscapes + 1; + } + // Note that we *don't* need to escape runs of backslashes that don't + // precede a double quote! See MSDN: + // http://msdn.microsoft.com/en-us/library/17w5ykft%28v=vs.85%29.aspx + + ++len; + ++Str; + } + + if (Quoted) { + // Make sure the closing quote doesn't get escaped by a trailing backslash. + unsigned PrecedingEscapes = CountPrecedingBackslashes(Start, Str); + len += PrecedingEscapes + 1; + } + + return len; +} + +} + +static bool Execute(ProcessInfo &PI, StringRef Program, const char **args, + const char **envp, const StringRef **redirects, + unsigned memoryLimit, std::string *ErrMsg) { + if (!sys::fs::can_execute(Program)) { + if (ErrMsg) + *ErrMsg = "program not executable"; + return false; + } + + // Windows wants a command line, not an array of args, to pass to the new + // process. We have to concatenate them all, while quoting the args that + // have embedded spaces (or are empty). + + // First, determine the length of the command line. + unsigned len = 0; + for (unsigned i = 0; args[i]; i++) { + len += ArgLenWithQuotes(args[i]) + 1; + } + + // Now build the command line. + OwningArrayPtr<char> command(new char[len+1]); + char *p = command.get(); + + for (unsigned i = 0; args[i]; i++) { + const char *arg = args[i]; + const char *start = arg; + + bool needsQuoting = ArgNeedsQuotes(arg); + if (needsQuoting) + *p++ = '"'; + + while (*arg != '\0') { + if (*arg == '\"') { + // Escape all preceding escapes (if any), and then escape the quote. + p = EscapePrecedingEscapes(p, start, arg); + *p++ = '\\'; + } + + *p++ = *arg++; + } + + if (needsQuoting) { + // Make sure our quote doesn't get escaped by a trailing backslash. + p = EscapePrecedingEscapes(p, start, arg); + *p++ = '"'; + } + *p++ = ' '; + } + + *p = 0; + + // The pointer to the environment block for the new process. + std::vector<wchar_t> EnvBlock; + + if (envp) { + // An environment block consists of a null-terminated block of + // null-terminated strings. Convert the array of environment variables to + // an environment block by concatenating them. + for (unsigned i = 0; envp[i]; ++i) { + SmallVector<wchar_t, MAX_PATH> EnvString; + if (error_code ec = windows::UTF8ToUTF16(envp[i], EnvString)) { + SetLastError(ec.value()); + MakeErrMsg(ErrMsg, "Unable to convert environment variable to UTF-16"); + return false; + } + + EnvBlock.insert(EnvBlock.end(), EnvString.begin(), EnvString.end()); + EnvBlock.push_back(0); + } + EnvBlock.push_back(0); + } + + // Create a child process. + STARTUPINFOW si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.hStdInput = INVALID_HANDLE_VALUE; + si.hStdOutput = INVALID_HANDLE_VALUE; + si.hStdError = INVALID_HANDLE_VALUE; + + if (redirects) { + si.dwFlags = STARTF_USESTDHANDLES; + + si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg); + if (si.hStdInput == INVALID_HANDLE_VALUE) { + MakeErrMsg(ErrMsg, "can't redirect stdin"); + return false; + } + si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg); + if (si.hStdOutput == INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdInput); + MakeErrMsg(ErrMsg, "can't redirect stdout"); + 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 handle already open for stdout. + if (!DuplicateHandle(GetCurrentProcess(), si.hStdOutput, + GetCurrentProcess(), &si.hStdError, + 0, TRUE, DUPLICATE_SAME_ACCESS)) { + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + MakeErrMsg(ErrMsg, "can't dup stderr to stdout"); + return false; + } + } else { + // Just redirect stderr + si.hStdError = RedirectIO(redirects[2], 2, ErrMsg); + if (si.hStdError == INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + MakeErrMsg(ErrMsg, "can't redirect stderr"); + return false; + } + } + } + + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + + fflush(stdout); + fflush(stderr); + + SmallVector<wchar_t, MAX_PATH> ProgramUtf16; + if (error_code ec = windows::UTF8ToUTF16(Program, ProgramUtf16)) { + SetLastError(ec.value()); + MakeErrMsg(ErrMsg, + std::string("Unable to convert application name to UTF-16")); + return false; + } + + SmallVector<wchar_t, MAX_PATH> CommandUtf16; + if (error_code ec = windows::UTF8ToUTF16(command.get(), CommandUtf16)) { + SetLastError(ec.value()); + MakeErrMsg(ErrMsg, + std::string("Unable to convert command-line to UTF-16")); + return false; + } + + BOOL rc = CreateProcessW(ProgramUtf16.data(), CommandUtf16.data(), 0, 0, + TRUE, CREATE_UNICODE_ENVIRONMENT, + EnvBlock.empty() ? 0 : EnvBlock.data(), 0, &si, + &pi); + DWORD err = GetLastError(); + + // Regardless of whether the process got created or not, we are done with + // the handles we created for it to inherit. + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + // Now return an error if the process didn't get created. + if (!rc) { + SetLastError(err); + MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") + + Program.str() + "'"); + return false; + } + + PI.Pid = pi.dwProcessId; + PI.ProcessHandle = pi.hProcess; + + // Make sure these get closed no matter what. + ScopedCommonHandle hThread(pi.hThread); + + // Assign the process to a job if a memory limit is defined. + ScopedJobHandle hJob; + if (memoryLimit != 0) { + hJob = CreateJobObjectW(0, 0); + bool success = false; + if (hJob) { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; + memset(&jeli, 0, sizeof(jeli)); + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; + jeli.ProcessMemoryLimit = uintptr_t(memoryLimit) * 1048576; + if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, + &jeli, sizeof(jeli))) { + if (AssignProcessToJobObject(hJob, pi.hProcess)) + success = true; + } + } + if (!success) { + SetLastError(GetLastError()); + MakeErrMsg(ErrMsg, std::string("Unable to set memory limit")); + TerminateProcess(pi.hProcess, 1); + WaitForSingleObject(pi.hProcess, INFINITE); + return false; + } + } + + return true; +} + +namespace llvm { +ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, + bool WaitUntilChildTerminates, std::string *ErrMsg) { + assert(PI.Pid && "invalid pid to wait on, process not started?"); + assert(PI.ProcessHandle && + "invalid process handle to wait on, process not started?"); + DWORD milliSecondsToWait = 0; + if (WaitUntilChildTerminates) + milliSecondsToWait = INFINITE; + else if (SecondsToWait > 0) + milliSecondsToWait = SecondsToWait * 1000; + + ProcessInfo WaitResult = PI; + DWORD WaitStatus = WaitForSingleObject(PI.ProcessHandle, milliSecondsToWait); + if (WaitStatus == WAIT_TIMEOUT) { + if (SecondsToWait) { + if (!TerminateProcess(PI.ProcessHandle, 1)) { + if (ErrMsg) + MakeErrMsg(ErrMsg, "Failed to terminate timed-out program."); + + // -2 indicates a crash or timeout as opposed to failure to execute. + WaitResult.ReturnCode = -2; + CloseHandle(PI.ProcessHandle); + return WaitResult; + } + WaitForSingleObject(PI.ProcessHandle, INFINITE); + CloseHandle(PI.ProcessHandle); + } else { + // Non-blocking wait. + return ProcessInfo(); + } + } + + // Get its exit status. + DWORD status; + BOOL rc = GetExitCodeProcess(PI.ProcessHandle, &status); + DWORD err = GetLastError(); + CloseHandle(PI.ProcessHandle); + + if (!rc) { + SetLastError(err); + if (ErrMsg) + MakeErrMsg(ErrMsg, "Failed getting status for program."); + + // -2 indicates a crash or timeout as opposed to failure to execute. + WaitResult.ReturnCode = -2; + return WaitResult; + } + + if (!status) + return WaitResult; + + // Pass 10(Warning) and 11(Error) to the callee as negative value. + if ((status & 0xBFFF0000U) == 0x80000000U) + WaitResult.ReturnCode = static_cast<int>(status); + else if (status & 0xFF) + WaitResult.ReturnCode = status & 0x7FFFFFFF; + else + WaitResult.ReturnCode = 1; + + return WaitResult; +} + +error_code sys::ChangeStdinToBinary(){ + int result = _setmode( _fileno(stdin), _O_BINARY ); + if (result == -1) + return error_code(errno, generic_category()); + return make_error_code(errc::success); +} + +error_code sys::ChangeStdoutToBinary(){ + int result = _setmode( _fileno(stdout), _O_BINARY ); + if (result == -1) + return error_code(errno, generic_category()); + return make_error_code(errc::success); +} + +error_code sys::ChangeStderrToBinary(){ + int result = _setmode( _fileno(stderr), _O_BINARY ); + if (result == -1) + return error_code(errno, generic_category()); + return make_error_code(errc::success); +} + +bool llvm::sys::argumentsFitWithinSystemLimits(ArrayRef<const char*> Args) { + // The documented max length of the command line passed to CreateProcess. + static const size_t MaxCommandStringLength = 32768; + size_t ArgLength = 0; + for (ArrayRef<const char*>::iterator I = Args.begin(), E = Args.end(); + I != E; ++I) { + // Account for the trailing space for every arg but the last one and the + // trailing NULL of the last argument. + ArgLength += ArgLenWithQuotes(*I) + 1; + if (ArgLength > MaxCommandStringLength) { + return false; + } + } + return true; +} +} |