180814287SRaphael Isemann //===-- ProcessLauncherWindows.cpp ----------------------------------------===//
2172d37d3SZachary Turner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6172d37d3SZachary Turner //
7172d37d3SZachary Turner //===----------------------------------------------------------------------===//
8172d37d3SZachary Turner 
9172d37d3SZachary Turner #include "lldb/Host/windows/ProcessLauncherWindows.h"
10b9c1b51eSKate Stone #include "lldb/Host/HostProcess.h"
11eef758e9SPavel Labath #include "lldb/Host/ProcessLaunchInfo.h"
12172d37d3SZachary Turner 
13190fadcdSZachary Turner #include "llvm/ADT/SmallVector.h"
14190fadcdSZachary Turner #include "llvm/Support/ConvertUTF.h"
15981e6358SAaron Smith #include "llvm/Support/Program.h"
16190fadcdSZachary Turner 
17172d37d3SZachary Turner #include <string>
18172d37d3SZachary Turner #include <vector>
19172d37d3SZachary Turner 
20172d37d3SZachary Turner using namespace lldb;
21172d37d3SZachary Turner using namespace lldb_private;
22172d37d3SZachary Turner 
CreateEnvironmentBuffer(const Environment & env,std::vector<char> & buffer)23*93c1b3caSPavel Labath static void CreateEnvironmentBuffer(const Environment &env,
243ff377a9SPavel Labath                                     std::vector<char> &buffer) {
2562be8346SPavel Labath   // The buffer is a list of null-terminated UTF-16 strings, followed by an
2662be8346SPavel Labath   // extra L'\0' (two bytes of 0).  An empty environment must have one
2762be8346SPavel Labath   // empty string, followed by an extra L'\0'.
283ff377a9SPavel Labath   for (const auto &KV : env) {
29190fadcdSZachary Turner     std::wstring warg;
303ff377a9SPavel Labath     if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) {
31b0717666SAlexandre Ganea       buffer.insert(
32b0717666SAlexandre Ganea           buffer.end(), reinterpret_cast<const char *>(warg.c_str()),
33b0717666SAlexandre Ganea           reinterpret_cast<const char *>(warg.c_str() + warg.size() + 1));
3419e2ea8fSZachary Turner     }
35190fadcdSZachary Turner   }
36190fadcdSZachary Turner   // One null wchar_t (to end the block) is two null bytes
37190fadcdSZachary Turner   buffer.push_back(0);
38190fadcdSZachary Turner   buffer.push_back(0);
3962be8346SPavel Labath   // Insert extra two bytes, just in case the environment was empty.
4062be8346SPavel Labath   buffer.push_back(0);
4162be8346SPavel Labath   buffer.push_back(0);
4219e2ea8fSZachary Turner }
43981e6358SAaron Smith 
GetFlattenedWindowsCommandString(Args args,std::wstring & command)44*93c1b3caSPavel Labath static bool GetFlattenedWindowsCommandString(Args args, std::wstring &command) {
45981e6358SAaron Smith   if (args.empty())
46981e6358SAaron Smith     return false;
47981e6358SAaron Smith 
48981e6358SAaron Smith   std::vector<llvm::StringRef> args_ref;
49981e6358SAaron Smith   for (auto &entry : args.entries())
50646a893fSAdrian McCarthy     args_ref.push_back(entry.ref());
51981e6358SAaron Smith 
52dab898f9SSerge Pavlov   llvm::ErrorOr<std::wstring> result =
53dab898f9SSerge Pavlov       llvm::sys::flattenWindowsCommandLine(args_ref);
54dab898f9SSerge Pavlov   if (result.getError())
55dab898f9SSerge Pavlov     return false;
56dab898f9SSerge Pavlov 
57dab898f9SSerge Pavlov   command = *result;
58981e6358SAaron Smith   return true;
5919e2ea8fSZachary Turner }
6019e2ea8fSZachary Turner 
61172d37d3SZachary Turner HostProcess
LaunchProcess(const ProcessLaunchInfo & launch_info,Status & error)62b9c1b51eSKate Stone ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
6397206d57SZachary Turner                                       Status &error) {
64172d37d3SZachary Turner   error.Clear();
65172d37d3SZachary Turner 
66172d37d3SZachary Turner   std::string executable;
67172d37d3SZachary Turner   std::vector<char> environment;
685a8ad459SZachary Turner   STARTUPINFO startupinfo = {};
695a8ad459SZachary Turner   PROCESS_INFORMATION pi = {};
70172d37d3SZachary Turner 
71172d37d3SZachary Turner   HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
72172d37d3SZachary Turner   HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
73172d37d3SZachary Turner   HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
74172d37d3SZachary Turner 
75172d37d3SZachary Turner   startupinfo.cb = sizeof(startupinfo);
76172d37d3SZachary Turner   startupinfo.dwFlags |= STARTF_USESTDHANDLES;
77b9c1b51eSKate Stone   startupinfo.hStdError =
78b9c1b51eSKate Stone       stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
79b9c1b51eSKate Stone   startupinfo.hStdInput =
80b9c1b51eSKate Stone       stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
81b9c1b51eSKate Stone   startupinfo.hStdOutput =
82b9c1b51eSKate Stone       stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
83172d37d3SZachary Turner 
84b9c1b51eSKate Stone   const char *hide_console_var =
85b9c1b51eSKate Stone       getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
86b9c1b51eSKate Stone   if (hide_console_var &&
87e50f9c41SMartin Storsjö       llvm::StringRef(hide_console_var).equals_insensitive("true")) {
88555a7a6aSZachary Turner     startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
89555a7a6aSZachary Turner     startupinfo.wShowWindow = SW_HIDE;
90555a7a6aSZachary Turner   }
91555a7a6aSZachary Turner 
92190fadcdSZachary Turner   DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
93742346a2SZachary Turner   if (launch_info.GetFlags().Test(eLaunchFlagDebug))
94742346a2SZachary Turner     flags |= DEBUG_ONLY_THIS_PROCESS;
95742346a2SZachary Turner 
96d75a8fffSDavid Bolvansky   if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO))
97d75a8fffSDavid Bolvansky     flags &= ~CREATE_NEW_CONSOLE;
98d75a8fffSDavid Bolvansky 
9919e2ea8fSZachary Turner   LPVOID env_block = nullptr;
1003ff377a9SPavel Labath   ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment);
10119e2ea8fSZachary Turner   env_block = environment.data();
10219e2ea8fSZachary Turner 
103172d37d3SZachary Turner   executable = launch_info.GetExecutableFile().GetPath();
104dab898f9SSerge Pavlov   std::wstring wcommandLine;
105dab898f9SSerge Pavlov   GetFlattenedWindowsCommandString(launch_info.GetArguments(), wcommandLine);
106190fadcdSZachary Turner 
107dab898f9SSerge Pavlov   std::wstring wexecutable, wworkingDirectory;
108190fadcdSZachary Turner   llvm::ConvertUTF8toWide(executable, wexecutable);
109b9c1b51eSKate Stone   llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(),
110b9c1b51eSKate Stone                           wworkingDirectory);
11134f2bee0SAdrian McCarthy   // If the command line is empty, it's best to pass a null pointer to tell
11234f2bee0SAdrian McCarthy   // CreateProcessW to use the executable name as the command line.  If the
11334f2bee0SAdrian McCarthy   // command line is not empty, its contents may be modified by CreateProcessW.
11434f2bee0SAdrian McCarthy   WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
115190fadcdSZachary Turner 
116b9c1b51eSKate Stone   BOOL result = ::CreateProcessW(
11734f2bee0SAdrian McCarthy       wexecutable.c_str(), pwcommandLine, NULL, NULL, TRUE, flags, env_block,
118b9c1b51eSKate Stone       wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
119b9c1b51eSKate Stone       &startupinfo, &pi);
1209d6fabf9SStella Stamenova 
1219d6fabf9SStella Stamenova   if (!result) {
1229d6fabf9SStella Stamenova     // Call GetLastError before we make any other system calls.
1239d6fabf9SStella Stamenova     error.SetError(::GetLastError(), eErrorTypeWin32);
12434f2bee0SAdrian McCarthy     // Note that error 50 ("The request is not supported") will occur if you
12534f2bee0SAdrian McCarthy     // try debug a 64-bit inferior from a 32-bit LLDB.
1269d6fabf9SStella Stamenova   }
1279d6fabf9SStella Stamenova 
128b9c1b51eSKate Stone   if (result) {
129b9c1b51eSKate Stone     // Do not call CloseHandle on pi.hProcess, since we want to pass that back
130b9c1b51eSKate Stone     // through the HostProcess.
131172d37d3SZachary Turner     ::CloseHandle(pi.hThread);
132172d37d3SZachary Turner   }
133172d37d3SZachary Turner 
134172d37d3SZachary Turner   if (stdin_handle)
135172d37d3SZachary Turner     ::CloseHandle(stdin_handle);
136172d37d3SZachary Turner   if (stdout_handle)
137172d37d3SZachary Turner     ::CloseHandle(stdout_handle);
138172d37d3SZachary Turner   if (stderr_handle)
139172d37d3SZachary Turner     ::CloseHandle(stderr_handle);
140172d37d3SZachary Turner 
141172d37d3SZachary Turner   if (!result)
1429d6fabf9SStella Stamenova     return HostProcess();
1439d6fabf9SStella Stamenova 
144172d37d3SZachary Turner   return HostProcess(pi.hProcess);
145172d37d3SZachary Turner }
146172d37d3SZachary Turner 
147172d37d3SZachary Turner HANDLE
GetStdioHandle(const ProcessLaunchInfo & launch_info,int fd)148b9c1b51eSKate Stone ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,
149b9c1b51eSKate Stone                                        int fd) {
150172d37d3SZachary Turner   const FileAction *action = launch_info.GetFileActionForFD(fd);
151172d37d3SZachary Turner   if (action == nullptr)
152172d37d3SZachary Turner     return NULL;
1535a8ad459SZachary Turner   SECURITY_ATTRIBUTES secattr = {};
154172d37d3SZachary Turner   secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
155172d37d3SZachary Turner   secattr.bInheritHandle = TRUE;
156172d37d3SZachary Turner 
15727a5c2b3SZachary Turner   llvm::StringRef path = action->GetPath();
158172d37d3SZachary Turner   DWORD access = 0;
159022c3c85SZachary Turner   DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
160172d37d3SZachary Turner   DWORD create = 0;
161172d37d3SZachary Turner   DWORD flags = 0;
162b9c1b51eSKate Stone   if (fd == STDIN_FILENO) {
163172d37d3SZachary Turner     access = GENERIC_READ;
164172d37d3SZachary Turner     create = OPEN_EXISTING;
165172d37d3SZachary Turner     flags = FILE_ATTRIBUTE_READONLY;
166172d37d3SZachary Turner   }
167b9c1b51eSKate Stone   if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
168172d37d3SZachary Turner     access = GENERIC_WRITE;
169172d37d3SZachary Turner     create = CREATE_ALWAYS;
170172d37d3SZachary Turner     if (fd == STDERR_FILENO)
171172d37d3SZachary Turner       flags = FILE_FLAG_WRITE_THROUGH;
172172d37d3SZachary Turner   }
173172d37d3SZachary Turner 
174190fadcdSZachary Turner   std::wstring wpath;
175190fadcdSZachary Turner   llvm::ConvertUTF8toWide(path, wpath);
176b9c1b51eSKate Stone   HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
177b9c1b51eSKate Stone                                 flags, NULL);
178172d37d3SZachary Turner   return (result == INVALID_HANDLE_VALUE) ? NULL : result;
179172d37d3SZachary Turner }
180