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