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 | |
20 | using namespace lldb; |
21 | using namespace lldb_private; |
22 | |
23 | static 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 | |
44 | static 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 | |
61 | HostProcess |
62 | ProcessLauncherWindows::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 | |
147 | HANDLE |
148 | ProcessLauncherWindows::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 | |