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