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