LLDB mainline
ProcessLauncherWindows.cpp
Go to the documentation of this file.
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
14
15#include "llvm/ADT/SmallVector.h"
16#include "llvm/Support/ConvertUTF.h"
17#include "llvm/Support/Program.h"
18#include "llvm/Support/WindowsError.h"
19
20#include <string>
21#include <vector>
22
23using namespace lldb;
24using namespace lldb_private;
25
26/// Create a UTF-16 environment block to use with CreateProcessW.
27///
28/// The buffer is a sequence of null-terminated UTF-16 strings, followed by an
29/// extra L'\0' (two bytes of 0). An empty environment must have one
30/// empty string, followed by an extra L'\0'.
31///
32/// The keys are sorted to comply with the CreateProcess API calling convention.
33///
34/// Ensure that the resulting buffer is used in conjunction with
35/// CreateProcessW and be sure that dwCreationFlags includes
36/// CREATE_UNICODE_ENVIRONMENT.
37///
38/// \param env The Environment object to convert.
39/// \returns The sorted sequence of environment variables and their values,
40/// separated by null terminators. The vector is guaranteed to never be empty.
41static std::vector<wchar_t> CreateEnvironmentBufferW(const Environment &env) {
42 std::vector<std::wstring> env_entries;
43 for (const auto &KV : env) {
44 std::wstring wentry;
45 if (llvm::ConvertUTF8toWide(Environment::compose(KV), wentry))
46 env_entries.push_back(std::move(wentry));
47 }
48 std::sort(env_entries.begin(), env_entries.end(),
49 [](const std::wstring &a, const std::wstring &b) {
50 return _wcsicmp(a.c_str(), b.c_str()) < 0;
51 });
52
53 std::vector<wchar_t> buffer;
54 for (const auto &env_entry : env_entries) {
55 buffer.insert(buffer.end(), env_entry.begin(), env_entry.end());
56 buffer.push_back(L'\0');
57 }
58
59 if (buffer.empty())
60 buffer.push_back(L'\0'); // If there are no environment variables, we have
61 // to ensure there are 4 zero bytes in the buffer.
62 buffer.push_back(L'\0');
63
64 return buffer;
65}
66
67namespace lldb_private {
68llvm::ErrorOr<std::wstring>
70 if (args.empty())
71 return L"";
72
73 std::vector<llvm::StringRef> args_ref;
74 for (auto &entry : args.entries())
75 args_ref.push_back(entry.ref());
76
77 return llvm::sys::flattenWindowsCommandLine(args_ref);
78}
79
80llvm::ErrorOr<std::wstring>
81GetFlattenedWindowsCommandStringW(llvm::ArrayRef<const char *> args) {
82 if (args.empty())
83 return L"";
84
85 std::vector<llvm::StringRef> args_ref(args.begin(), args.end());
86
87 return llvm::sys::flattenWindowsCommandLine(args_ref);
88}
89} // namespace lldb_private
90
91llvm::ErrorOr<ProcThreadAttributeList>
92ProcThreadAttributeList::Create(STARTUPINFOEXW &startupinfoex) {
93 SIZE_T attributelist_size = 0;
94 InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr,
95 /*dwAttributeCount=*/1, /*dwFlags=*/0,
96 &attributelist_size);
97
98 startupinfoex.lpAttributeList =
99 static_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attributelist_size));
100
101 if (!startupinfoex.lpAttributeList)
102 return llvm::mapWindowsError(ERROR_OUTOFMEMORY);
103
104 if (!InitializeProcThreadAttributeList(startupinfoex.lpAttributeList,
105 /*dwAttributeCount=*/1,
106 /*dwFlags=*/0, &attributelist_size)) {
107 free(startupinfoex.lpAttributeList);
108 return llvm::mapWindowsError(GetLastError());
109 }
110
111 return ProcThreadAttributeList(startupinfoex.lpAttributeList);
112}
113
115 BOOL ok = UpdateProcThreadAttribute(lpAttributeList, 0,
117 sizeof(hPC), NULL, NULL);
118 if (!ok)
119 return llvm::errorCodeToError(llvm::mapWindowsError(GetLastError()));
120 return llvm::Error::success();
121}
122
125 Status &error) {
126 error.Clear();
127
128 STARTUPINFOEXW startupinfoex = {};
129 startupinfoex.StartupInfo.cb = sizeof(STARTUPINFOEXW);
130 startupinfoex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
131
132 PseudoConsole::Mode pty_mode = launch_info.ShouldUsePTY()
133 ? launch_info.GetPTY().GetMode()
135
136 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
137 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
138 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
139 llvm::scope_exit close_handles([&] {
140 if (stdin_handle)
141 ::CloseHandle(stdin_handle);
142 if (stdout_handle)
143 ::CloseHandle(stdout_handle);
144 if (stderr_handle)
145 ::CloseHandle(stderr_handle);
146 });
147
148 auto attributelist_or_err = ProcThreadAttributeList::Create(startupinfoex);
149 if (!attributelist_or_err) {
150 error = attributelist_or_err.getError();
151 return HostProcess();
152 }
153 ProcThreadAttributeList attributelist = std::move(*attributelist_or_err);
154
155 std::vector<HANDLE> inherited_handles;
156 switch (pty_mode) {
158 HPCON hPC = launch_info.GetPTY().GetPseudoTerminalHandle();
159 if (auto err = attributelist.SetupPseudoConsole(hPC)) {
160 error = Status::FromError(std::move(err));
161 return HostProcess();
162 }
163 break;
164 }
166 PseudoConsole &pty = launch_info.GetPTY();
167 startupinfoex.StartupInfo.hStdInput = pty.GetChildStdinHandle();
168 startupinfoex.StartupInfo.hStdOutput = pty.GetChildStdoutHandle();
169 startupinfoex.StartupInfo.hStdError = pty.GetChildStdoutHandle();
170 inherited_handles = {pty.GetChildStdinHandle(), pty.GetChildStdoutHandle()};
171 if (!UpdateProcThreadAttribute(
172 startupinfoex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
173 inherited_handles.data(), inherited_handles.size() * sizeof(HANDLE),
174 nullptr, nullptr)) {
175 error = Status(::GetLastError(), eErrorTypeWin32);
176 return HostProcess();
177 }
178 break;
179 }
181 auto inherited_handles_or_err =
182 GetInheritedHandles(startupinfoex, &launch_info, stdout_handle,
183 stderr_handle, stdin_handle);
184 if (!inherited_handles_or_err) {
185 error = Status(inherited_handles_or_err.getError());
186 return HostProcess();
187 }
188 inherited_handles = std::move(*inherited_handles_or_err);
189 break;
190 }
191 }
192
193 const char *hide_console_var =
194 getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
195 if (hide_console_var &&
196 llvm::StringRef(hide_console_var).equals_insensitive("true")) {
197 startupinfoex.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
198 startupinfoex.StartupInfo.wShowWindow = SW_HIDE;
199 }
200
201 DWORD flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT;
202 const bool stdio_redirected = launch_info.IsFDRedirected(STDIN_FILENO) &&
203 launch_info.IsFDRedirected(STDOUT_FILENO) &&
204 launch_info.IsFDRedirected(STDERR_FILENO);
205 if (stdio_redirected)
206 flags |= CREATE_NO_WINDOW;
207 else if (!launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO) &&
208 pty_mode == PseudoConsole::Mode::None)
209 flags |= CREATE_NEW_CONSOLE;
210
211 if (launch_info.GetFlags().Test(eLaunchFlagDebug))
212 flags |= DEBUG_ONLY_THIS_PROCESS;
213
214 std::vector<wchar_t> environment =
216
217 auto wcommandLineOrErr =
219 if (!wcommandLineOrErr) {
220 error = Status(wcommandLineOrErr.getError());
221 return HostProcess();
222 }
223 std::wstring wcommandLine = *wcommandLineOrErr;
224 // If the command line is empty, it's best to pass a null pointer to tell
225 // CreateProcessW to use the executable name as the command line. If the
226 // command line is not empty, its contents may be modified by CreateProcessW.
227 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
228
229 std::wstring wexecutable, wworkingDirectory;
230 llvm::ConvertUTF8toWide(launch_info.GetExecutableFile().GetPath(),
231 wexecutable);
232 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(),
233 wworkingDirectory);
234
235 PROCESS_INFORMATION pi = {};
236
237 BOOL result = ::CreateProcessW(
238 wexecutable.c_str(), pwcommandLine, NULL, NULL,
239 /*bInheritHandles=*/!inherited_handles.empty() ||
240 pty_mode != PseudoConsole::Mode::None,
241 flags, environment.data(),
242 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
243 reinterpret_cast<STARTUPINFOW *>(&startupinfoex), &pi);
244
245 if (!result) {
246 // Call GetLastError before we make any other system calls.
247 error = Status(::GetLastError(), eErrorTypeWin32);
248 // Note that error 50 ("The request is not supported") will occur if you
249 // try debug a 64-bit inferior from a 32-bit LLDB.
250 }
251
252 if (result) {
253 // Do not call CloseHandle on pi.hProcess, since we want to pass that back
254 // through the HostProcess.
255 ::CloseHandle(pi.hThread);
256 if (pty_mode == PseudoConsole::Mode::Pipe)
257 launch_info.GetPTY().CloseAnonymousPipes();
258 }
259
260 if (!result)
261 return HostProcess();
262
263 return HostProcess(pi.hProcess);
264}
265
266llvm::ErrorOr<std::vector<HANDLE>> ProcessLauncherWindows::GetInheritedHandles(
267 STARTUPINFOEXW &startupinfoex, const ProcessLaunchInfo *launch_info,
268 HANDLE stdout_handle, HANDLE stderr_handle, HANDLE stdin_handle) {
269 std::vector<HANDLE> inherited_handles;
270
271 startupinfoex.StartupInfo.hStdInput =
272 stdin_handle ? stdin_handle : GetStdHandle(STD_INPUT_HANDLE);
273 startupinfoex.StartupInfo.hStdOutput =
274 stdout_handle ? stdout_handle : GetStdHandle(STD_OUTPUT_HANDLE);
275
276 // eFileActionDuplicate stores the source fd in m_fd and the destination in
277 // m_arg. GetFileActionForFD searches by m_fd (source), so a
278 // AppendDuplicateFileAction(STDOUT, STDERR) won't be found when looking up
279 // STDERR. Scan for duplicate actions that target stderr explicitly.
280 HANDLE effective_stderr = stderr_handle;
281 if (!effective_stderr && launch_info) {
282 for (size_t i = 0; i < launch_info->GetNumFileActions(); ++i) {
283 const FileAction *act = launch_info->GetFileActionAtIndex(i);
285 act->GetActionArgument() == STDERR_FILENO) {
286 effective_stderr = startupinfoex.StartupInfo.hStdOutput;
287 break;
288 }
289 }
290 }
291 startupinfoex.StartupInfo.hStdError =
292 effective_stderr ? effective_stderr : GetStdHandle(STD_ERROR_HANDLE);
293
294 // PROC_THREAD_ATTRIBUTE_HANDLE_LIST requires unique entries.
295 auto push_if_new = [&](HANDLE h) {
296 if (h && std::find(inherited_handles.begin(), inherited_handles.end(), h) ==
297 inherited_handles.end())
298 inherited_handles.push_back(h);
299 };
300 push_if_new(startupinfoex.StartupInfo.hStdError);
301 push_if_new(startupinfoex.StartupInfo.hStdInput);
302 push_if_new(startupinfoex.StartupInfo.hStdOutput);
303
304 if (launch_info) {
305 for (size_t i = 0; i < launch_info->GetNumFileActions(); ++i) {
306 const WindowsFileAction *act = static_cast<const WindowsFileAction *>(
307 launch_info->GetFileActionAtIndex(i));
308 if (std::find(inherited_handles.begin(), inherited_handles.end(),
309 act->GetHandle()) != inherited_handles.end())
310 continue;
312 continue;
313 if (act->GetActionArgument() != -1 &&
314 act->GetFD() == act->GetActionArgument())
315 inherited_handles.push_back(act->GetHandle());
316 else if (act->GetActionArgumentHandle() != INVALID_HANDLE_VALUE &&
317 act->GetHandle() == act->GetActionArgumentHandle())
318 inherited_handles.push_back(act->GetHandle());
319 }
320 }
321
322 if (inherited_handles.empty())
323 return inherited_handles;
324
325 if (!UpdateProcThreadAttribute(
326 startupinfoex.lpAttributeList, /*dwFlags=*/0,
327 PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(),
328 inherited_handles.size() * sizeof(HANDLE),
329 /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr))
330 return llvm::mapWindowsError(::GetLastError());
331
332 return inherited_handles;
333}
334
335HANDLE
337 int fd) {
338 const FileAction *action = launch_info.GetFileActionForFD(fd);
339 if (action == nullptr)
340 return NULL;
341 const std::string path = action->GetFileSpec().GetPath();
342
343 return GetStdioHandle(path, fd);
344}
345
347 int fd) {
348 if (path.empty())
349 return NULL;
350 SECURITY_ATTRIBUTES secattr = {};
351 secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
352 secattr.bInheritHandle = TRUE;
353
354 DWORD access = 0;
355 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
356 DWORD create = 0;
357 DWORD flags = 0;
358 switch (fd) {
359 case STDIN_FILENO:
360 access = GENERIC_READ;
361 create = OPEN_EXISTING;
362 flags = FILE_ATTRIBUTE_READONLY;
363 break;
364 case STDERR_FILENO:
365 flags = FILE_FLAG_WRITE_THROUGH;
366 [[fallthrough]];
367 case STDOUT_FILENO:
368 access = GENERIC_WRITE;
369 create = CREATE_ALWAYS;
370 break;
371 default:
372 break;
373 }
374
375 std::wstring wpath;
376 llvm::ConvertUTF8toWide(path, wpath);
377 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
378 flags, NULL);
379 return (result == INVALID_HANDLE_VALUE) ? NULL : result;
380}
static llvm::raw_ostream & error(Stream &strm)
static std::vector< wchar_t > CreateEnvironmentBufferW(const Environment &env)
Create a UTF-16 environment block to use with CreateProcessW.
#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
void * HPCON
void * HANDLE
A command line argument class.
Definition Args.h:33
llvm::ArrayRef< ArgEntry > entries() const
Definition Args.h:132
bool empty() const
Definition Args.h:122
static std::string compose(const value_type &KeyValue)
Definition Environment.h:80
Represents a file descriptor action to be performed during process launch.
Definition FileAction.h:21
Action GetAction() const
Get the type of action.
Definition FileAction.h:62
int GetActionArgument() const
Get the action-specific argument.
Definition FileAction.h:68
const FileSpec & GetFileSpec() const
Get the file specification for open actions.
int GetFD() const
Get the file descriptor this action applies to.
Definition FileAction.h:59
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:374
bool Test(ValueType bit) const
Test a single flag bit.
Definition Flags.h:96
This class manages the lifetime of a PROC_THREAD_ATTRIBUTE_LIST, which is used with STARTUPINFOEX.
static llvm::ErrorOr< ProcThreadAttributeList > Create(STARTUPINFOEXW &startupinfoex)
Allocate memory for the attribute list, initialize it, and sets the lpAttributeList member of STARTUP...
llvm::Error SetupPseudoConsole(HPCON hPC)
Setup the PseudoConsole handle in the underlying LPPROC_THREAD_ATTRIBUTE_LIST.
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList
ProcThreadAttributeList(const ProcThreadAttributeList &)=delete
ProcThreadAttributeList is not copyable.
FileSpec & GetExecutableFile()
Definition ProcessInfo.h:43
Environment & GetEnvironment()
Definition ProcessInfo.h:88
bool IsFDRedirected(int fd) const
Returns true if fd has an explicit file action, or is the destination of a duplicate action.
const FileAction * GetFileActionAtIndex(size_t idx) const
const FileAction * GetFileActionForFD(int fd) const
bool ShouldUsePTY() const
Returns whether if lldb should read information from the PTY.
const FileSpec & GetWorkingDirectory() const
HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) override
static HANDLE GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd)
static llvm::ErrorOr< std::vector< HANDLE > > GetInheritedHandles(STARTUPINFOEXW &startupinfoex, const ProcessLaunchInfo *launch_info=nullptr, HANDLE stdout_handle=NULL, HANDLE stderr_handle=NULL, HANDLE stdin_handle=NULL)
Get the list of Windows handles that should be inherited by the child process and update STARTUPINFOE...
HANDLE GetChildStdinHandle() const
The child-side stdin read HANDLE (pipe mode only).
HANDLE GetChildStdoutHandle() const
The child-side stdout/stderr write HANDLE (pipe mode only).
An error handling class.
Definition Status.h:118
static Status FromError(llvm::Error error)
Avoid using this in new code. Migrate APIs to llvm::Expected instead.
Definition Status.cpp:136
A Windows-specific extension of FileAction that supports HANDLE-based file operations in addition to ...
HANDLE GetHandle() const
Get the Windows HANDLE for this action's file.
HANDLE GetActionArgumentHandle() const
Get the Windows HANDLE argument for eFileActionDuplicate actions.
A class that represents a running process on the host machine.
llvm::ErrorOr< std::wstring > GetFlattenedWindowsCommandStringW(const Args &args)
Flattens an Args object into a Windows command-line wide string.
@ eErrorTypeWin32
Standard Win32 error codes.