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_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT |
202 EXTENDED_STARTUPINFO_PRESENT;
203 if (launch_info.GetFlags().Test(eLaunchFlagDebug))
204 flags |= DEBUG_ONLY_THIS_PROCESS;
205
206 if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO) ||
207 pty_mode != PseudoConsole::Mode::None)
208 flags &= ~CREATE_NEW_CONSOLE;
209
210 std::vector<wchar_t> environment =
212
213 auto wcommandLineOrErr =
215 if (!wcommandLineOrErr) {
216 error = Status(wcommandLineOrErr.getError());
217 return HostProcess();
218 }
219 std::wstring wcommandLine = *wcommandLineOrErr;
220 // If the command line is empty, it's best to pass a null pointer to tell
221 // CreateProcessW to use the executable name as the command line. If the
222 // command line is not empty, its contents may be modified by CreateProcessW.
223 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
224
225 std::wstring wexecutable, wworkingDirectory;
226 llvm::ConvertUTF8toWide(launch_info.GetExecutableFile().GetPath(),
227 wexecutable);
228 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(),
229 wworkingDirectory);
230
231 PROCESS_INFORMATION pi = {};
232
233 BOOL result = ::CreateProcessW(
234 wexecutable.c_str(), pwcommandLine, NULL, NULL,
235 /*bInheritHandles=*/!inherited_handles.empty() ||
236 pty_mode != PseudoConsole::Mode::None,
237 flags, environment.data(),
238 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
239 reinterpret_cast<STARTUPINFOW *>(&startupinfoex), &pi);
240
241 if (!result) {
242 // Call GetLastError before we make any other system calls.
243 error = Status(::GetLastError(), eErrorTypeWin32);
244 // Note that error 50 ("The request is not supported") will occur if you
245 // try debug a 64-bit inferior from a 32-bit LLDB.
246 }
247
248 if (result) {
249 // Do not call CloseHandle on pi.hProcess, since we want to pass that back
250 // through the HostProcess.
251 ::CloseHandle(pi.hThread);
252 if (pty_mode == PseudoConsole::Mode::Pipe)
253 launch_info.GetPTY().CloseAnonymousPipes();
254 }
255
256 if (!result)
257 return HostProcess();
258
259 return HostProcess(pi.hProcess);
260}
261
262llvm::ErrorOr<std::vector<HANDLE>> ProcessLauncherWindows::GetInheritedHandles(
263 STARTUPINFOEXW &startupinfoex, const ProcessLaunchInfo *launch_info,
264 HANDLE stdout_handle, HANDLE stderr_handle, HANDLE stdin_handle) {
265 std::vector<HANDLE> inherited_handles;
266
267 startupinfoex.StartupInfo.hStdError =
268 stderr_handle ? stderr_handle : GetStdHandle(STD_ERROR_HANDLE);
269 startupinfoex.StartupInfo.hStdInput =
270 stdin_handle ? stdin_handle : GetStdHandle(STD_INPUT_HANDLE);
271 startupinfoex.StartupInfo.hStdOutput =
272 stdout_handle ? stdout_handle : GetStdHandle(STD_OUTPUT_HANDLE);
273
274 if (startupinfoex.StartupInfo.hStdError)
275 inherited_handles.push_back(startupinfoex.StartupInfo.hStdError);
276 if (startupinfoex.StartupInfo.hStdInput)
277 inherited_handles.push_back(startupinfoex.StartupInfo.hStdInput);
278 if (startupinfoex.StartupInfo.hStdOutput)
279 inherited_handles.push_back(startupinfoex.StartupInfo.hStdOutput);
280
281 if (launch_info) {
282 for (size_t i = 0; i < launch_info->GetNumFileActions(); ++i) {
283 const WindowsFileAction *act = static_cast<const WindowsFileAction *>(
284 launch_info->GetFileActionAtIndex(i));
285 if (std::find(inherited_handles.begin(), inherited_handles.end(),
286 act->GetHandle()) != inherited_handles.end())
287 continue;
289 continue;
290 if (act->GetActionArgument() != -1 &&
291 act->GetFD() == act->GetActionArgument())
292 inherited_handles.push_back(act->GetHandle());
293 else if (act->GetActionArgumentHandle() != INVALID_HANDLE_VALUE &&
294 act->GetHandle() == act->GetActionArgumentHandle())
295 inherited_handles.push_back(act->GetHandle());
296 }
297 }
298
299 if (inherited_handles.empty())
300 return inherited_handles;
301
302 if (!UpdateProcThreadAttribute(
303 startupinfoex.lpAttributeList, /*dwFlags=*/0,
304 PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(),
305 inherited_handles.size() * sizeof(HANDLE),
306 /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr))
307 return llvm::mapWindowsError(::GetLastError());
308
309 return inherited_handles;
310}
311
312HANDLE
314 int fd) {
315 const FileAction *action = launch_info.GetFileActionForFD(fd);
316 if (action == nullptr)
317 return NULL;
318 const std::string path = action->GetFileSpec().GetPath();
319
320 return GetStdioHandle(path, fd);
321}
322
324 int fd) {
325 if (path.empty())
326 return NULL;
327 SECURITY_ATTRIBUTES secattr = {};
328 secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
329 secattr.bInheritHandle = TRUE;
330
331 DWORD access = 0;
332 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
333 DWORD create = 0;
334 DWORD flags = 0;
335 switch (fd) {
336 case STDIN_FILENO:
337 access = GENERIC_READ;
338 create = OPEN_EXISTING;
339 flags = FILE_ATTRIBUTE_READONLY;
340 break;
341 case STDERR_FILENO:
342 flags = FILE_FLAG_WRITE_THROUGH;
343 case STDOUT_FILENO:
344 access = GENERIC_WRITE;
345 create = CREATE_ALWAYS;
346 break;
347 default:
348 break;
349 }
350
351 std::wstring wpath;
352 llvm::ConvertUTF8toWide(path, wpath);
353 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
354 flags, NULL);
355 return (result == INVALID_HANDLE_VALUE) ? NULL : result;
356}
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
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.