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/ScopeExit.h"
14#include "llvm/ADT/SmallVector.h"
15#include "llvm/Support/ConvertUTF.h"
16#include "llvm/Support/Program.h"
17
18#include <string>
19#include <vector>
20
21using namespace lldb;
22using namespace lldb_private;
23
24static void CreateEnvironmentBuffer(const Environment &env,
25 std::vector<char> &buffer) {
26 // The buffer is a list of null-terminated UTF-16 strings, followed by an
27 // extra L'\0' (two bytes of 0). An empty environment must have one
28 // empty string, followed by an extra L'\0'.
29 for (const auto &KV : env) {
30 std::wstring warg;
31 if (llvm::ConvertUTF8toWide(Source: Environment::compose(KeyValue: KV), Result&: warg)) {
32 buffer.insert(
33 position: buffer.end(), first: reinterpret_cast<const char *>(warg.c_str()),
34 last: reinterpret_cast<const 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(x: 0);
39 buffer.push_back(x: 0);
40 // Insert extra two bytes, just in case the environment was empty.
41 buffer.push_back(x: 0);
42 buffer.push_back(x: 0);
43}
44
45static bool GetFlattenedWindowsCommandString(Args args, std::wstring &command) {
46 if (args.empty())
47 return false;
48
49 std::vector<llvm::StringRef> args_ref;
50 for (auto &entry : args.entries())
51 args_ref.push_back(x: entry.ref());
52
53 llvm::ErrorOr<std::wstring> result =
54 llvm::sys::flattenWindowsCommandLine(args_ref);
55 if (result.getError())
56 return false;
57
58 command = *result;
59 return true;
60}
61
62HostProcess
63ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
64 Status &error) {
65 error.Clear();
66
67 std::string executable;
68 std::vector<char> environment;
69 STARTUPINFOEX startupinfoex = {};
70 STARTUPINFO &startupinfo = startupinfoex.StartupInfo;
71 PROCESS_INFORMATION pi = {};
72
73 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
74 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
75 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
76 auto close_handles = llvm::make_scope_exit(F: [&] {
77 if (stdin_handle)
78 ::CloseHandle(stdin_handle);
79 if (stdout_handle)
80 ::CloseHandle(stdout_handle);
81 if (stderr_handle)
82 ::CloseHandle(stderr_handle);
83 });
84
85 startupinfo.cb = sizeof(startupinfoex);
86 startupinfo.dwFlags |= STARTF_USESTDHANDLES;
87 startupinfo.hStdError =
88 stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
89 startupinfo.hStdInput =
90 stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
91 startupinfo.hStdOutput =
92 stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
93
94 std::vector<HANDLE> inherited_handles;
95 if (startupinfo.hStdError)
96 inherited_handles.push_back(startupinfo.hStdError);
97 if (startupinfo.hStdInput)
98 inherited_handles.push_back(startupinfo.hStdInput);
99 if (startupinfo.hStdOutput)
100 inherited_handles.push_back(startupinfo.hStdOutput);
101
102 SIZE_T attributelist_size = 0;
103 InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr,
104 /*dwAttributeCount=*/1, /*dwFlags=*/0,
105 &attributelist_size);
106
107 startupinfoex.lpAttributeList =
108 static_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attributelist_size));
109 auto free_attributelist =
110 llvm::make_scope_exit(F: [&] { free(startupinfoex.lpAttributeList); });
111 if (!InitializeProcThreadAttributeList(startupinfoex.lpAttributeList,
112 /*dwAttributeCount=*/1, /*dwFlags=*/0,
113 &attributelist_size)) {
114 error = Status(::GetLastError(), eErrorTypeWin32);
115 return HostProcess();
116 }
117 auto delete_attributelist = llvm::make_scope_exit(
118 F: [&] { DeleteProcThreadAttributeList(startupinfoex.lpAttributeList); });
119 for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) {
120 const FileAction *act = launch_info.GetFileActionAtIndex(idx: i);
121 if (act->GetAction() == FileAction::eFileActionDuplicate &&
122 act->GetFD() == act->GetActionArgument())
123 inherited_handles.push_back(reinterpret_cast<HANDLE>(act->GetFD()));
124 }
125 if (!inherited_handles.empty()) {
126 if (!UpdateProcThreadAttribute(
127 startupinfoex.lpAttributeList, /*dwFlags=*/0,
128 PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(),
129 inherited_handles.size() * sizeof(HANDLE),
130 /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) {
131 error = Status(::GetLastError(), eErrorTypeWin32);
132 return HostProcess();
133 }
134 }
135
136 const char *hide_console_var =
137 getenv(name: "LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
138 if (hide_console_var &&
139 llvm::StringRef(hide_console_var).equals_insensitive(RHS: "true")) {
140 startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
141 startupinfo.wShowWindow = SW_HIDE;
142 }
143
144 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT |
145 EXTENDED_STARTUPINFO_PRESENT;
146 if (launch_info.GetFlags().Test(bit: eLaunchFlagDebug))
147 flags |= DEBUG_ONLY_THIS_PROCESS;
148
149 if (launch_info.GetFlags().Test(bit: eLaunchFlagDisableSTDIO))
150 flags &= ~CREATE_NEW_CONSOLE;
151
152 LPVOID env_block = nullptr;
153 ::CreateEnvironmentBuffer(env: launch_info.GetEnvironment(), buffer&: environment);
154 env_block = environment.data();
155
156 executable = launch_info.GetExecutableFile().GetPath();
157 std::wstring wcommandLine;
158 GetFlattenedWindowsCommandString(args: launch_info.GetArguments(), command&: wcommandLine);
159
160 std::wstring wexecutable, wworkingDirectory;
161 llvm::ConvertUTF8toWide(Source: executable, Result&: wexecutable);
162 llvm::ConvertUTF8toWide(Source: launch_info.GetWorkingDirectory().GetPath(),
163 Result&: wworkingDirectory);
164 // If the command line is empty, it's best to pass a null pointer to tell
165 // CreateProcessW to use the executable name as the command line. If the
166 // command line is not empty, its contents may be modified by CreateProcessW.
167 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
168
169 BOOL result = ::CreateProcessW(
170 wexecutable.c_str(), pwcommandLine, NULL, NULL,
171 /*bInheritHandles=*/!inherited_handles.empty(), flags, env_block,
172 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
173 reinterpret_cast<STARTUPINFO *>(&startupinfoex), &pi);
174
175 if (!result) {
176 // Call GetLastError before we make any other system calls.
177 error = Status(::GetLastError(), eErrorTypeWin32);
178 // Note that error 50 ("The request is not supported") will occur if you
179 // try debug a 64-bit inferior from a 32-bit LLDB.
180 }
181
182 if (result) {
183 // Do not call CloseHandle on pi.hProcess, since we want to pass that back
184 // through the HostProcess.
185 ::CloseHandle(pi.hThread);
186 }
187
188 if (!result)
189 return HostProcess();
190
191 return HostProcess(pi.hProcess);
192}
193
194HANDLE
195ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,
196 int fd) {
197 const FileAction *action = launch_info.GetFileActionForFD(fd);
198 if (action == nullptr)
199 return NULL;
200 SECURITY_ATTRIBUTES secattr = {};
201 secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
202 secattr.bInheritHandle = TRUE;
203
204 llvm::StringRef path = action->GetPath();
205 DWORD access = 0;
206 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
207 DWORD create = 0;
208 DWORD flags = 0;
209 if (fd == STDIN_FILENO) {
210 access = GENERIC_READ;
211 create = OPEN_EXISTING;
212 flags = FILE_ATTRIBUTE_READONLY;
213 }
214 if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
215 access = GENERIC_WRITE;
216 create = CREATE_ALWAYS;
217 if (fd == STDERR_FILENO)
218 flags = FILE_FLAG_WRITE_THROUGH;
219 }
220
221 std::wstring wpath;
222 llvm::ConvertUTF8toWide(Source: path, Result&: wpath);
223 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
224 flags, NULL);
225 return (result == INVALID_HANDLE_VALUE) ? NULL : result;
226}
227

source code of lldb/source/Host/windows/ProcessLauncherWindows.cpp