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