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/ScopeExit.h"
16#include "llvm/ADT/SmallVector.h"
17#include "llvm/Support/ConvertUTF.h"
18#include "llvm/Support/Program.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
68/// Flattens an Args object into a Windows command-line wide string.
69///
70/// Returns an empty string if args is empty.
71///
72/// \param args The Args object to flatten.
73/// \returns A wide string containing the flattened command line.
74static llvm::ErrorOr<std::wstring>
76 if (args.empty())
77 return L"";
78
79 std::vector<llvm::StringRef> args_ref;
80 for (auto &entry : args.entries())
81 args_ref.push_back(entry.ref());
82
83 return llvm::sys::flattenWindowsCommandLine(args_ref);
84}
85
86llvm::ErrorOr<ProcThreadAttributeList>
87ProcThreadAttributeList::Create(STARTUPINFOEXW &startupinfoex) {
88 SIZE_T attributelist_size = 0;
89 InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr,
90 /*dwAttributeCount=*/1, /*dwFlags=*/0,
91 &attributelist_size);
92
93 startupinfoex.lpAttributeList =
94 static_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attributelist_size));
95
96 if (!startupinfoex.lpAttributeList)
97 return llvm::mapWindowsError(ERROR_OUTOFMEMORY);
98
99 if (!InitializeProcThreadAttributeList(startupinfoex.lpAttributeList,
100 /*dwAttributeCount=*/1,
101 /*dwFlags=*/0, &attributelist_size)) {
102 free(startupinfoex.lpAttributeList);
103 return llvm::mapWindowsError(GetLastError());
104 }
105
106 return ProcThreadAttributeList(startupinfoex.lpAttributeList);
107}
108
111 Status &error) {
112 error.Clear();
113
114 STARTUPINFOEXW startupinfoex = {};
115 startupinfoex.StartupInfo.cb = sizeof(STARTUPINFOEXW);
116 startupinfoex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
117
118 HPCON hPC = launch_info.GetPTY().GetPseudoTerminalHandle();
119 bool use_pty = hPC != INVALID_HANDLE_VALUE &&
120 launch_info.GetNumFileActions() == 0 &&
121 launch_info.GetFlags().Test(lldb::eLaunchFlagLaunchInTTY);
122
123 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
124 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
125 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
126 llvm::scope_exit close_handles([&] {
127 if (stdin_handle)
128 ::CloseHandle(stdin_handle);
129 if (stdout_handle)
130 ::CloseHandle(stdout_handle);
131 if (stderr_handle)
132 ::CloseHandle(stderr_handle);
133 });
134
135 auto attributelist_or_err = ProcThreadAttributeList::Create(startupinfoex);
136 if (!attributelist_or_err) {
137 error = attributelist_or_err.getError();
138 return HostProcess();
139 }
140 llvm::scope_exit delete_attributelist(
141 [&] { DeleteProcThreadAttributeList(startupinfoex.lpAttributeList); });
142
143 std::vector<HANDLE> inherited_handles;
144 if (use_pty) {
145 if (!UpdateProcThreadAttribute(startupinfoex.lpAttributeList, 0,
147 sizeof(hPC), NULL, NULL)) {
148 error = Status(::GetLastError(), eErrorTypeWin32);
149 return HostProcess();
150 }
151 } else {
152 auto inherited_handles_or_err = GetInheritedHandles(
153 launch_info, startupinfoex, stdout_handle, stderr_handle, stdin_handle);
154 if (!inherited_handles_or_err) {
155 error = Status(inherited_handles_or_err.getError());
156 return HostProcess();
157 }
158 inherited_handles = std::move(*inherited_handles_or_err);
159 }
160
161 const char *hide_console_var =
162 getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
163 if (hide_console_var &&
164 llvm::StringRef(hide_console_var).equals_insensitive("true")) {
165 startupinfoex.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
166 startupinfoex.StartupInfo.wShowWindow = SW_HIDE;
167 }
168
169 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT |
170 EXTENDED_STARTUPINFO_PRESENT;
171 if (launch_info.GetFlags().Test(eLaunchFlagDebug))
172 flags |= DEBUG_ONLY_THIS_PROCESS;
173
174 if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO) || use_pty)
175 flags &= ~CREATE_NEW_CONSOLE;
176
177 std::vector<wchar_t> environment =
179
180 auto wcommandLineOrErr =
182 if (!wcommandLineOrErr) {
183 error = Status(wcommandLineOrErr.getError());
184 return HostProcess();
185 }
186 std::wstring wcommandLine = *wcommandLineOrErr;
187 // If the command line is empty, it's best to pass a null pointer to tell
188 // CreateProcessW to use the executable name as the command line. If the
189 // command line is not empty, its contents may be modified by CreateProcessW.
190 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
191
192 std::wstring wexecutable, wworkingDirectory;
193 llvm::ConvertUTF8toWide(launch_info.GetExecutableFile().GetPath(),
194 wexecutable);
195 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(),
196 wworkingDirectory);
197
198 PROCESS_INFORMATION pi = {};
199
200 BOOL result = ::CreateProcessW(
201 wexecutable.c_str(), pwcommandLine, NULL, NULL,
202 /*bInheritHandles=*/!inherited_handles.empty(), flags, environment.data(),
203 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
204 reinterpret_cast<STARTUPINFOW *>(&startupinfoex), &pi);
205
206 if (!result) {
207 // Call GetLastError before we make any other system calls.
208 error = Status(::GetLastError(), eErrorTypeWin32);
209 // Note that error 50 ("The request is not supported") will occur if you
210 // try debug a 64-bit inferior from a 32-bit LLDB.
211 }
212
213 if (result) {
214 // Do not call CloseHandle on pi.hProcess, since we want to pass that back
215 // through the HostProcess.
216 ::CloseHandle(pi.hThread);
217 }
218
219 if (!result)
220 return HostProcess();
221
222 return HostProcess(pi.hProcess);
223}
224
225llvm::ErrorOr<std::vector<HANDLE>> ProcessLauncherWindows::GetInheritedHandles(
226 const ProcessLaunchInfo &launch_info, STARTUPINFOEXW &startupinfoex,
227 HANDLE stdout_handle, HANDLE stderr_handle, HANDLE stdin_handle) {
228 std::vector<HANDLE> inherited_handles;
229
230 startupinfoex.StartupInfo.hStdError =
231 stderr_handle ? stderr_handle : GetStdHandle(STD_ERROR_HANDLE);
232 startupinfoex.StartupInfo.hStdInput =
233 stdin_handle ? stdin_handle : GetStdHandle(STD_INPUT_HANDLE);
234 startupinfoex.StartupInfo.hStdOutput =
235 stdout_handle ? stdout_handle : GetStdHandle(STD_OUTPUT_HANDLE);
236
237 if (startupinfoex.StartupInfo.hStdError)
238 inherited_handles.push_back(startupinfoex.StartupInfo.hStdError);
239 if (startupinfoex.StartupInfo.hStdInput)
240 inherited_handles.push_back(startupinfoex.StartupInfo.hStdInput);
241 if (startupinfoex.StartupInfo.hStdOutput)
242 inherited_handles.push_back(startupinfoex.StartupInfo.hStdOutput);
243
244 for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) {
245 const FileAction *act = launch_info.GetFileActionAtIndex(i);
247 act->GetFD() == act->GetActionArgument())
248 inherited_handles.push_back(reinterpret_cast<HANDLE>(act->GetFD()));
249 }
250
251 if (inherited_handles.empty())
252 return inherited_handles;
253
254 if (!UpdateProcThreadAttribute(
255 startupinfoex.lpAttributeList, /*dwFlags=*/0,
256 PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(),
257 inherited_handles.size() * sizeof(HANDLE),
258 /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr))
259 return llvm::mapWindowsError(::GetLastError());
260
261 return inherited_handles;
262}
263
264HANDLE
266 int fd) {
267 const FileAction *action = launch_info.GetFileActionForFD(fd);
268 if (action == nullptr)
269 return NULL;
270 SECURITY_ATTRIBUTES secattr = {};
271 secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
272 secattr.bInheritHandle = TRUE;
273
274 DWORD access = 0;
275 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
276 DWORD create = 0;
277 DWORD flags = 0;
278 if (fd == STDIN_FILENO) {
279 access = GENERIC_READ;
280 create = OPEN_EXISTING;
281 flags = FILE_ATTRIBUTE_READONLY;
282 }
283 if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
284 access = GENERIC_WRITE;
285 create = CREATE_ALWAYS;
286 if (fd == STDERR_FILENO)
287 flags = FILE_FLAG_WRITE_THROUGH;
288 }
289
290 const std::string path = action->GetFileSpec().GetPath();
291 std::wstring wpath;
292 llvm::ConvertUTF8toWide(path, wpath);
293 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
294 flags, NULL);
295 return (result == INVALID_HANDLE_VALUE) ? NULL : result;
296}
static llvm::raw_ostream & error(Stream &strm)
static llvm::ErrorOr< std::wstring > GetFlattenedWindowsCommandStringW(Args args)
Flattens an Args object into a Windows command-line wide string.
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
static llvm::ErrorOr< ProcThreadAttributeList > Create(STARTUPINFOEXW &startupinfoex)
Allocate memory for the attribute list, initialize it, and sets the lpAttributeList member of STARTUP...
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
const FileSpec & GetWorkingDirectory() const
HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) override
llvm::ErrorOr< std::vector< HANDLE > > GetInheritedHandles(const ProcessLaunchInfo &launch_info, STARTUPINFOEXW &startupinfoex, 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 GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd)
An error handling class.
Definition Status.h:118
A class that represents a running process on the host machine.
@ eErrorTypeWin32
Standard Win32 error codes.