1 //===-- ProcessLauncherWindows.cpp ------------------------------*- 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 #include "lldb/Host/windows/ProcessLauncherWindows.h" 10 #include "lldb/Host/HostProcess.h" 11 #include "lldb/Host/ProcessLaunchInfo.h" 12 13 #include "llvm/ADT/SmallVector.h" 14 #include "llvm/Support/ConvertUTF.h" 15 #include "llvm/Support/Program.h" 16 17 #include <string> 18 #include <vector> 19 20 using namespace lldb; 21 using namespace lldb_private; 22 23 namespace { 24 void CreateEnvironmentBuffer(const Environment &env, 25 std::vector<char> &buffer) { 26 if (env.size() == 0) 27 return; 28 29 // Environment buffer is a null terminated list of null terminated strings 30 for (const auto &KV : env) { 31 std::wstring warg; 32 if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) { 33 buffer.insert(buffer.end(), (char *)warg.c_str(), 34 (char *)(warg.c_str() + warg.size() + 1)); 35 } 36 } 37 // One null wchar_t (to end the block) is two null bytes 38 buffer.push_back(0); 39 buffer.push_back(0); 40 } 41 42 bool GetFlattenedWindowsCommandString(Args args, std::string &command) { 43 if (args.empty()) 44 return false; 45 46 std::vector<llvm::StringRef> args_ref; 47 for (auto &entry : args.entries()) 48 args_ref.push_back(entry.ref); 49 50 command = llvm::sys::flattenWindowsCommandLine(args_ref); 51 return true; 52 } 53 } // namespace 54 55 HostProcess 56 ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, 57 Status &error) { 58 error.Clear(); 59 60 std::string executable; 61 std::string commandLine; 62 std::vector<char> environment; 63 STARTUPINFO startupinfo = {}; 64 PROCESS_INFORMATION pi = {}; 65 66 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); 67 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); 68 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO); 69 70 startupinfo.cb = sizeof(startupinfo); 71 startupinfo.dwFlags |= STARTF_USESTDHANDLES; 72 startupinfo.hStdError = 73 stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); 74 startupinfo.hStdInput = 75 stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE); 76 startupinfo.hStdOutput = 77 stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); 78 79 const char *hide_console_var = 80 getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"); 81 if (hide_console_var && 82 llvm::StringRef(hide_console_var).equals_lower("true")) { 83 startupinfo.dwFlags |= STARTF_USESHOWWINDOW; 84 startupinfo.wShowWindow = SW_HIDE; 85 } 86 87 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; 88 if (launch_info.GetFlags().Test(eLaunchFlagDebug)) 89 flags |= DEBUG_ONLY_THIS_PROCESS; 90 91 if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO)) 92 flags &= ~CREATE_NEW_CONSOLE; 93 94 LPVOID env_block = nullptr; 95 ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment); 96 if (!environment.empty()) 97 env_block = environment.data(); 98 99 executable = launch_info.GetExecutableFile().GetPath(); 100 GetFlattenedWindowsCommandString(launch_info.GetArguments(), commandLine); 101 102 std::wstring wexecutable, wcommandLine, wworkingDirectory; 103 llvm::ConvertUTF8toWide(executable, wexecutable); 104 llvm::ConvertUTF8toWide(commandLine, wcommandLine); 105 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(), 106 wworkingDirectory); 107 108 wcommandLine.resize(PATH_MAX); // Needs to be over-allocated because 109 // CreateProcessW can modify it 110 BOOL result = ::CreateProcessW( 111 wexecutable.c_str(), &wcommandLine[0], NULL, NULL, TRUE, flags, env_block, 112 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), 113 &startupinfo, &pi); 114 115 if (!result) { 116 // Call GetLastError before we make any other system calls. 117 error.SetError(::GetLastError(), eErrorTypeWin32); 118 } 119 120 if (result) { 121 // Do not call CloseHandle on pi.hProcess, since we want to pass that back 122 // through the HostProcess. 123 ::CloseHandle(pi.hThread); 124 } 125 126 if (stdin_handle) 127 ::CloseHandle(stdin_handle); 128 if (stdout_handle) 129 ::CloseHandle(stdout_handle); 130 if (stderr_handle) 131 ::CloseHandle(stderr_handle); 132 133 if (!result) 134 return HostProcess(); 135 136 return HostProcess(pi.hProcess); 137 } 138 139 HANDLE 140 ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, 141 int fd) { 142 const FileAction *action = launch_info.GetFileActionForFD(fd); 143 if (action == nullptr) 144 return NULL; 145 SECURITY_ATTRIBUTES secattr = {}; 146 secattr.nLength = sizeof(SECURITY_ATTRIBUTES); 147 secattr.bInheritHandle = TRUE; 148 149 llvm::StringRef path = action->GetPath(); 150 DWORD access = 0; 151 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 152 DWORD create = 0; 153 DWORD flags = 0; 154 if (fd == STDIN_FILENO) { 155 access = GENERIC_READ; 156 create = OPEN_EXISTING; 157 flags = FILE_ATTRIBUTE_READONLY; 158 } 159 if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { 160 access = GENERIC_WRITE; 161 create = CREATE_ALWAYS; 162 if (fd == STDERR_FILENO) 163 flags = FILE_FLAG_WRITE_THROUGH; 164 } 165 166 std::wstring wpath; 167 llvm::ConvertUTF8toWide(path, wpath); 168 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create, 169 flags, NULL); 170 return (result == INVALID_HANDLE_VALUE) ? NULL : result; 171 } 172