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<char, MAX_PATH> U8Result; 71 for (StringRef Ext : PathExts) { 72 SmallVector<wchar_t, MAX_PATH> U16Result; 73 DWORD Len = MAX_PATH; 74 do { 75 U16Result.reserve(Len); 76 // Lets attach the extension manually. That is needed for files 77 // with a point in name like aaa.bbb. SearchPathW will not add extension 78 // from its argument to such files because it thinks they already had one. 79 SmallVector<wchar_t, MAX_PATH> U16NameExt; 80 if (std::error_code EC = 81 windows::UTF8ToUTF16(Twine(Name + Ext).str(), U16NameExt)) 82 return EC; 83 84 Len = ::SearchPathW(Path, c_str(U16NameExt), nullptr, 85 U16Result.capacity(), U16Result.data(), nullptr); 86 } while (Len > U16Result.capacity()); 87 88 if (Len == 0) 89 continue; 90 91 U16Result.set_size(Len); 92 93 if (std::error_code EC = 94 windows::UTF16ToUTF8(U16Result.data(), U16Result.size(), U8Result)) 95 return EC; 96 97 if (sys::fs::can_execute(U8Result)) 98 break; // Found it. 99 100 U8Result.clear(); 101 } 102 103 if (U8Result.empty()) 104 return mapWindowsError(::GetLastError()); 105 106 return std::string(U8Result.begin(), U8Result.end()); 107} 108 109bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) { 110 if (!ErrMsg) 111 return true; 112 char *buffer = NULL; 113 DWORD LastError = GetLastError(); 114 DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | 115 FORMAT_MESSAGE_FROM_SYSTEM | 116 FORMAT_MESSAGE_MAX_WIDTH_MASK, 117 NULL, LastError, 0, (LPSTR)&buffer, 1, NULL); 118 if (R) 119 *ErrMsg = prefix + ": " + buffer; 120 else 121 *ErrMsg = prefix + ": Unknown error"; 122 *ErrMsg += " (0x" + llvm::utohexstr(LastError) + ")"; 123 124 LocalFree(buffer); 125 return R != 0; 126} 127 128static HANDLE RedirectIO(Optional<StringRef> Path, int fd, 129 std::string *ErrMsg) { 130 HANDLE h; 131 if (!Path) { 132 if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)_get_osfhandle(fd), 133 GetCurrentProcess(), &h, 134 0, TRUE, DUPLICATE_SAME_ACCESS)) 135 return INVALID_HANDLE_VALUE; 136 return h; 137 } 138 139 std::string fname; 140 if (Path->empty()) 141 fname = "NUL"; 142 else 143 fname = std::string(*Path); 144 145 SECURITY_ATTRIBUTES sa; 146 sa.nLength = sizeof(sa); 147 sa.lpSecurityDescriptor = 0; 148 sa.bInheritHandle = TRUE; 149 150 SmallVector<wchar_t, 128> fnameUnicode; 151 if (Path->empty()) { 152 // Don't play long-path tricks on "NUL". 153 if (windows::UTF8ToUTF16(fname, fnameUnicode)) 154 return INVALID_HANDLE_VALUE; 155 } else { 156 if (sys::windows::widenPath(fname, fnameUnicode)) 157 return INVALID_HANDLE_VALUE; 158 } 159 h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ, 160 FILE_SHARE_READ, &sa, fd == 0 ? OPEN_EXISTING : CREATE_ALWAYS, 161 FILE_ATTRIBUTE_NORMAL, NULL); 162 if (h == INVALID_HANDLE_VALUE) { 163 MakeErrMsg(ErrMsg, fname + ": Can't open file for " + 164 (fd ? "input" : "output")); 165 } 166 167 return h; 168} 169 170} 171 172static bool Execute(ProcessInfo &PI, StringRef Program, 173 ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env, 174 ArrayRef<Optional<StringRef>> Redirects, 175 unsigned MemoryLimit, std::string *ErrMsg, 176 BitVector *AffinityMask) { 177 if (!sys::fs::can_execute(Program)) { 178 if (ErrMsg) 179 *ErrMsg = "program not executable"; 180 return false; 181 } 182 183 // can_execute may succeed by looking at Program + ".exe". CreateProcessW 184 // will implicitly add the .exe if we provide a command line without an 185 // executable path, but since we use an explicit executable, we have to add 186 // ".exe" ourselves. 187 SmallString<64> ProgramStorage; 188 if (!sys::fs::exists(Program)) 189 Program = Twine(Program + ".exe").toStringRef(ProgramStorage); 190 191 // Windows wants a command line, not an array of args, to pass to the new 192 // process. We have to concatenate them all, while quoting the args that 193 // have embedded spaces (or are empty). 194 auto Result = flattenWindowsCommandLine(Args); 195 if (std::error_code ec = Result.getError()) { 196 SetLastError(ec.value()); 197 MakeErrMsg(ErrMsg, std::string("Unable to convert command-line to UTF-16")); 198 return false; 199 } 200 std::wstring Command = *Result; 201 202 // The pointer to the environment block for the new process. 203 std::vector<wchar_t> EnvBlock; 204 205 if (Env) { 206 // An environment block consists of a null-terminated block of 207 // null-terminated strings. Convert the array of environment variables to 208 // an environment block by concatenating them. 209 for (StringRef E : *Env) { 210 SmallVector<wchar_t, MAX_PATH> EnvString; 211 if (std::error_code ec = windows::UTF8ToUTF16(E, EnvString)) { 212 SetLastError(ec.value()); 213 MakeErrMsg(ErrMsg, "Unable to convert environment variable to UTF-16"); 214 return false; 215 } 216 217 llvm::append_range(EnvBlock, EnvString); 218 EnvBlock.push_back(0); 219 } 220 EnvBlock.push_back(0); 221 } 222 223 // Create a child process. 224 STARTUPINFOW si; 225 memset(&si, 0, sizeof(si)); 226 si.cb = sizeof(si); 227 si.hStdInput = INVALID_HANDLE_VALUE; 228 si.hStdOutput = INVALID_HANDLE_VALUE; 229 si.hStdError = INVALID_HANDLE_VALUE; 230 231 if (!Redirects.empty()) { 232 si.dwFlags = STARTF_USESTDHANDLES; 233 234 si.hStdInput = RedirectIO(Redirects[0], 0, ErrMsg); 235 if (si.hStdInput == INVALID_HANDLE_VALUE) { 236 MakeErrMsg(ErrMsg, "can't redirect stdin"); 237 return false; 238 } 239 si.hStdOutput = RedirectIO(Redirects[1], 1, ErrMsg); 240 if (si.hStdOutput == INVALID_HANDLE_VALUE) { 241 CloseHandle(si.hStdInput); 242 MakeErrMsg(ErrMsg, "can't redirect stdout"); 243 return false; 244 } 245 if (Redirects[1] && Redirects[2] && *Redirects[1] == *Redirects[2]) { 246 // If stdout and stderr should go to the same place, redirect stderr 247 // to the handle already open for stdout. 248 if (!DuplicateHandle(GetCurrentProcess(), si.hStdOutput, 249 GetCurrentProcess(), &si.hStdError, 250 0, TRUE, DUPLICATE_SAME_ACCESS)) { 251 CloseHandle(si.hStdInput); 252 CloseHandle(si.hStdOutput); 253 MakeErrMsg(ErrMsg, "can't dup stderr to stdout"); 254 return false; 255 } 256 } else { 257 // Just redirect stderr 258 si.hStdError = RedirectIO(Redirects[2], 2, ErrMsg); 259 if (si.hStdError == INVALID_HANDLE_VALUE) { 260 CloseHandle(si.hStdInput); 261 CloseHandle(si.hStdOutput); 262 MakeErrMsg(ErrMsg, "can't redirect stderr"); 263 return false; 264 } 265 } 266 } 267 268 PROCESS_INFORMATION pi; 269 memset(&pi, 0, sizeof(pi)); 270 271 fflush(stdout); 272 fflush(stderr); 273 274 SmallVector<wchar_t, MAX_PATH> ProgramUtf16; 275 if (std::error_code ec = sys::windows::widenPath(Program, ProgramUtf16)) { 276 SetLastError(ec.value()); 277 MakeErrMsg(ErrMsg, 278 std::string("Unable to convert application name to UTF-16")); 279 return false; 280 } 281 282 unsigned CreateFlags = CREATE_UNICODE_ENVIRONMENT; 283 if (AffinityMask) 284 CreateFlags |= CREATE_SUSPENDED; 285 286 std::vector<wchar_t> CommandUtf16(Command.size() + 1, 0); 287 std::copy(Command.begin(), Command.end(), CommandUtf16.begin()); 288 BOOL rc = CreateProcessW(ProgramUtf16.data(), CommandUtf16.data(), 0, 0, TRUE, 289 CreateFlags, EnvBlock.empty() ? 0 : EnvBlock.data(), 290 0, &si, &pi); 291 DWORD err = GetLastError(); 292 293 // Regardless of whether the process got created or not, we are done with 294 // the handles we created for it to inherit. 295 CloseHandle(si.hStdInput); 296 CloseHandle(si.hStdOutput); 297 CloseHandle(si.hStdError); 298 299 // Now return an error if the process didn't get created. 300 if (!rc) { 301 SetLastError(err); 302 MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") + 303 Program.str() + "'"); 304 return false; 305 } 306 307 PI.Pid = pi.dwProcessId; 308 PI.Process = pi.hProcess; 309 310 // Make sure these get closed no matter what. 311 ScopedCommonHandle hThread(pi.hThread); 312 313 // Assign the process to a job if a memory limit is defined. 314 ScopedJobHandle hJob; 315 if (MemoryLimit != 0) { 316 hJob = CreateJobObjectW(0, 0); 317 bool success = false; 318 if (hJob) { 319 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli; 320 memset(&jeli, 0, sizeof(jeli)); 321 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; 322 jeli.ProcessMemoryLimit = uintptr_t(MemoryLimit) * 1048576; 323 if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, 324 &jeli, sizeof(jeli))) { 325 if (AssignProcessToJobObject(hJob, pi.hProcess)) 326 success = true; 327 } 328 } 329 if (!success) { 330 SetLastError(GetLastError()); 331 MakeErrMsg(ErrMsg, std::string("Unable to set memory limit")); 332 TerminateProcess(pi.hProcess, 1); 333 WaitForSingleObject(pi.hProcess, INFINITE); 334 return false; 335 } 336 } 337 338 // Set the affinity mask 339 if (AffinityMask) { 340 ::SetProcessAffinityMask(pi.hProcess, 341 (DWORD_PTR)AffinityMask->getData().front()); 342 ::ResumeThread(pi.hThread); 343 } 344 345 return true; 346} 347 348static bool argNeedsQuotes(StringRef Arg) { 349 if (Arg.empty()) 350 return true; 351 return StringRef::npos != Arg.find_first_of("\t \"&\'()*<>\\`^|\n"); 352} 353 354static std::string quoteSingleArg(StringRef Arg) { 355 std::string Result; 356 Result.push_back('"'); 357 358 while (!Arg.empty()) { 359 size_t FirstNonBackslash = Arg.find_first_not_of('\\'); 360 size_t BackslashCount = FirstNonBackslash; 361 if (FirstNonBackslash == StringRef::npos) { 362 // The entire remainder of the argument is backslashes. Escape all of 363 // them and just early out. 364 BackslashCount = Arg.size(); 365 Result.append(BackslashCount * 2, '\\'); 366 break; 367 } 368 369 if (Arg[FirstNonBackslash] == '\"') { 370 // This is an embedded quote. Escape all preceding backslashes, then 371 // add one additional backslash to escape the quote. 372 Result.append(BackslashCount * 2 + 1, '\\'); 373 Result.push_back('\"'); 374 } else { 375 // This is just a normal character. Don't escape any of the preceding 376 // backslashes, just append them as they are and then append the 377 // character. 378 Result.append(BackslashCount, '\\'); 379 Result.push_back(Arg[FirstNonBackslash]); 380 } 381 382 // Drop all the backslashes, plus the following character. 383 Arg = Arg.drop_front(FirstNonBackslash + 1); 384 } 385 386 Result.push_back('"'); 387 return Result; 388} 389 390namespace llvm { 391ErrorOr<std::wstring> sys::flattenWindowsCommandLine(ArrayRef<StringRef> Args) { 392 std::string Command; 393 for (StringRef Arg : Args) { 394 if (argNeedsQuotes(Arg)) 395 Command += quoteSingleArg(Arg); 396 else 397 Command += Arg; 398 399 Command.push_back(' '); 400 } 401 402 SmallVector<wchar_t, MAX_PATH> CommandUtf16; 403 if (std::error_code ec = windows::UTF8ToUTF16(Command, CommandUtf16)) 404 return ec; 405 406 return std::wstring(CommandUtf16.begin(), CommandUtf16.end()); 407} 408 409ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, 410 bool WaitUntilChildTerminates, std::string *ErrMsg, 411 Optional<ProcessStatistics> *ProcStat) { 412 assert(PI.Pid && "invalid pid to wait on, process not started?"); 413 assert((PI.Process && PI.Process != INVALID_HANDLE_VALUE) && 414 "invalid process handle to wait on, process not started?"); 415 DWORD milliSecondsToWait = 0; 416 if (WaitUntilChildTerminates) 417 milliSecondsToWait = INFINITE; 418 else if (SecondsToWait > 0) 419 milliSecondsToWait = SecondsToWait * 1000; 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 (SecondsToWait) { 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( 535 ArgsUTF16.data(), ArgsUTF16.size(), 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} 576