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