1//===- Win32/Program.cpp - Win32 Program Implementation ------- -*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// This file provides the Win32 specific implementation of the Program class. 10// 11//===----------------------------------------------------------------------===// 12 13#include "llvm/ADT/StringExtras.h" 14#include "llvm/Support/ConvertUTF.h" 15#include "llvm/Support/Errc.h" 16#include "llvm/Support/FileSystem.h" 17#include "llvm/Support/Path.h" 18#include "llvm/Support/Windows/WindowsSupport.h" 19#include "llvm/Support/WindowsError.h" 20#include "llvm/Support/raw_ostream.h" 21#include <psapi.h> 22#include <cstdio> 23#include <fcntl.h> 24#include <io.h> 25#include <malloc.h> 26#include <numeric> 27 28//===----------------------------------------------------------------------===// 29//=== WARNING: Implementation here must contain only Win32 specific code 30//=== and must not be UNIX code 31//===----------------------------------------------------------------------===// 32 33namespace llvm { 34 35ProcessInfo::ProcessInfo() : Pid(0), Process(0), ReturnCode(0) {} 36 37ErrorOr<std::string> sys::findProgramByName(StringRef Name, 38 ArrayRef<StringRef> Paths) { 39 assert(!Name.empty() && "Must have a name!"); 40 41 if (Name.find_first_of("/\\") != StringRef::npos) 42 return std::string(Name); 43 44 const wchar_t *Path = nullptr; 45 std::wstring PathStorage; 46 if (!Paths.empty()) { 47 PathStorage.reserve(Paths.size() * MAX_PATH); 48 for (unsigned i = 0; i < Paths.size(); ++i) { 49 if (i) 50 PathStorage.push_back(L';'); 51 StringRef P = Paths[i]; 52 SmallVector<wchar_t, MAX_PATH> TmpPath; 53 if (std::error_code EC = windows::UTF8ToUTF16(P, TmpPath)) 54 return EC; 55 PathStorage.append(TmpPath.begin(), TmpPath.end()); 56 } 57 Path = PathStorage.c_str(); 58 } 59 60 SmallVector<wchar_t, MAX_PATH> U16Name; 61 if (std::error_code EC = windows::UTF8ToUTF16(Name, U16Name)) 62 return EC; 63 64 SmallVector<StringRef, 12> PathExts; 65 PathExts.push_back(""); 66 PathExts.push_back(".exe"); // FIXME: This must be in %PATHEXT%. 67 if (const char *PathExtEnv = std::getenv("PATHEXT")) 68 SplitString(PathExtEnv, PathExts, ";"); 69 70 SmallVector<wchar_t, MAX_PATH> U16Result; 71 DWORD Len = MAX_PATH; 72 for (StringRef Ext : PathExts) { 73 SmallVector<wchar_t, MAX_PATH> U16Ext; 74 if (std::error_code EC = windows::UTF8ToUTF16(Ext, U16Ext)) 75 return EC; 76 77 do { 78 U16Result.reserve(Len); 79 // Lets attach the extension manually. That is needed for files 80 // with a point in name like aaa.bbb. SearchPathW will not add extension 81 // from its argument to such files because it thinks they already had one. 82 SmallVector<wchar_t, MAX_PATH> U16NameExt; 83 if (std::error_code EC = 84 windows::UTF8ToUTF16(Twine(Name + Ext).str(), U16NameExt)) 85 return EC; 86 87 Len = ::SearchPathW(Path, c_str(U16NameExt), nullptr, 88 U16Result.capacity(), U16Result.data(), nullptr); 89 } while (Len > U16Result.capacity()); 90 91 if (Len != 0) 92 break; // Found it. 93 } 94 95 if (Len == 0) 96 return mapWindowsError(::GetLastError()); 97 98 U16Result.set_size(Len); 99 100 SmallVector<char, MAX_PATH> U8Result; 101 if (std::error_code EC = 102 windows::UTF16ToUTF8(U16Result.data(), U16Result.size(), U8Result)) 103 return EC; 104 105 return std::string(U8Result.begin(), U8Result.end()); 106} 107 108bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) { 109 if (!ErrMsg) 110 return true; 111 char *buffer = NULL; 112 DWORD LastError = GetLastError(); 113 DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | 114 FORMAT_MESSAGE_FROM_SYSTEM | 115 FORMAT_MESSAGE_MAX_WIDTH_MASK, 116 NULL, LastError, 0, (LPSTR)&buffer, 1, NULL); 117 if (R) 118 *ErrMsg = prefix + ": " + buffer; 119 else 120 *ErrMsg = prefix + ": Unknown error"; 121 *ErrMsg += " (0x" + llvm::utohexstr(LastError) + ")"; 122 123 LocalFree(buffer); 124 return R != 0; 125} 126 127static HANDLE RedirectIO(Optional<StringRef> Path, int fd, 128 std::string *ErrMsg) { 129 HANDLE h; 130 if (!Path) { 131 if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd), 132 GetCurrentProcess(), &h, 133 0, TRUE, DUPLICATE_SAME_ACCESS)) 134 return INVALID_HANDLE_VALUE; 135 return h; 136 } 137 138 std::string fname; 139 if (Path->empty()) 140 fname = "NUL"; 141 else 142 fname = std::string(*Path); 143 144 SECURITY_ATTRIBUTES sa; 145 sa.nLength = sizeof(sa); 146 sa.lpSecurityDescriptor = 0; 147 sa.bInheritHandle = TRUE; 148 149 SmallVector<wchar_t, 128> fnameUnicode; 150 if (Path->empty()) { 151 // Don't play long-path tricks on "NUL". 152 if (windows::UTF8ToUTF16(fname, fnameUnicode)) 153 return INVALID_HANDLE_VALUE; 154 } else { 155 if (sys::windows::widenPath(fname, fnameUnicode)) 156 return INVALID_HANDLE_VALUE; 157 } 158 h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ, 159 FILE_SHARE_READ, &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS, 160 FILE_ATTRIBUTE_NORMAL, NULL); 161 if (h == INVALID_HANDLE_VALUE) { 162 MakeErrMsg(ErrMsg, fname + ": Can't open file for " + 163 (fd ? "input" : "output")); 164 } 165 166 return h; 167} 168 169} 170 171static bool Execute(ProcessInfo &PI, StringRef Program, 172 ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env, 173 ArrayRef<Optional<StringRef>> Redirects, 174 unsigned MemoryLimit, std::string *ErrMsg, 175 BitVector *AffinityMask) { 176 if (!sys::fs::can_execute(Program)) { 177 if (ErrMsg) 178 *ErrMsg = "program not executable"; 179 return false; 180 } 181 182 // can_execute may succeed by looking at Program + ".exe". CreateProcessW 183 // will implicitly add the .exe if we provide a command line without an 184 // executable path, but since we use an explicit executable, we have to add 185 // ".exe" ourselves. 186 SmallString<64> ProgramStorage; 187 if (!sys::fs::exists(Program)) 188 Program = Twine(Program + ".exe").toStringRef(ProgramStorage); 189 190 // Windows wants a command line, not an array of args, to pass to the new 191 // process. We have to concatenate them all, while quoting the args that 192 // have embedded spaces (or are empty). 193 auto Result = flattenWindowsCommandLine(Args); 194 if (std::error_code ec = Result.getError()) { 195 SetLastError(ec.value()); 196 MakeErrMsg(ErrMsg, std::string("Unable to convert command-line to UTF-16")); 197 return false; 198 } 199 std::wstring Command = *Result; 200 201 // The pointer to the environment block for the new process. 202 std::vector<wchar_t> EnvBlock; 203 204 if (Env) { 205 // An environment block consists of a null-terminated block of 206 // null-terminated strings. Convert the array of environment variables to 207 // an environment block by concatenating them. 208 for (StringRef E : *Env) { 209 SmallVector<wchar_t, MAX_PATH> EnvString; 210 if (std::error_code ec = windows::UTF8ToUTF16(E, EnvString)) { 211 SetLastError(ec.value()); 212 MakeErrMsg(ErrMsg, "Unable to convert environment variable to UTF-16"); 213 return false; 214 } 215 216 llvm::append_range(EnvBlock, EnvString); 217 EnvBlock.push_back(0); 218 } 219 EnvBlock.push_back(0); 220 } 221 222 // Create a child process. 223 STARTUPINFOW si; 224 memset(&si, 0, sizeof(si)); 225 si.cb = sizeof(si); 226 si.hStdInput = INVALID_HANDLE_VALUE; 227 si.hStdOutput = INVALID_HANDLE_VALUE; 228 si.hStdError = INVALID_HANDLE_VALUE; 229 230 if (!Redirects.empty()) { 231 si.dwFlags = STARTF_USESTDHANDLES; 232 233 si.hStdInput = RedirectIO(Redirects[0], 0, ErrMsg); 234 if (si.hStdInput == INVALID_HANDLE_VALUE) { 235 MakeErrMsg(ErrMsg, "can't redirect stdin"); 236 return false; 237 } 238 si.hStdOutput = RedirectIO(Redirects[1], 1, ErrMsg); 239 if (si.hStdOutput == INVALID_HANDLE_VALUE) { 240 CloseHandle(si.hStdInput); 241 MakeErrMsg(ErrMsg, "can't redirect stdout"); 242 return false; 243 } 244 if (Redirects[1] && Redirects[2] && *Redirects[1] == *Redirects[2]) { 245 // If stdout and stderr should go to the same place, redirect stderr 246 // to the handle already open for stdout. 247 if (!DuplicateHandle(GetCurrentProcess(), si.hStdOutput, 248 GetCurrentProcess(), &si.hStdError, 249 0, TRUE, DUPLICATE_SAME_ACCESS)) { 250 CloseHandle(si.hStdInput); 251 CloseHandle(si.hStdOutput); 252 MakeErrMsg(ErrMsg, "can't dup stderr to stdout"); 253 return false; 254 } 255 } else { 256 // Just redirect stderr 257 si.hStdError = RedirectIO(Redirects[2], 2, ErrMsg); 258 if (si.hStdError == INVALID_HANDLE_VALUE) { 259 CloseHandle(si.hStdInput); 260 CloseHandle(si.hStdOutput); 261 MakeErrMsg(ErrMsg, "can't redirect stderr"); 262 return false; 263 } 264 } 265 } 266 267 PROCESS_INFORMATION pi; 268 memset(&pi, 0, sizeof(pi)); 269 270 fflush(stdout); 271 fflush(stderr); 272 273 SmallVector<wchar_t, MAX_PATH> ProgramUtf16; 274 if (std::error_code ec = sys::windows::widenPath(Program, ProgramUtf16)) { 275 SetLastError(ec.value()); 276 MakeErrMsg(ErrMsg, 277 std::string("Unable to convert application name to UTF-16")); 278 return false; 279 } 280 281 unsigned CreateFlags = CREATE_UNICODE_ENVIRONMENT; 282 if (AffinityMask) 283 CreateFlags |= CREATE_SUSPENDED; 284 285 std::vector<wchar_t> CommandUtf16(Command.size() + 1, 0); 286 std::copy(Command.begin(), Command.end(), CommandUtf16.begin()); 287 BOOL rc = CreateProcessW(ProgramUtf16.data(), CommandUtf16.data(), 0, 0, TRUE, 288 CreateFlags, EnvBlock.empty() ? 0 : EnvBlock.data(), 289 0, &si, &pi); 290 DWORD err = GetLastError(); 291 292 // Regardless of whether the process got created or not, we are done with 293 // the handles we created for it to inherit. 294 CloseHandle(si.hStdInput); 295 CloseHandle(si.hStdOutput); 296 CloseHandle(si.hStdError); 297 298 // Now return an error if the process didn't get created. 299 if (!rc) { 300 SetLastError(err); 301 MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") + 302 Program.str() + "'"); 303 return false; 304 } 305 306 PI.Pid = pi.dwProcessId; 307 PI.Process = pi.hProcess; 308 309 // Make sure these get closed no matter what. 310 ScopedCommonHandle hThread(pi.hThread); 311 312 // Assign the process to a job if a memory limit is defined. 313 ScopedJobHandle hJob; 314 if (MemoryLimit != 0) { 315 hJob = CreateJobObjectW(0, 0); 316 bool success = false; 317 if (hJob) { 318 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; 319 memset(&jeli, 0, sizeof(jeli)); 320 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; 321 jeli.ProcessMemoryLimit = uintptr_t(MemoryLimit) * 1048576; 322 if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, 323 &jeli, sizeof(jeli))) { 324 if (AssignProcessToJobObject(hJob, pi.hProcess)) 325 success = true; 326 } 327 } 328 if (!success) { 329 SetLastError(GetLastError()); 330 MakeErrMsg(ErrMsg, std::string("Unable to set memory limit")); 331 TerminateProcess(pi.hProcess, 1); 332 WaitForSingleObject(pi.hProcess, INFINITE); 333 return false; 334 } 335 } 336 337 // Set the affinity mask 338 if (AffinityMask) { 339 ::SetProcessAffinityMask(pi.hProcess, 340 (DWORD_PTR)AffinityMask->getData().front()); 341 ::ResumeThread(pi.hThread); 342 } 343 344 return true; 345} 346 347static bool argNeedsQuotes(StringRef Arg) { 348 if (Arg.empty()) 349 return true; 350 return StringRef::npos != Arg.find_first_of("\t \"&\'()*<>\\`^|\n"); 351} 352 353static std::string quoteSingleArg(StringRef Arg) { 354 std::string Result; 355 Result.push_back('"'); 356 357 while (!Arg.empty()) { 358 size_t FirstNonBackslash = Arg.find_first_not_of('\\'); 359 size_t BackslashCount = FirstNonBackslash; 360 if (FirstNonBackslash == StringRef::npos) { 361 // The entire remainder of the argument is backslashes. Escape all of 362 // them and just early out. 363 BackslashCount = Arg.size(); 364 Result.append(BackslashCount * 2, '\\'); 365 break; 366 } 367 368 if (Arg[FirstNonBackslash] == '\"') { 369 // This is an embedded quote. Escape all preceding backslashes, then 370 // add one additional backslash to escape the quote. 371 Result.append(BackslashCount * 2 + 1, '\\'); 372 Result.push_back('\"'); 373 } else { 374 // This is just a normal character. Don't escape any of the preceding 375 // backslashes, just append them as they are and then append the 376 // character. 377 Result.append(BackslashCount, '\\'); 378 Result.push_back(Arg[FirstNonBackslash]); 379 } 380 381 // Drop all the backslashes, plus the following character. 382 Arg = Arg.drop_front(FirstNonBackslash + 1); 383 } 384 385 Result.push_back('"'); 386 return Result; 387} 388 389namespace llvm { 390ErrorOr<std::wstring> sys::flattenWindowsCommandLine(ArrayRef<StringRef> Args) { 391 std::string Command; 392 for (StringRef Arg : Args) { 393 if (argNeedsQuotes(Arg)) 394 Command += quoteSingleArg(Arg); 395 else 396 Command += Arg; 397 398 Command.push_back(' '); 399 } 400 401 SmallVector<wchar_t, MAX_PATH> CommandUtf16; 402 if (std::error_code ec = windows::UTF8ToUTF16(Command, CommandUtf16)) 403 return ec; 404 405 return std::wstring(CommandUtf16.begin(), CommandUtf16.end()); 406} 407 408ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, 409 bool WaitUntilChildTerminates, std::string *ErrMsg, 410 Optional<ProcessStatistics> *ProcStat) { 411 assert(PI.Pid && "invalid pid to wait on, process not started?"); 412 assert((PI.Process && PI.Process != INVALID_HANDLE_VALUE) && 413 "invalid process handle to wait on, process not started?"); 414 DWORD milliSecondsToWait = 0; 415 if (WaitUntilChildTerminates) 416 milliSecondsToWait = INFINITE; 417 else if (SecondsToWait > 0) 418 milliSecondsToWait = SecondsToWait * 1000; 419 420 ProcessInfo WaitResult = PI; 421 if (ProcStat) 422 ProcStat->reset(); 423 DWORD WaitStatus = WaitForSingleObject(PI.Process, milliSecondsToWait); 424 if (WaitStatus == WAIT_TIMEOUT) { 425 if (SecondsToWait) { 426 if (!TerminateProcess(PI.Process, 1)) { 427 if (ErrMsg) 428 MakeErrMsg(ErrMsg, "Failed to terminate timed-out program"); 429 430 // -2 indicates a crash or timeout as opposed to failure to execute. 431 WaitResult.ReturnCode = -2; 432 CloseHandle(PI.Process); 433 return WaitResult; 434 } 435 WaitForSingleObject(PI.Process, INFINITE); 436 CloseHandle(PI.Process); 437 } else { 438 // Non-blocking wait. 439 return ProcessInfo(); 440 } 441 } 442 443 // Get process execution statistics. 444 if (ProcStat) { 445 FILETIME CreationTime, ExitTime, KernelTime, UserTime; 446 PROCESS_MEMORY_COUNTERS MemInfo; 447 if (GetProcessTimes(PI.Process, &CreationTime, &ExitTime, &KernelTime, 448 &UserTime) && 449 GetProcessMemoryInfo(PI.Process, &MemInfo, sizeof(MemInfo))) { 450 auto UserT = std::chrono::duration_cast<std::chrono::microseconds>( 451 toDuration(UserTime)); 452 auto KernelT = std::chrono::duration_cast<std::chrono::microseconds>( 453 toDuration(KernelTime)); 454 uint64_t PeakMemory = MemInfo.PeakPagefileUsage / 1024; 455 *ProcStat = ProcessStatistics{UserT + KernelT, UserT, PeakMemory}; 456 } 457 } 458 459 // Get its exit status. 460 DWORD status; 461 BOOL rc = GetExitCodeProcess(PI.Process, &status); 462 DWORD err = GetLastError(); 463 if (err != ERROR_INVALID_HANDLE) 464 CloseHandle(PI.Process); 465 466 if (!rc) { 467 SetLastError(err); 468 if (ErrMsg) 469 MakeErrMsg(ErrMsg, "Failed getting status for program"); 470 471 // -2 indicates a crash or timeout as opposed to failure to execute. 472 WaitResult.ReturnCode = -2; 473 return WaitResult; 474 } 475 476 if (!status) 477 return WaitResult; 478 479 // Pass 10(Warning) and 11(Error) to the callee as negative value. 480 if ((status & 0xBFFF0000U) == 0x80000000U) 481 WaitResult.ReturnCode = static_cast<int>(status); 482 else if (status & 0xFF) 483 WaitResult.ReturnCode = status & 0x7FFFFFFF; 484 else 485 WaitResult.ReturnCode = 1; 486 487 return WaitResult; 488} 489 490std::error_code sys::ChangeStdinToBinary() { 491 int result = _setmode(_fileno(stdin), _O_BINARY); 492 if (result == -1) 493 return std::error_code(errno, std::generic_category()); 494 return std::error_code(); 495} 496 497std::error_code sys::ChangeStdoutToBinary() { 498 int result = _setmode(_fileno(stdout), _O_BINARY); 499 if (result == -1) 500 return std::error_code(errno, std::generic_category()); 501 return std::error_code(); 502} 503 504std::error_code 505llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, 506 WindowsEncodingMethod Encoding) { 507 std::error_code EC; 508 llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::OF_Text); 509 if (EC) 510 return EC; 511 512 if (Encoding == WEM_UTF8) { 513 OS << Contents; 514 } else if (Encoding == WEM_CurrentCodePage) { 515 SmallVector<wchar_t, 1> ArgsUTF16; 516 SmallVector<char, 1> ArgsCurCP; 517 518 if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) 519 return EC; 520 521 if ((EC = windows::UTF16ToCurCP( 522 ArgsUTF16.data(), ArgsUTF16.size(), ArgsCurCP))) 523 return EC; 524 525 OS.write(ArgsCurCP.data(), ArgsCurCP.size()); 526 } else if (Encoding == WEM_UTF16) { 527 SmallVector<wchar_t, 1> ArgsUTF16; 528 529 if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) 530 return EC; 531 532 // Endianness guessing 533 char BOM[2]; 534 uint16_t src = UNI_UTF16_BYTE_ORDER_MARK_NATIVE; 535 memcpy(BOM, &src, 2); 536 OS.write(BOM, 2); 537 OS.write((char *)ArgsUTF16.data(), ArgsUTF16.size() << 1); 538 } else { 539 llvm_unreachable("Unknown encoding"); 540 } 541 542 if (OS.has_error()) 543 return make_error_code(errc::io_error); 544 545 return EC; 546} 547 548bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, 549 ArrayRef<StringRef> Args) { 550 // The documentation on CreateProcessW states that the size of the argument 551 // lpCommandLine must not be greater than 32767 characters, including the 552 // Unicode terminating null character. We use smaller value to reduce risk 553 // of getting invalid command line due to unaccounted factors. 554 static const size_t MaxCommandStringLength = 32000; 555 SmallVector<StringRef, 8> FullArgs; 556 FullArgs.push_back(Program); 557 FullArgs.append(Args.begin(), Args.end()); 558 auto Result = flattenWindowsCommandLine(FullArgs); 559 assert(!Result.getError()); 560 return (Result->size() + 1) <= MaxCommandStringLength; 561} 562} 563