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
13
14#include "llvm/ADT/SmallVector.h"
15#include "llvm/Support/ConvertUTF.h"
16#include "llvm/Support/Program.h"
17#include "llvm/Support/WindowsError.h"
18
19#include <string>
20#include <vector>
21
22using namespace lldb;
23using namespace lldb_private;
24
25/// Create a UTF-16 environment block to use with CreateProcessW.
26///
27/// The buffer is a sequence of null-terminated UTF-16 strings, followed by an
28/// extra L'\0' (two bytes of 0). An empty environment must have one
29/// empty string, followed by an extra L'\0'.
30///
31/// The keys are sorted to comply with the CreateProcess API calling convention.
32///
33/// Ensure that the resulting buffer is used in conjunction with
34/// CreateProcessW and be sure that dwCreationFlags includes
35/// CREATE_UNICODE_ENVIRONMENT.
36///
37/// \param env The Environment object to convert.
38/// \returns The sorted sequence of environment variables and their values,
39/// separated by null terminators. The vector is guaranteed to never be empty.
40static std::vector<wchar_t> CreateEnvironmentBufferW(const Environment &env) {
41 std::vector<std::wstring> env_entries;
42 for (const auto &KV : env) {
43 std::wstring wentry;
44 if (llvm::ConvertUTF8toWide(Environment::compose(KV), wentry))
45 env_entries.push_back(std::move(wentry));
46 }
47 std::sort(env_entries.begin(), env_entries.end(),
48 [](const std::wstring &a, const std::wstring &b) {
49 return _wcsicmp(a.c_str(), b.c_str()) < 0;
50 });
51
52 std::vector<wchar_t> buffer;
53 for (const auto &env_entry : env_entries) {
54 buffer.insert(buffer.end(), env_entry.begin(), env_entry.end());
55 buffer.push_back(L'\0');
56 }
57
58 if (buffer.empty())
59 buffer.push_back(L'\0'); // If there are no environment variables, we have
60 // to ensure there are 4 zero bytes in the buffer.
61 buffer.push_back(L'\0');
62
63 return buffer;
64}
65
66namespace lldb_private {
67llvm::ErrorOr<std::wstring>
69 if (args.empty())
70 return L"";
71
72 std::vector<llvm::StringRef> args_ref;
73 for (auto &entry : args.entries())
74 args_ref.push_back(entry.ref());
75
76 return llvm::sys::flattenWindowsCommandLine(args_ref);
77}
78
79llvm::ErrorOr<std::wstring>
80GetFlattenedWindowsCommandStringW(llvm::ArrayRef<const char *> args) {
81 if (args.empty())
82 return L"";
83
84 std::vector<llvm::StringRef> args_ref;
85 for (int i = 0; args[i] != nullptr; ++i)
86 args_ref.push_back(args[i]);
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), NULL, NULL);
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 bool use_pty = launch_info.ShouldUsePTY();
134
135 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
136 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
137 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
138 llvm::scope_exit close_handles([&] {
139 if (stdin_handle)
140 ::CloseHandle(stdin_handle);
141 if (stdout_handle)
142 ::CloseHandle(stdout_handle);
143 if (stderr_handle)
144 ::CloseHandle(stderr_handle);
145 });
146
147 auto attributelist_or_err = ProcThreadAttributeList::Create(startupinfoex);
148 if (!attributelist_or_err) {
149 error = attributelist_or_err.getError();
150 return HostProcess();
151 }
152 ProcThreadAttributeList attributelist = std::move(*attributelist_or_err);
153
154 std::vector<HANDLE> inherited_handles;
155 if (use_pty) {
156 HPCON hPC = launch_info.GetPTY().GetPseudoTerminalHandle();
157 if (auto err = attributelist.SetupPseudoConsole(hPC)) {
158 error = Status::FromError(std::move(err));
159 return HostProcess();
160 }
161 } else {
162 auto inherited_handles_or_err =
163 GetInheritedHandles(startupinfoex, &launch_info, stdout_handle,
164 stderr_handle, stdin_handle);
165 if (!inherited_handles_or_err) {
166 error = Status(inherited_handles_or_err.getError());
167 return HostProcess();
168 }
169 inherited_handles = std::move(*inherited_handles_or_err);
170 }
171
172 const char *hide_console_var =
173 getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
174 if (hide_console_var &&
175 llvm::StringRef(hide_console_var).equals_insensitive("true")) {
176 startupinfoex.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
177 startupinfoex.StartupInfo.wShowWindow = SW_HIDE;
178 }
179
180 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT |
181 EXTENDED_STARTUPINFO_PRESENT;
182 if (launch_info.GetFlags().Test(eLaunchFlagDebug))
183 flags |= DEBUG_ONLY_THIS_PROCESS;
184
185 if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO) || use_pty)
186 flags &= ~CREATE_NEW_CONSOLE;
187
188 std::vector<wchar_t> environment =
190
191 auto wcommandLineOrErr =
193 if (!wcommandLineOrErr) {
194 error = Status(wcommandLineOrErr.getError());
195 return HostProcess();
196 }
197 std::wstring wcommandLine = *wcommandLineOrErr;
198 // If the command line is empty, it's best to pass a null pointer to tell
199 // CreateProcessW to use the executable name as the command line. If the
200 // command line is not empty, its contents may be modified by CreateProcessW.
201 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
202
203 std::wstring wexecutable, wworkingDirectory;
204 llvm::ConvertUTF8toWide(launch_info.GetExecutableFile().GetPath(),
205 wexecutable);
206 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(),
207 wworkingDirectory);
208
209 PROCESS_INFORMATION pi = {};
210
211 BOOL result = ::CreateProcessW(
212 wexecutable.c_str(), pwcommandLine, NULL, NULL,
213 /*bInheritHandles=*/!inherited_handles.empty() || use_pty, flags,
214 environment.data(),
215 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
216 reinterpret_cast<STARTUPINFOW *>(&startupinfoex), &pi);
217
218 if (!result) {
219 // Call GetLastError before we make any other system calls.
220 error = Status(::GetLastError(), eErrorTypeWin32);
221 // Note that error 50 ("The request is not supported") will occur if you
222 // try debug a 64-bit inferior from a 32-bit LLDB.
223 }
224
225 if (result) {
226 // Do not call CloseHandle on pi.hProcess, since we want to pass that back
227 // through the HostProcess.
228 ::CloseHandle(pi.hThread);
229 }
230
231 if (!result)
232 return HostProcess();
233
234 return HostProcess(pi.hProcess);
235}
236
237llvm::ErrorOr<std::vector<HANDLE>> ProcessLauncherWindows::GetInheritedHandles(
238 STARTUPINFOEXW &startupinfoex, const ProcessLaunchInfo *launch_info,
239 HANDLE stdout_handle, HANDLE stderr_handle, HANDLE stdin_handle) {
240 std::vector<HANDLE> inherited_handles;
241
242 startupinfoex.StartupInfo.hStdError =
243 stderr_handle ? stderr_handle : GetStdHandle(STD_ERROR_HANDLE);
244 startupinfoex.StartupInfo.hStdInput =
245 stdin_handle ? stdin_handle : GetStdHandle(STD_INPUT_HANDLE);
246 startupinfoex.StartupInfo.hStdOutput =
247 stdout_handle ? stdout_handle : GetStdHandle(STD_OUTPUT_HANDLE);
248
249 if (startupinfoex.StartupInfo.hStdError)
250 inherited_handles.push_back(startupinfoex.StartupInfo.hStdError);
251 if (startupinfoex.StartupInfo.hStdInput)
252 inherited_handles.push_back(startupinfoex.StartupInfo.hStdInput);
253 if (startupinfoex.StartupInfo.hStdOutput)
254 inherited_handles.push_back(startupinfoex.StartupInfo.hStdOutput);
255
256 if (launch_info) {
257 for (size_t i = 0; i < launch_info->GetNumFileActions(); ++i) {
258 const FileAction *act = launch_info->GetFileActionAtIndex(i);
260 act->GetFD() == act->GetActionArgument())
261 inherited_handles.push_back(reinterpret_cast<HANDLE>(act->GetFD()));
262 }
263 }
264
265 if (inherited_handles.empty())
266 return inherited_handles;
267
268 if (!UpdateProcThreadAttribute(
269 startupinfoex.lpAttributeList, /*dwFlags=*/0,
270 PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(),
271 inherited_handles.size() * sizeof(HANDLE),
272 /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr))
273 return llvm::mapWindowsError(::GetLastError());
274
275 return inherited_handles;
276}
277
278HANDLE
280 int fd) {
281 const FileAction *action = launch_info.GetFileActionForFD(fd);
282 if (action == nullptr)
283 return NULL;
284 const std::string path = action->GetFileSpec().GetPath();
285
286 return GetStdioHandle(path, fd);
287}
288
290 int fd) {
291 if (path.empty())
292 return NULL;
293 SECURITY_ATTRIBUTES secattr = {};
294 secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
295 secattr.bInheritHandle = TRUE;
296
297 DWORD access = 0;
298 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
299 DWORD create = 0;
300 DWORD flags = 0;
301 if (fd == STDIN_FILENO) {
302 access = GENERIC_READ;
303 create = OPEN_EXISTING;
304 flags = FILE_ATTRIBUTE_READONLY;
305 }
306 if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
307 access = GENERIC_WRITE;
308 create = CREATE_ALWAYS;
309 if (fd == STDERR_FILENO)
310 flags = FILE_FLAG_WRITE_THROUGH;
311 }
312
313 std::wstring wpath;
314 llvm::ConvertUTF8toWide(path, wpath);
315 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
316 flags, NULL);
317 return (result == INVALID_HANDLE_VALUE) ? NULL : result;
318}
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
Action GetAction() const
Definition FileAction.h:38
int GetActionArgument() const
Definition FileAction.h:40
const FileSpec & GetFileSpec() const
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...
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 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.