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