diff options
Diffstat (limited to 'contrib/llvm/lib/Support/Windows/Program.inc')
| -rw-r--r-- | contrib/llvm/lib/Support/Windows/Program.inc | 523 |
1 files changed, 0 insertions, 523 deletions
diff --git a/contrib/llvm/lib/Support/Windows/Program.inc b/contrib/llvm/lib/Support/Windows/Program.inc deleted file mode 100644 index 0f54e59ee55b..000000000000 --- a/contrib/llvm/lib/Support/Windows/Program.inc +++ /dev/null @@ -1,523 +0,0 @@ -//===- Win32/Program.cpp - Win32 Program Implementation ------- -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file provides the Win32 specific implementation of the Program class. -// -//===----------------------------------------------------------------------===// - -#include "WindowsSupport.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/ConvertUTF.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/WindowsError.h" -#include "llvm/Support/raw_ostream.h" -#include <cstdio> -#include <fcntl.h> -#include <io.h> -#include <malloc.h> -#include <numeric> - -//===----------------------------------------------------------------------===// -//=== WARNING: Implementation here must contain only Win32 specific code -//=== and must not be UNIX code -//===----------------------------------------------------------------------===// - -namespace llvm { - -ProcessInfo::ProcessInfo() : Pid(0), Process(0), ReturnCode(0) {} - -ErrorOr<std::string> sys::findProgramByName(StringRef Name, - ArrayRef<StringRef> Paths) { - assert(!Name.empty() && "Must have a name!"); - - if (Name.find_first_of("/\\") != StringRef::npos) - return std::string(Name); - - const wchar_t *Path = nullptr; - std::wstring PathStorage; - if (!Paths.empty()) { - PathStorage.reserve(Paths.size() * MAX_PATH); - for (unsigned i = 0; i < Paths.size(); ++i) { - if (i) - PathStorage.push_back(L';'); - StringRef P = Paths[i]; - SmallVector<wchar_t, MAX_PATH> TmpPath; - if (std::error_code EC = windows::UTF8ToUTF16(P, TmpPath)) - return EC; - PathStorage.append(TmpPath.begin(), TmpPath.end()); - } - Path = PathStorage.c_str(); - } - - SmallVector<wchar_t, MAX_PATH> U16Name; - if (std::error_code EC = windows::UTF8ToUTF16(Name, U16Name)) - return EC; - - SmallVector<StringRef, 12> PathExts; - PathExts.push_back(""); - PathExts.push_back(".exe"); // FIXME: This must be in %PATHEXT%. - if (const char *PathExtEnv = std::getenv("PATHEXT")) - SplitString(PathExtEnv, PathExts, ";"); - - SmallVector<wchar_t, MAX_PATH> U16Result; - DWORD Len = MAX_PATH; - for (StringRef Ext : PathExts) { - SmallVector<wchar_t, MAX_PATH> U16Ext; - if (std::error_code EC = windows::UTF8ToUTF16(Ext, U16Ext)) - return EC; - - do { - U16Result.reserve(Len); - // Lets attach the extension manually. That is needed for files - // with a point in name like aaa.bbb. SearchPathW will not add extension - // from its argument to such files because it thinks they already had one. - SmallVector<wchar_t, MAX_PATH> U16NameExt; - if (std::error_code EC = - windows::UTF8ToUTF16(Twine(Name + Ext).str(), U16NameExt)) - return EC; - - Len = ::SearchPathW(Path, c_str(U16NameExt), nullptr, - U16Result.capacity(), U16Result.data(), nullptr); - } while (Len > U16Result.capacity()); - - if (Len != 0) - break; // Found it. - } - - if (Len == 0) - return mapWindowsError(::GetLastError()); - - U16Result.set_size(Len); - - SmallVector<char, MAX_PATH> U8Result; - if (std::error_code EC = - windows::UTF16ToUTF8(U16Result.data(), U16Result.size(), U8Result)) - return EC; - - return std::string(U8Result.begin(), U8Result.end()); -} - -bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) { - if (!ErrMsg) - return true; - char *buffer = NULL; - DWORD LastError = GetLastError(); - DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, LastError, 0, (LPSTR)&buffer, 1, NULL); - if (R) - *ErrMsg = prefix + ": " + buffer; - else - *ErrMsg = prefix + ": Unknown error"; - *ErrMsg += " (0x" + llvm::utohexstr(LastError) + ")"; - - LocalFree(buffer); - return R != 0; -} - -static HANDLE RedirectIO(Optional<StringRef> Path, int fd, - std::string *ErrMsg) { - HANDLE h; - if (!Path) { - 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 (Path->empty()) { - // Don't play long-path tricks on "NUL". - if (windows::UTF8ToUTF16(fname, fnameUnicode)) - return INVALID_HANDLE_VALUE; - } else { - if (path::widenPath(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, fname + ": Can't open file for " + - (fd ? "input" : "output")); - } - - return h; -} - -} - -static bool Execute(ProcessInfo &PI, StringRef Program, - ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env, - ArrayRef<Optional<StringRef>> Redirects, - unsigned MemoryLimit, std::string *ErrMsg) { - if (!sys::fs::can_execute(Program)) { - if (ErrMsg) - *ErrMsg = "program not executable"; - return false; - } - - // can_execute may succeed by looking at Program + ".exe". CreateProcessW - // will implicitly add the .exe if we provide a command line without an - // executable path, but since we use an explicit executable, we have to add - // ".exe" ourselves. - SmallString<64> ProgramStorage; - if (!sys::fs::exists(Program)) - Program = Twine(Program + ".exe").toStringRef(ProgramStorage); - - // 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). - std::string Command = flattenWindowsCommandLine(Args); - - // The pointer to the environment block for the new process. - std::vector<wchar_t> EnvBlock; - - if (Env) { - // 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 (const auto E : *Env) { - SmallVector<wchar_t, MAX_PATH> EnvString; - if (std::error_code ec = windows::UTF8ToUTF16(E, 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.empty()) { - 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 (std::error_code ec = path::widenPath(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 (std::error_code ec = windows::UTF8ToUTF16(Command, 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.Process = 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; -} - -static bool argNeedsQuotes(StringRef Arg) { - if (Arg.empty()) - return true; - return StringRef::npos != Arg.find_first_of("\t \"&\'()*<>\\`^|\n"); -} - -static std::string quoteSingleArg(StringRef Arg) { - std::string Result; - Result.push_back('"'); - - while (!Arg.empty()) { - size_t FirstNonBackslash = Arg.find_first_not_of('\\'); - size_t BackslashCount = FirstNonBackslash; - if (FirstNonBackslash == StringRef::npos) { - // The entire remainder of the argument is backslashes. Escape all of - // them and just early out. - BackslashCount = Arg.size(); - Result.append(BackslashCount * 2, '\\'); - break; - } - - if (Arg[FirstNonBackslash] == '\"') { - // This is an embedded quote. Escape all preceding backslashes, then - // add one additional backslash to escape the quote. - Result.append(BackslashCount * 2 + 1, '\\'); - Result.push_back('\"'); - } else { - // This is just a normal character. Don't escape any of the preceding - // backslashes, just append them as they are and then append the - // character. - Result.append(BackslashCount, '\\'); - Result.push_back(Arg[FirstNonBackslash]); - } - - // Drop all the backslashes, plus the following character. - Arg = Arg.drop_front(FirstNonBackslash + 1); - } - - Result.push_back('"'); - return Result; -} - -namespace llvm { -std::string sys::flattenWindowsCommandLine(ArrayRef<StringRef> Args) { - std::string Command; - for (StringRef Arg : Args) { - if (argNeedsQuotes(Arg)) - Command += quoteSingleArg(Arg); - else - Command += Arg; - - Command.push_back(' '); - } - - return Command; -} - -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.Process && PI.Process != INVALID_HANDLE_VALUE) && - "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.Process, milliSecondsToWait); - if (WaitStatus == WAIT_TIMEOUT) { - if (SecondsToWait) { - if (!TerminateProcess(PI.Process, 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.Process); - return WaitResult; - } - WaitForSingleObject(PI.Process, INFINITE); - CloseHandle(PI.Process); - } else { - // Non-blocking wait. - return ProcessInfo(); - } - } - - // Get its exit status. - DWORD status; - BOOL rc = GetExitCodeProcess(PI.Process, &status); - DWORD err = GetLastError(); - if (err != ERROR_INVALID_HANDLE) - CloseHandle(PI.Process); - - 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; -} - -std::error_code sys::ChangeStdinToBinary() { - int result = _setmode(_fileno(stdin), _O_BINARY); - if (result == -1) - return std::error_code(errno, std::generic_category()); - return std::error_code(); -} - -std::error_code sys::ChangeStdoutToBinary() { - int result = _setmode(_fileno(stdout), _O_BINARY); - if (result == -1) - return std::error_code(errno, std::generic_category()); - return std::error_code(); -} - -std::error_code -llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, - WindowsEncodingMethod Encoding) { - std::error_code EC; - llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::F_Text); - if (EC) - return EC; - - if (Encoding == WEM_UTF8) { - OS << Contents; - } else if (Encoding == WEM_CurrentCodePage) { - SmallVector<wchar_t, 1> ArgsUTF16; - SmallVector<char, 1> ArgsCurCP; - - if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) - return EC; - - if ((EC = windows::UTF16ToCurCP( - ArgsUTF16.data(), ArgsUTF16.size(), ArgsCurCP))) - return EC; - - OS.write(ArgsCurCP.data(), ArgsCurCP.size()); - } else if (Encoding == WEM_UTF16) { - SmallVector<wchar_t, 1> ArgsUTF16; - - if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) - return EC; - - // Endianness guessing - char BOM[2]; - uint16_t src = UNI_UTF16_BYTE_ORDER_MARK_NATIVE; - memcpy(BOM, &src, 2); - OS.write(BOM, 2); - OS.write((char *)ArgsUTF16.data(), ArgsUTF16.size() << 1); - } else { - llvm_unreachable("Unknown encoding"); - } - - if (OS.has_error()) - return make_error_code(errc::io_error); - - return EC; -} - -bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, - ArrayRef<StringRef> Args) { - // The documented max length of the command line passed to CreateProcess. - static const size_t MaxCommandStringLength = 32768; - SmallVector<StringRef, 8> FullArgs; - FullArgs.push_back(Program); - FullArgs.append(Args.begin(), Args.end()); - std::string Result = flattenWindowsCommandLine(FullArgs); - return (Result.size() + 1) <= MaxCommandStringLength; -} -} |
