1 //===-- ProcessLauncherWindows.cpp ----------------------------------------===// 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 static void CreateEnvironmentBuffer(const Environment &env, 24 std::vector<char> &buffer) { 25 // The buffer is a list of null-terminated UTF-16 strings, followed by an 26 // extra L'\0' (two bytes of 0). An empty environment must have one 27 // empty string, followed by an extra L'\0'. 28 for (const auto &KV : env) { 29 std::wstring warg; 30 if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) { 31 buffer.insert( 32 buffer.end(), reinterpret_cast<const char *>(warg.c_str()), 33 reinterpret_cast<const char *>(warg.c_str() + warg.size() + 1)); 34 } 35 } 36 // One null wchar_t (to end the block) is two null bytes 37 buffer.push_back(0); 38 buffer.push_back(0); 39 // Insert extra two bytes, just in case the environment was empty. 40 buffer.push_back(0); 41 buffer.push_back(0); 42 } 43 44 static bool GetFlattenedWindowsCommandString(Args args, std::wstring &command) { 45 if (args.empty()) 46 return false; 47 48 std::vector<llvm::StringRef> args_ref; 49 for (auto &entry : args.entries()) 50 args_ref.push_back(entry.ref()); 51 52 llvm::ErrorOr<std::wstring> result = 53 llvm::sys::flattenWindowsCommandLine(args_ref); 54 if (result.getError()) 55 return false; 56 57 command = *result; 58 return true; 59 } 60 61 HostProcess 62 ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, 63 Status &error) { 64 error.Clear(); 65 66 std::string executable; 67 std::vector<char> environment; 68 STARTUPINFO startupinfo = {}; 69 PROCESS_INFORMATION pi = {}; 70 71 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); 72 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); 73 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO); 74 75 startupinfo.cb = sizeof(startupinfo); 76 startupinfo.dwFlags |= STARTF_USESTDHANDLES; 77 startupinfo.hStdError = 78 stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); 79 startupinfo.hStdInput = 80 stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE); 81 startupinfo.hStdOutput = 82 stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); 83 84 const char *hide_console_var = 85 getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"); 86 if (hide_console_var && 87 llvm::StringRef(hide_console_var).equals_insensitive("true")) { 88 startupinfo.dwFlags |= STARTF_USESHOWWINDOW; 89 startupinfo.wShowWindow = SW_HIDE; 90 } 91 92 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; 93 if (launch_info.GetFlags().Test(eLaunchFlagDebug)) 94 flags |= DEBUG_ONLY_THIS_PROCESS; 95 96 if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO)) 97 flags &= ~CREATE_NEW_CONSOLE; 98 99 LPVOID env_block = nullptr; 100 ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment); 101 env_block = environment.data(); 102 103 executable = launch_info.GetExecutableFile().GetPath(); 104 std::wstring wcommandLine; 105 GetFlattenedWindowsCommandString(launch_info.GetArguments(), wcommandLine); 106 107 std::wstring wexecutable, wworkingDirectory; 108 llvm::ConvertUTF8toWide(executable, wexecutable); 109 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(), 110 wworkingDirectory); 111 // If the command line is empty, it's best to pass a null pointer to tell 112 // CreateProcessW to use the executable name as the command line. If the 113 // command line is not empty, its contents may be modified by CreateProcessW. 114 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0]; 115 116 BOOL result = ::CreateProcessW( 117 wexecutable.c_str(), pwcommandLine, NULL, NULL, TRUE, flags, env_block, 118 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), 119 &startupinfo, &pi); 120 121 if (!result) { 122 // Call GetLastError before we make any other system calls. 123 error.SetError(::GetLastError(), eErrorTypeWin32); 124 // Note that error 50 ("The request is not supported") will occur if you 125 // try debug a 64-bit inferior from a 32-bit LLDB. 126 } 127 128 if (result) { 129 // Do not call CloseHandle on pi.hProcess, since we want to pass that back 130 // through the HostProcess. 131 ::CloseHandle(pi.hThread); 132 } 133 134 if (stdin_handle) 135 ::CloseHandle(stdin_handle); 136 if (stdout_handle) 137 ::CloseHandle(stdout_handle); 138 if (stderr_handle) 139 ::CloseHandle(stderr_handle); 140 141 if (!result) 142 return HostProcess(); 143 144 return HostProcess(pi.hProcess); 145 } 146 147 HANDLE 148 ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, 149 int fd) { 150 const FileAction *action = launch_info.GetFileActionForFD(fd); 151 if (action == nullptr) 152 return NULL; 153 SECURITY_ATTRIBUTES secattr = {}; 154 secattr.nLength = sizeof(SECURITY_ATTRIBUTES); 155 secattr.bInheritHandle = TRUE; 156 157 llvm::StringRef path = action->GetPath(); 158 DWORD access = 0; 159 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 160 DWORD create = 0; 161 DWORD flags = 0; 162 if (fd == STDIN_FILENO) { 163 access = GENERIC_READ; 164 create = OPEN_EXISTING; 165 flags = FILE_ATTRIBUTE_READONLY; 166 } 167 if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { 168 access = GENERIC_WRITE; 169 create = CREATE_ALWAYS; 170 if (fd == STDERR_FILENO) 171 flags = FILE_FLAG_WRITE_THROUGH; 172 } 173 174 std::wstring wpath; 175 llvm::ConvertUTF8toWide(path, wpath); 176 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create, 177 flags, NULL); 178 return (result == INVALID_HANDLE_VALUE) ? NULL : result; 179 } 180