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