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