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
20using namespace lldb;
21using namespace lldb_private;
22
23static 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(Source: Environment::compose(KeyValue: KV), Result&: warg)) {
31 buffer.insert(
32 position: buffer.end(), first: reinterpret_cast<const char *>(warg.c_str()),
33 last: 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(x: 0);
38 buffer.push_back(x: 0);
39 // Insert extra two bytes, just in case the environment was empty.
40 buffer.push_back(x: 0);
41 buffer.push_back(x: 0);
42}
43
44static 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(x: 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
61HostProcess
62ProcessLauncherWindows::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(name: "LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
86 if (hide_console_var &&
87 llvm::StringRef(hide_console_var).equals_insensitive(RHS: "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(bit: eLaunchFlagDebug))
94 flags |= DEBUG_ONLY_THIS_PROCESS;
95
96 if (launch_info.GetFlags().Test(bit: eLaunchFlagDisableSTDIO))
97 flags &= ~CREATE_NEW_CONSOLE;
98
99 LPVOID env_block = nullptr;
100 ::CreateEnvironmentBuffer(env: launch_info.GetEnvironment(), buffer&: environment);
101 env_block = environment.data();
102
103 executable = launch_info.GetExecutableFile().GetPath();
104 std::wstring wcommandLine;
105 GetFlattenedWindowsCommandString(args: launch_info.GetArguments(), command&: wcommandLine);
106
107 std::wstring wexecutable, wworkingDirectory;
108 llvm::ConvertUTF8toWide(Source: executable, Result&: wexecutable);
109 llvm::ConvertUTF8toWide(Source: launch_info.GetWorkingDirectory().GetPath(),
110 Result&: 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(::err: GetLastError(), type: 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
147HANDLE
148ProcessLauncherWindows::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(Source: path, Result&: 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

source code of lldb/source/Host/windows/ProcessLauncherWindows.cpp