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, std::optional<ArrayRef<StringRef>> Env, 176 ArrayRef<std::optional<StringRef>> Redirects, 177 unsigned MemoryLimit, std::string *ErrMsg, 178 BitVector *AffinityMask) { 179 if (!sys::fs::can_execute(Program)) { 180 if (ErrMsg) 181 *ErrMsg = "program not executable"; 182 return false; 183 } 184 185 // can_execute may succeed by looking at Program + ".exe". CreateProcessW 186 // will implicitly add the .exe if we provide a command line without an 187 // executable path, but since we use an explicit executable, we have to add 188 // ".exe" ourselves. 189 SmallString<64> ProgramStorage; 190 if (!sys::fs::exists(Program)) 191 Program = Twine(Program + ".exe").toStringRef(ProgramStorage); 192 193 // Windows wants a command line, not an array of args, to pass to the new 194 // process. We have to concatenate them all, while quoting the args that 195 // have embedded spaces (or are empty). 196 auto Result = flattenWindowsCommandLine(Args); 197 if (std::error_code ec = Result.getError()) { 198 SetLastError(ec.value()); 199 MakeErrMsg(ErrMsg, std::string("Unable to convert command-line to UTF-16")); 200 return false; 201 } 202 std::wstring Command = *Result; 203 204 // The pointer to the environment block for the new process. 205 std::vector<wchar_t> EnvBlock; 206 207 if (Env) { 208 // An environment block consists of a null-terminated block of 209 // null-terminated strings. Convert the array of environment variables to 210 // an environment block by concatenating them. 211 for (StringRef E : *Env) { 212 SmallVector<wchar_t, MAX_PATH> EnvString; 213 if (std::error_code ec = windows::UTF8ToUTF16(E, EnvString)) { 214 SetLastError(ec.value()); 215 MakeErrMsg(ErrMsg, "Unable to convert environment variable to UTF-16"); 216 return false; 217 } 218 219 llvm::append_range(EnvBlock, EnvString); 220 EnvBlock.push_back(0); 221 } 222 EnvBlock.push_back(0); 223 } 224 225 // Create a child process. 226 STARTUPINFOW si; 227 memset(&si, 0, sizeof(si)); 228 si.cb = sizeof(si); 229 si.hStdInput = INVALID_HANDLE_VALUE; 230 si.hStdOutput = INVALID_HANDLE_VALUE; 231 si.hStdError = INVALID_HANDLE_VALUE; 232 233 if (!Redirects.empty()) { 234 si.dwFlags = STARTF_USESTDHANDLES; 235 236 si.hStdInput = RedirectIO(Redirects[0], 0, ErrMsg); 237 if (si.hStdInput == INVALID_HANDLE_VALUE) { 238 MakeErrMsg(ErrMsg, "can't redirect stdin"); 239 return false; 240 } 241 si.hStdOutput = RedirectIO(Redirects[1], 1, ErrMsg); 242 if (si.hStdOutput == INVALID_HANDLE_VALUE) { 243 CloseHandle(si.hStdInput); 244 MakeErrMsg(ErrMsg, "can't redirect stdout"); 245 return false; 246 } 247 if (Redirects[1] && Redirects[2] && *Redirects[1] == *Redirects[2]) { 248 // If stdout and stderr should go to the same place, redirect stderr 249 // to the handle already open for stdout. 250 if (!DuplicateHandle(GetCurrentProcess(), si.hStdOutput, 251 GetCurrentProcess(), &si.hStdError, 0, TRUE, 252 DUPLICATE_SAME_ACCESS)) { 253 CloseHandle(si.hStdInput); 254 CloseHandle(si.hStdOutput); 255 MakeErrMsg(ErrMsg, "can't dup stderr to stdout"); 256 return false; 257 } 258 } else { 259 // Just redirect stderr 260 si.hStdError = RedirectIO(Redirects[2], 2, ErrMsg); 261 if (si.hStdError == INVALID_HANDLE_VALUE) { 262 CloseHandle(si.hStdInput); 263 CloseHandle(si.hStdOutput); 264 MakeErrMsg(ErrMsg, "can't redirect stderr"); 265 return false; 266 } 267 } 268 } 269 270 PROCESS_INFORMATION pi; 271 memset(&pi, 0, sizeof(pi)); 272 273 fflush(stdout); 274 fflush(stderr); 275 276 SmallVector<wchar_t, MAX_PATH> ProgramUtf16; 277 if (std::error_code ec = sys::windows::widenPath(Program, ProgramUtf16)) { 278 SetLastError(ec.value()); 279 MakeErrMsg(ErrMsg, 280 std::string("Unable to convert application name to UTF-16")); 281 return false; 282 } 283 284 unsigned CreateFlags = CREATE_UNICODE_ENVIRONMENT; 285 if (AffinityMask) 286 CreateFlags |= CREATE_SUSPENDED; 287 288 std::vector<wchar_t> CommandUtf16(Command.size() + 1, 0); 289 std::copy(Command.begin(), Command.end(), CommandUtf16.begin()); 290 BOOL rc = CreateProcessW(ProgramUtf16.data(), CommandUtf16.data(), 0, 0, TRUE, 291 CreateFlags, EnvBlock.empty() ? 0 : EnvBlock.data(), 292 0, &si, &pi); 293 DWORD err = GetLastError(); 294 295 // Regardless of whether the process got created or not, we are done with 296 // the handles we created for it to inherit. 297 CloseHandle(si.hStdInput); 298 CloseHandle(si.hStdOutput); 299 CloseHandle(si.hStdError); 300 301 // Now return an error if the process didn't get created. 302 if (!rc) { 303 SetLastError(err); 304 MakeErrMsg(ErrMsg, 305 std::string("Couldn't execute program '") + Program.str() + "'"); 306 return false; 307 } 308 309 PI.Pid = pi.dwProcessId; 310 PI.Process = pi.hProcess; 311 312 // Make sure these get closed no matter what. 313 ScopedCommonHandle hThread(pi.hThread); 314 315 // Assign the process to a job if a memory limit is defined. 316 ScopedJobHandle hJob; 317 if (MemoryLimit != 0) { 318 hJob = CreateJobObjectW(0, 0); 319 bool success = false; 320 if (hJob) { 321 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; 322 memset(&jeli, 0, sizeof(jeli)); 323 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; 324 jeli.ProcessMemoryLimit = uintptr_t(MemoryLimit) * 1048576; 325 if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, 326 &jeli, sizeof(jeli))) { 327 if (AssignProcessToJobObject(hJob, pi.hProcess)) 328 success = true; 329 } 330 } 331 if (!success) { 332 SetLastError(GetLastError()); 333 MakeErrMsg(ErrMsg, std::string("Unable to set memory limit")); 334 TerminateProcess(pi.hProcess, 1); 335 WaitForSingleObject(pi.hProcess, INFINITE); 336 return false; 337 } 338 } 339 340 // Set the affinity mask 341 if (AffinityMask) { 342 ::SetProcessAffinityMask(pi.hProcess, 343 (DWORD_PTR)AffinityMask->getData().front()); 344 ::ResumeThread(pi.hThread); 345 } 346 347 return true; 348} 349 350static bool argNeedsQuotes(StringRef Arg) { 351 if (Arg.empty()) 352 return true; 353 return StringRef::npos != Arg.find_first_of("\t \"&\'()*<>\\`^|\n"); 354} 355 356static std::string quoteSingleArg(StringRef Arg) { 357 std::string Result; 358 Result.push_back('"'); 359 360 while (!Arg.empty()) { 361 size_t FirstNonBackslash = Arg.find_first_not_of('\\'); 362 size_t BackslashCount = FirstNonBackslash; 363 if (FirstNonBackslash == StringRef::npos) { 364 // The entire remainder of the argument is backslashes. Escape all of 365 // them and just early out. 366 BackslashCount = Arg.size(); 367 Result.append(BackslashCount * 2, '\\'); 368 break; 369 } 370 371 if (Arg[FirstNonBackslash] == '\"') { 372 // This is an embedded quote. Escape all preceding backslashes, then 373 // add one additional backslash to escape the quote. 374 Result.append(BackslashCount * 2 + 1, '\\'); 375 Result.push_back('\"'); 376 } else { 377 // This is just a normal character. Don't escape any of the preceding 378 // backslashes, just append them as they are and then append the 379 // character. 380 Result.append(BackslashCount, '\\'); 381 Result.push_back(Arg[FirstNonBackslash]); 382 } 383 384 // Drop all the backslashes, plus the following character. 385 Arg = Arg.drop_front(FirstNonBackslash + 1); 386 } 387 388 Result.push_back('"'); 389 return Result; 390} 391 392namespace llvm { 393ErrorOr<std::wstring> sys::flattenWindowsCommandLine(ArrayRef<StringRef> Args) { 394 std::string Command; 395 for (StringRef Arg : Args) { 396 if (argNeedsQuotes(Arg)) 397 Command += quoteSingleArg(Arg); 398 else 399 Command += Arg; 400 401 Command.push_back(' '); 402 } 403 404 SmallVector<wchar_t, MAX_PATH> CommandUtf16; 405 if (std::error_code ec = windows::UTF8ToUTF16(Command, CommandUtf16)) 406 return ec; 407 408 return std::wstring(CommandUtf16.begin(), CommandUtf16.end()); 409} 410 411ProcessInfo sys::Wait(const ProcessInfo &PI, 412 std::optional<unsigned> SecondsToWait, 413 std::string *ErrMsg, 414 std::optional<ProcessStatistics> *ProcStat, 415 bool Polling) { 416 assert(PI.Pid && "invalid pid to wait on, process not started?"); 417 assert((PI.Process && PI.Process != INVALID_HANDLE_VALUE) && 418 "invalid process handle to wait on, process not started?"); 419 DWORD milliSecondsToWait = SecondsToWait ? *SecondsToWait * 1000 : INFINITE; 420 421 ProcessInfo WaitResult = PI; 422 if (ProcStat) 423 ProcStat->reset(); 424 DWORD WaitStatus = WaitForSingleObject(PI.Process, milliSecondsToWait); 425 if (WaitStatus == WAIT_TIMEOUT) { 426 if (!Polling && *SecondsToWait > 0) { 427 if (!TerminateProcess(PI.Process, 1)) { 428 if (ErrMsg) 429 MakeErrMsg(ErrMsg, "Failed to terminate timed-out program"); 430 431 // -2 indicates a crash or timeout as opposed to failure to execute. 432 WaitResult.ReturnCode = -2; 433 CloseHandle(PI.Process); 434 return WaitResult; 435 } 436 WaitForSingleObject(PI.Process, INFINITE); 437 CloseHandle(PI.Process); 438 } else { 439 // Non-blocking wait. 440 return ProcessInfo(); 441 } 442 } 443 444 // Get process execution statistics. 445 if (ProcStat) { 446 FILETIME CreationTime, ExitTime, KernelTime, UserTime; 447 PROCESS_MEMORY_COUNTERS MemInfo; 448 if (GetProcessTimes(PI.Process, &CreationTime, &ExitTime, &KernelTime, 449 &UserTime) && 450 GetProcessMemoryInfo(PI.Process, &MemInfo, sizeof(MemInfo))) { 451 auto UserT = std::chrono::duration_cast<std::chrono::microseconds>( 452 toDuration(UserTime)); 453 auto KernelT = std::chrono::duration_cast<std::chrono::microseconds>( 454 toDuration(KernelTime)); 455 uint64_t PeakMemory = MemInfo.PeakPagefileUsage / 1024; 456 *ProcStat = ProcessStatistics{UserT + KernelT, UserT, PeakMemory}; 457 } 458 } 459 460 // Get its exit status. 461 DWORD status; 462 BOOL rc = GetExitCodeProcess(PI.Process, &status); 463 DWORD err = GetLastError(); 464 if (err != ERROR_INVALID_HANDLE) 465 CloseHandle(PI.Process); 466 467 if (!rc) { 468 SetLastError(err); 469 if (ErrMsg) 470 MakeErrMsg(ErrMsg, "Failed getting status for program"); 471 472 // -2 indicates a crash or timeout as opposed to failure to execute. 473 WaitResult.ReturnCode = -2; 474 return WaitResult; 475 } 476 477 if (!status) 478 return WaitResult; 479 480 // Pass 10(Warning) and 11(Error) to the callee as negative value. 481 if ((status & 0xBFFF0000U) == 0x80000000U) 482 WaitResult.ReturnCode = static_cast<int>(status); 483 else if (status & 0xFF) 484 WaitResult.ReturnCode = status & 0x7FFFFFFF; 485 else 486 WaitResult.ReturnCode = 1; 487 488 return WaitResult; 489} 490 491std::error_code llvm::sys::ChangeStdinMode(sys::fs::OpenFlags Flags) { 492 if (!(Flags & fs::OF_CRLF)) 493 return ChangeStdinToBinary(); 494 return std::error_code(); 495} 496 497std::error_code llvm::sys::ChangeStdoutMode(sys::fs::OpenFlags Flags) { 498 if (!(Flags & fs::OF_CRLF)) 499 return ChangeStdoutToBinary(); 500 return std::error_code(); 501} 502 503std::error_code sys::ChangeStdinToBinary() { 504 int result = _setmode(_fileno(stdin), _O_BINARY); 505 if (result == -1) 506 return std::error_code(errno, std::generic_category()); 507 return std::error_code(); 508} 509 510std::error_code sys::ChangeStdoutToBinary() { 511 int result = _setmode(_fileno(stdout), _O_BINARY); 512 if (result == -1) 513 return std::error_code(errno, std::generic_category()); 514 return std::error_code(); 515} 516 517std::error_code 518llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, 519 WindowsEncodingMethod Encoding) { 520 std::error_code EC; 521 llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::OF_TextWithCRLF); 522 if (EC) 523 return EC; 524 525 if (Encoding == WEM_UTF8) { 526 OS << Contents; 527 } else if (Encoding == WEM_CurrentCodePage) { 528 SmallVector<wchar_t, 1> ArgsUTF16; 529 SmallVector<char, 1> ArgsCurCP; 530 531 if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) 532 return EC; 533 534 if ((EC = windows::UTF16ToCurCP(ArgsUTF16.data(), ArgsUTF16.size(), 535 ArgsCurCP))) 536 return EC; 537 538 OS.write(ArgsCurCP.data(), ArgsCurCP.size()); 539 } else if (Encoding == WEM_UTF16) { 540 SmallVector<wchar_t, 1> ArgsUTF16; 541 542 if ((EC = windows::UTF8ToUTF16(Contents, ArgsUTF16))) 543 return EC; 544 545 // Endianness guessing 546 char BOM[2]; 547 uint16_t src = UNI_UTF16_BYTE_ORDER_MARK_NATIVE; 548 memcpy(BOM, &src, 2); 549 OS.write(BOM, 2); 550 OS.write((char *)ArgsUTF16.data(), ArgsUTF16.size() << 1); 551 } else { 552 llvm_unreachable("Unknown encoding"); 553 } 554 555 if (OS.has_error()) 556 return make_error_code(errc::io_error); 557 558 return EC; 559} 560 561bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, 562 ArrayRef<StringRef> Args) { 563 // The documentation on CreateProcessW states that the size of the argument 564 // lpCommandLine must not be greater than 32767 characters, including the 565 // Unicode terminating null character. We use smaller value to reduce risk 566 // of getting invalid command line due to unaccounted factors. 567 static const size_t MaxCommandStringLength = 32000; 568 SmallVector<StringRef, 8> FullArgs; 569 FullArgs.push_back(Program); 570 FullArgs.append(Args.begin(), Args.end()); 571 auto Result = flattenWindowsCommandLine(FullArgs); 572 assert(!Result.getError()); 573 return (Result->size() + 1) <= MaxCommandStringLength; 574} 575} // namespace llvm 576