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
9 #include "lldb/Host/windows/ProcessLauncherWindows.h"
10 #include "lldb/Host/HostProcess.h"
11 #include "lldb/Host/ProcessLaunchInfo.h"
12
13 #include "llvm/ADT/SmallVector.h"
14 #include "llvm/Support/ConvertUTF.h"
15 #include "llvm/Support/Program.h"
16
17 #include <string>
18 #include <vector>
19
20 using namespace lldb;
21 using namespace lldb_private;
22
23 namespace {
CreateEnvironmentBuffer(const Environment & env,std::vector<char> & buffer)24 void CreateEnvironmentBuffer(const Environment &env,
25 std::vector<char> &buffer) {
26 // The buffer is a list of null-terminated UTF-16 strings, followed by an
27 // extra L'\0' (two bytes of 0). An empty environment must have one
28 // empty string, followed by an extra L'\0'.
29 for (const auto &KV : env) {
30 std::wstring warg;
31 if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) {
32 buffer.insert(
33 buffer.end(), reinterpret_cast<const char *>(warg.c_str()),
34 reinterpret_cast<const char *>(warg.c_str() + warg.size() + 1));
35 }
36 }
37 // One null wchar_t (to end the block) is two null bytes
38 buffer.push_back(0);
39 buffer.push_back(0);
40 // Insert extra two bytes, just in case the environment was empty.
41 buffer.push_back(0);
42 buffer.push_back(0);
43 }
44
GetFlattenedWindowsCommandString(Args args,std::wstring & command)45 bool GetFlattenedWindowsCommandString(Args args, std::wstring &command) {
46 if (args.empty())
47 return false;
48
49 std::vector<llvm::StringRef> args_ref;
50 for (auto &entry : args.entries())
51 args_ref.push_back(entry.ref());
52
53 llvm::ErrorOr<std::wstring> result =
54 llvm::sys::flattenWindowsCommandLine(args_ref);
55 if (result.getError())
56 return false;
57
58 command = *result;
59 return true;
60 }
61 } // namespace
62
63 HostProcess
LaunchProcess(const ProcessLaunchInfo & launch_info,Status & error)64 ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
65 Status &error) {
66 error.Clear();
67
68 std::string executable;
69 std::vector<char> environment;
70 STARTUPINFO startupinfo = {};
71 PROCESS_INFORMATION pi = {};
72
73 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
74 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
75 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
76
77 startupinfo.cb = sizeof(startupinfo);
78 startupinfo.dwFlags |= STARTF_USESTDHANDLES;
79 startupinfo.hStdError =
80 stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
81 startupinfo.hStdInput =
82 stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
83 startupinfo.hStdOutput =
84 stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
85
86 const char *hide_console_var =
87 getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
88 if (hide_console_var &&
89 llvm::StringRef(hide_console_var).equals_lower("true")) {
90 startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
91 startupinfo.wShowWindow = SW_HIDE;
92 }
93
94 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
95 if (launch_info.GetFlags().Test(eLaunchFlagDebug))
96 flags |= DEBUG_ONLY_THIS_PROCESS;
97
98 if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO))
99 flags &= ~CREATE_NEW_CONSOLE;
100
101 LPVOID env_block = nullptr;
102 ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment);
103 env_block = environment.data();
104
105 executable = launch_info.GetExecutableFile().GetPath();
106 std::wstring wcommandLine;
107 GetFlattenedWindowsCommandString(launch_info.GetArguments(), wcommandLine);
108
109 std::wstring wexecutable, wworkingDirectory;
110 llvm::ConvertUTF8toWide(executable, wexecutable);
111 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(),
112 wworkingDirectory);
113 // If the command line is empty, it's best to pass a null pointer to tell
114 // CreateProcessW to use the executable name as the command line. If the
115 // command line is not empty, its contents may be modified by CreateProcessW.
116 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
117
118 BOOL result = ::CreateProcessW(
119 wexecutable.c_str(), pwcommandLine, NULL, NULL, TRUE, flags, env_block,
120 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
121 &startupinfo, &pi);
122
123 if (!result) {
124 // Call GetLastError before we make any other system calls.
125 error.SetError(::GetLastError(), eErrorTypeWin32);
126 // Note that error 50 ("The request is not supported") will occur if you
127 // try debug a 64-bit inferior from a 32-bit LLDB.
128 }
129
130 if (result) {
131 // Do not call CloseHandle on pi.hProcess, since we want to pass that back
132 // through the HostProcess.
133 ::CloseHandle(pi.hThread);
134 }
135
136 if (stdin_handle)
137 ::CloseHandle(stdin_handle);
138 if (stdout_handle)
139 ::CloseHandle(stdout_handle);
140 if (stderr_handle)
141 ::CloseHandle(stderr_handle);
142
143 if (!result)
144 return HostProcess();
145
146 return HostProcess(pi.hProcess);
147 }
148
149 HANDLE
GetStdioHandle(const ProcessLaunchInfo & launch_info,int fd)150 ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,
151 int fd) {
152 const FileAction *action = launch_info.GetFileActionForFD(fd);
153 if (action == nullptr)
154 return NULL;
155 SECURITY_ATTRIBUTES secattr = {};
156 secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
157 secattr.bInheritHandle = TRUE;
158
159 llvm::StringRef path = action->GetPath();
160 DWORD access = 0;
161 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
162 DWORD create = 0;
163 DWORD flags = 0;
164 if (fd == STDIN_FILENO) {
165 access = GENERIC_READ;
166 create = OPEN_EXISTING;
167 flags = FILE_ATTRIBUTE_READONLY;
168 }
169 if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
170 access = GENERIC_WRITE;
171 create = CREATE_ALWAYS;
172 if (fd == STDERR_FILENO)
173 flags = FILE_FLAG_WRITE_THROUGH;
174 }
175
176 std::wstring wpath;
177 llvm::ConvertUTF8toWide(path, wpath);
178 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
179 flags, NULL);
180 return (result == INVALID_HANDLE_VALUE) ? NULL : result;
181 }
182