xref: /openbsd-src/gnu/llvm/lldb/source/Host/windows/ProcessLauncherWindows.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- ProcessLauncherWindows.cpp ----------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Host/windows/ProcessLauncherWindows.h"
10061da546Spatrick #include "lldb/Host/HostProcess.h"
11061da546Spatrick #include "lldb/Host/ProcessLaunchInfo.h"
12061da546Spatrick 
13061da546Spatrick #include "llvm/ADT/SmallVector.h"
14061da546Spatrick #include "llvm/Support/ConvertUTF.h"
15061da546Spatrick #include "llvm/Support/Program.h"
16061da546Spatrick 
17061da546Spatrick #include <string>
18061da546Spatrick #include <vector>
19061da546Spatrick 
20061da546Spatrick using namespace lldb;
21061da546Spatrick using namespace lldb_private;
22061da546Spatrick 
CreateEnvironmentBuffer(const Environment & env,std::vector<char> & buffer)23*f6aab3d8Srobert static void CreateEnvironmentBuffer(const Environment &env,
24061da546Spatrick                                     std::vector<char> &buffer) {
25dda28197Spatrick   // The buffer is a list of null-terminated UTF-16 strings, followed by an
26dda28197Spatrick   // extra L'\0' (two bytes of 0).  An empty environment must have one
27dda28197Spatrick   // empty string, followed by an extra L'\0'.
28061da546Spatrick   for (const auto &KV : env) {
29061da546Spatrick     std::wstring warg;
30061da546Spatrick     if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) {
31061da546Spatrick       buffer.insert(
32061da546Spatrick           buffer.end(), reinterpret_cast<const char *>(warg.c_str()),
33061da546Spatrick           reinterpret_cast<const char *>(warg.c_str() + warg.size() + 1));
34061da546Spatrick     }
35061da546Spatrick   }
36061da546Spatrick   // One null wchar_t (to end the block) is two null bytes
37061da546Spatrick   buffer.push_back(0);
38061da546Spatrick   buffer.push_back(0);
39dda28197Spatrick   // Insert extra two bytes, just in case the environment was empty.
40dda28197Spatrick   buffer.push_back(0);
41dda28197Spatrick   buffer.push_back(0);
42061da546Spatrick }
43061da546Spatrick 
GetFlattenedWindowsCommandString(Args args,std::wstring & command)44*f6aab3d8Srobert static bool GetFlattenedWindowsCommandString(Args args, std::wstring &command) {
45061da546Spatrick   if (args.empty())
46061da546Spatrick     return false;
47061da546Spatrick 
48061da546Spatrick   std::vector<llvm::StringRef> args_ref;
49061da546Spatrick   for (auto &entry : args.entries())
50061da546Spatrick     args_ref.push_back(entry.ref());
51061da546Spatrick 
52be691f3bSpatrick   llvm::ErrorOr<std::wstring> result =
53be691f3bSpatrick       llvm::sys::flattenWindowsCommandLine(args_ref);
54be691f3bSpatrick   if (result.getError())
55be691f3bSpatrick     return false;
56be691f3bSpatrick 
57be691f3bSpatrick   command = *result;
58061da546Spatrick   return true;
59061da546Spatrick }
60061da546Spatrick 
61061da546Spatrick HostProcess
LaunchProcess(const ProcessLaunchInfo & launch_info,Status & error)62061da546Spatrick ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
63061da546Spatrick                                       Status &error) {
64061da546Spatrick   error.Clear();
65061da546Spatrick 
66061da546Spatrick   std::string executable;
67061da546Spatrick   std::vector<char> environment;
68061da546Spatrick   STARTUPINFO startupinfo = {};
69061da546Spatrick   PROCESS_INFORMATION pi = {};
70061da546Spatrick 
71061da546Spatrick   HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
72061da546Spatrick   HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
73061da546Spatrick   HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
74061da546Spatrick 
75061da546Spatrick   startupinfo.cb = sizeof(startupinfo);
76061da546Spatrick   startupinfo.dwFlags |= STARTF_USESTDHANDLES;
77061da546Spatrick   startupinfo.hStdError =
78061da546Spatrick       stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
79061da546Spatrick   startupinfo.hStdInput =
80061da546Spatrick       stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
81061da546Spatrick   startupinfo.hStdOutput =
82061da546Spatrick       stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
83061da546Spatrick 
84061da546Spatrick   const char *hide_console_var =
85061da546Spatrick       getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
86061da546Spatrick   if (hide_console_var &&
87be691f3bSpatrick       llvm::StringRef(hide_console_var).equals_insensitive("true")) {
88061da546Spatrick     startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
89061da546Spatrick     startupinfo.wShowWindow = SW_HIDE;
90061da546Spatrick   }
91061da546Spatrick 
92061da546Spatrick   DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
93061da546Spatrick   if (launch_info.GetFlags().Test(eLaunchFlagDebug))
94061da546Spatrick     flags |= DEBUG_ONLY_THIS_PROCESS;
95061da546Spatrick 
96061da546Spatrick   if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO))
97061da546Spatrick     flags &= ~CREATE_NEW_CONSOLE;
98061da546Spatrick 
99061da546Spatrick   LPVOID env_block = nullptr;
100061da546Spatrick   ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment);
101061da546Spatrick   env_block = environment.data();
102061da546Spatrick 
103061da546Spatrick   executable = launch_info.GetExecutableFile().GetPath();
104be691f3bSpatrick   std::wstring wcommandLine;
105be691f3bSpatrick   GetFlattenedWindowsCommandString(launch_info.GetArguments(), wcommandLine);
106061da546Spatrick 
107be691f3bSpatrick   std::wstring wexecutable, wworkingDirectory;
108061da546Spatrick   llvm::ConvertUTF8toWide(executable, wexecutable);
109*f6aab3d8Srobert   llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(),
110061da546Spatrick                           wworkingDirectory);
111061da546Spatrick   // If the command line is empty, it's best to pass a null pointer to tell
112061da546Spatrick   // CreateProcessW to use the executable name as the command line.  If the
113061da546Spatrick   // command line is not empty, its contents may be modified by CreateProcessW.
114061da546Spatrick   WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];
115061da546Spatrick 
116061da546Spatrick   BOOL result = ::CreateProcessW(
117061da546Spatrick       wexecutable.c_str(), pwcommandLine, NULL, NULL, TRUE, flags, env_block,
118061da546Spatrick       wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
119061da546Spatrick       &startupinfo, &pi);
120061da546Spatrick 
121061da546Spatrick   if (!result) {
122061da546Spatrick     // Call GetLastError before we make any other system calls.
123061da546Spatrick     error.SetError(::GetLastError(), eErrorTypeWin32);
124061da546Spatrick     // Note that error 50 ("The request is not supported") will occur if you
125061da546Spatrick     // try debug a 64-bit inferior from a 32-bit LLDB.
126061da546Spatrick   }
127061da546Spatrick 
128061da546Spatrick   if (result) {
129061da546Spatrick     // Do not call CloseHandle on pi.hProcess, since we want to pass that back
130061da546Spatrick     // through the HostProcess.
131061da546Spatrick     ::CloseHandle(pi.hThread);
132061da546Spatrick   }
133061da546Spatrick 
134061da546Spatrick   if (stdin_handle)
135061da546Spatrick     ::CloseHandle(stdin_handle);
136061da546Spatrick   if (stdout_handle)
137061da546Spatrick     ::CloseHandle(stdout_handle);
138061da546Spatrick   if (stderr_handle)
139061da546Spatrick     ::CloseHandle(stderr_handle);
140061da546Spatrick 
141061da546Spatrick   if (!result)
142061da546Spatrick     return HostProcess();
143061da546Spatrick 
144061da546Spatrick   return HostProcess(pi.hProcess);
145061da546Spatrick }
146061da546Spatrick 
147061da546Spatrick HANDLE
GetStdioHandle(const ProcessLaunchInfo & launch_info,int fd)148061da546Spatrick ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,
149061da546Spatrick                                        int fd) {
150061da546Spatrick   const FileAction *action = launch_info.GetFileActionForFD(fd);
151061da546Spatrick   if (action == nullptr)
152061da546Spatrick     return NULL;
153061da546Spatrick   SECURITY_ATTRIBUTES secattr = {};
154061da546Spatrick   secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
155061da546Spatrick   secattr.bInheritHandle = TRUE;
156061da546Spatrick 
157061da546Spatrick   llvm::StringRef path = action->GetPath();
158061da546Spatrick   DWORD access = 0;
159061da546Spatrick   DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
160061da546Spatrick   DWORD create = 0;
161061da546Spatrick   DWORD flags = 0;
162061da546Spatrick   if (fd == STDIN_FILENO) {
163061da546Spatrick     access = GENERIC_READ;
164061da546Spatrick     create = OPEN_EXISTING;
165061da546Spatrick     flags = FILE_ATTRIBUTE_READONLY;
166061da546Spatrick   }
167061da546Spatrick   if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
168061da546Spatrick     access = GENERIC_WRITE;
169061da546Spatrick     create = CREATE_ALWAYS;
170061da546Spatrick     if (fd == STDERR_FILENO)
171061da546Spatrick       flags = FILE_FLAG_WRITE_THROUGH;
172061da546Spatrick   }
173061da546Spatrick 
174061da546Spatrick   std::wstring wpath;
175061da546Spatrick   llvm::ConvertUTF8toWide(path, wpath);
176061da546Spatrick   HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
177061da546Spatrick                                 flags, NULL);
178061da546Spatrick   return (result == INVALID_HANDLE_VALUE) ? NULL : result;
179061da546Spatrick }
180