1414#include " llvm/ADT/SmallVector.h"
1515#include " llvm/Support/ConvertUTF.h"
1616#include " llvm/Support/Program.h"
17+ #include " llvm/Support/WindowsError.h"
1718
1819#include < string>
1920#include < vector>
2021
2122using namespace lldb ;
2223using namespace lldb_private ;
2324
24- static void CreateEnvironmentBuffer (const Environment &env,
25- std::vector<char > &buffer) {
26- // The buffer is a list of null-terminated UTF-16 strings, followed by an
27- // extra L'\0' (two bytes of 0). An empty environment must have one
28- // empty string, followed by an extra L'\0'.
25+ // / Create a UTF-16 environment block to use with CreateProcessW.
26+ // /
27+ // / The buffer is a sequence of null-terminated UTF-16 strings, followed by an
28+ // / extra L'\0' (two bytes of 0). An empty environment must have one
29+ // / empty string, followed by an extra L'\0'.
30+ // /
31+ // / The keys are sorted to comply with the CreateProcess API calling convention.
32+ // /
33+ // / Ensure that the resulting buffer is used in conjunction with
34+ // / CreateProcessW and be sure that dwCreationFlags includes
35+ // / CREATE_UNICODE_ENVIRONMENT.
36+ // /
37+ // / \param env The Environment object to convert.
38+ // / \returns The sorted sequence of environment variables and their values,
39+ // / separated by null terminators. The vector is guaranteed to never be empty.
40+ static std::vector<wchar_t > CreateEnvironmentBufferW (const Environment &env) {
41+ std::vector<std::wstring> env_entries;
2942 for (const auto &KV : env) {
30- std::wstring warg;
31- if (llvm::ConvertUTF8toWide (Environment::compose (KV), warg)) {
32- buffer.insert (
33- buffer.end (), reinterpret_cast <const char *>(warg.c_str ()),
34- reinterpret_cast <const char *>(warg.c_str () + warg.size () + 1 ));
35- }
43+ std::wstring wentry;
44+ if (llvm::ConvertUTF8toWide (Environment::compose (KV), wentry))
45+ env_entries.push_back (std::move (wentry));
3646 }
37- // One null wchar_t (to end the block) is two null bytes
38- buffer.push_back (0 );
39- buffer.push_back (0 );
40- // Insert extra two bytes, just in case the environment was empty.
41- buffer.push_back (0 );
42- buffer.push_back (0 );
47+ std::sort (env_entries.begin (), env_entries.end (),
48+ [](const std::wstring &a, const std::wstring &b) {
49+ return _wcsicmp (a.c_str (), b.c_str ()) < 0 ;
50+ });
51+
52+ std::vector<wchar_t > buffer;
53+ for (const auto &env_entry : env_entries) {
54+ buffer.insert (buffer.end (), env_entry.begin (), env_entry.end ());
55+ buffer.push_back (L' \0 ' );
56+ }
57+
58+ if (buffer.empty ())
59+ buffer.push_back (L' \0 ' ); // If there are no environment variables, we have
60+ // to ensure there are 4 zero bytes in the buffer.
61+ buffer.push_back (L' \0 ' );
62+
63+ return buffer;
4364}
4465
45- static bool GetFlattenedWindowsCommandString (Args args, std::wstring &command) {
66+ // / Flattens an Args object into a Windows command-line wide string.
67+ // /
68+ // / Returns an empty string if args is empty.
69+ // /
70+ // / \param args The Args object to flatten.
71+ // / \returns A wide string containing the flattened command line.
72+ static llvm::ErrorOr<std::wstring>
73+ GetFlattenedWindowsCommandStringW (Args args) {
4674 if (args.empty ())
47- return false ;
75+ return L" " ;
4876
4977 std::vector<llvm::StringRef> args_ref;
5078 for (auto &entry : args.entries ())
5179 args_ref.push_back (entry.ref ());
5280
53- llvm::ErrorOr<std::wstring> result =
54- llvm::sys::flattenWindowsCommandLine (args_ref);
55- if (result.getError ())
56- return false ;
57-
58- command = *result;
59- return true ;
81+ return llvm::sys::flattenWindowsCommandLine (args_ref);
6082}
6183
6284HostProcess
6385ProcessLauncherWindows::LaunchProcess (const ProcessLaunchInfo &launch_info,
6486 Status &error) {
6587 error.Clear ();
6688
67- std::string executable;
68- std::vector<char > environment;
69- STARTUPINFOEX startupinfoex = {};
70- STARTUPINFO &startupinfo = startupinfoex.StartupInfo ;
71- PROCESS_INFORMATION pi = {};
89+ STARTUPINFOEXW startupinfoex = {};
90+ startupinfoex.StartupInfo .cb = sizeof (startupinfoex);
91+ startupinfoex.StartupInfo .dwFlags |= STARTF_USESTDHANDLES;
7292
7393 HANDLE stdin_handle = GetStdioHandle (launch_info, STDIN_FILENO);
7494 HANDLE stdout_handle = GetStdioHandle (launch_info, STDOUT_FILENO);
@@ -82,23 +102,6 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
82102 ::CloseHandle (stderr_handle);
83103 });
84104
85- startupinfo.cb = sizeof (startupinfoex);
86- startupinfo.dwFlags |= STARTF_USESTDHANDLES;
87- startupinfo.hStdError =
88- stderr_handle ? stderr_handle : ::GetStdHandle (STD_ERROR_HANDLE);
89- startupinfo.hStdInput =
90- stdin_handle ? stdin_handle : ::GetStdHandle (STD_INPUT_HANDLE);
91- startupinfo.hStdOutput =
92- stdout_handle ? stdout_handle : ::GetStdHandle (STD_OUTPUT_HANDLE);
93-
94- std::vector<HANDLE> inherited_handles;
95- if (startupinfo.hStdError )
96- inherited_handles.push_back (startupinfo.hStdError );
97- if (startupinfo.hStdInput )
98- inherited_handles.push_back (startupinfo.hStdInput );
99- if (startupinfo.hStdOutput )
100- inherited_handles.push_back (startupinfo.hStdOutput );
101-
102105 SIZE_T attributelist_size = 0 ;
103106 InitializeProcThreadAttributeList (/* lpAttributeList=*/ nullptr ,
104107 /* dwAttributeCount=*/ 1 , /* dwFlags=*/ 0 ,
@@ -116,29 +119,21 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
116119 }
117120 auto delete_attributelist = llvm::make_scope_exit (
118121 [&] { DeleteProcThreadAttributeList (startupinfoex.lpAttributeList ); });
119- for (size_t i = 0 ; i < launch_info.GetNumFileActions (); ++i) {
120- const FileAction *act = launch_info.GetFileActionAtIndex (i);
121- if (act->GetAction () == FileAction::eFileActionDuplicate &&
122- act->GetFD () == act->GetActionArgument ())
123- inherited_handles.push_back (reinterpret_cast <HANDLE>(act->GetFD ()));
124- }
125- if (!inherited_handles.empty ()) {
126- if (!UpdateProcThreadAttribute (
127- startupinfoex.lpAttributeList , /* dwFlags=*/ 0 ,
128- PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data (),
129- inherited_handles.size () * sizeof (HANDLE),
130- /* lpPreviousValue=*/ nullptr , /* lpReturnSize=*/ nullptr )) {
131- error = Status (::GetLastError (), eErrorTypeWin32);
132- return HostProcess ();
133- }
122+
123+ auto inherited_handles_or_err = GetInheritedHandles (
124+ launch_info, startupinfoex, stdout_handle, stderr_handle, stdin_handle);
125+ if (!inherited_handles_or_err) {
126+ error = Status (inherited_handles_or_err.getError ());
127+ return HostProcess ();
134128 }
129+ std::vector<HANDLE> inherited_handles = *inherited_handles_or_err;
135130
136131 const char *hide_console_var =
137132 getenv (" LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE" );
138133 if (hide_console_var &&
139134 llvm::StringRef (hide_console_var).equals_insensitive (" true" )) {
140- startupinfo .dwFlags |= STARTF_USESHOWWINDOW;
141- startupinfo .wShowWindow = SW_HIDE;
135+ startupinfoex. StartupInfo .dwFlags |= STARTF_USESHOWWINDOW;
136+ startupinfoex. StartupInfo .wShowWindow = SW_HIDE;
142137 }
143138
144139 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT |
@@ -149,28 +144,34 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
149144 if (launch_info.GetFlags ().Test (eLaunchFlagDisableSTDIO))
150145 flags &= ~CREATE_NEW_CONSOLE;
151146
152- LPVOID env_block = nullptr ;
153- ::CreateEnvironmentBuffer (launch_info.GetEnvironment(), environment);
154- env_block = environment.data ();
147+ std::vector<wchar_t > environment =
148+ CreateEnvironmentBufferW (launch_info.GetEnvironment ());
155149
156- executable = launch_info.GetExecutableFile ().GetPath ();
157- std::wstring wcommandLine;
158- GetFlattenedWindowsCommandString (launch_info.GetArguments (), wcommandLine);
159-
160- std::wstring wexecutable, wworkingDirectory;
161- llvm::ConvertUTF8toWide (executable, wexecutable);
162- llvm::ConvertUTF8toWide (launch_info.GetWorkingDirectory ().GetPath (),
163- wworkingDirectory);
150+ auto wcommandLineOrErr =
151+ GetFlattenedWindowsCommandStringW (launch_info.GetArguments ());
152+ if (!wcommandLineOrErr) {
153+ error = Status (wcommandLineOrErr.getError ());
154+ return HostProcess ();
155+ }
156+ std::wstring wcommandLine = *wcommandLineOrErr;
164157 // If the command line is empty, it's best to pass a null pointer to tell
165158 // CreateProcessW to use the executable name as the command line. If the
166159 // command line is not empty, its contents may be modified by CreateProcessW.
167160 WCHAR *pwcommandLine = wcommandLine.empty () ? nullptr : &wcommandLine[0 ];
168161
162+ std::wstring wexecutable, wworkingDirectory;
163+ llvm::ConvertUTF8toWide (launch_info.GetExecutableFile ().GetPath (),
164+ wexecutable);
165+ llvm::ConvertUTF8toWide (launch_info.GetWorkingDirectory ().GetPath (),
166+ wworkingDirectory);
167+
168+ PROCESS_INFORMATION pi = {};
169+
169170 BOOL result = ::CreateProcessW (
170171 wexecutable.c_str (), pwcommandLine, NULL , NULL ,
171- /* bInheritHandles=*/ !inherited_handles.empty (), flags, env_block ,
172+ /* bInheritHandles=*/ !inherited_handles.empty (), flags, environment. data () ,
172173 wworkingDirectory.size () == 0 ? NULL : wworkingDirectory.c_str (),
173- reinterpret_cast <STARTUPINFO *>(&startupinfoex), &pi);
174+ reinterpret_cast <STARTUPINFOW *>(&startupinfoex), &pi);
174175
175176 if (!result) {
176177 // Call GetLastError before we make any other system calls.
@@ -191,6 +192,45 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
191192 return HostProcess (pi.hProcess );
192193}
193194
195+ llvm::ErrorOr<std::vector<HANDLE>> ProcessLauncherWindows::GetInheritedHandles (
196+ const ProcessLaunchInfo &launch_info, STARTUPINFOEXW &startupinfoex,
197+ HANDLE stdout_handle, HANDLE stderr_handle, HANDLE stdin_handle) {
198+ std::vector<HANDLE> inherited_handles;
199+
200+ startupinfoex.StartupInfo .hStdError =
201+ stderr_handle ? stderr_handle : GetStdHandle (STD_ERROR_HANDLE);
202+ startupinfoex.StartupInfo .hStdInput =
203+ stdin_handle ? stdin_handle : GetStdHandle (STD_INPUT_HANDLE);
204+ startupinfoex.StartupInfo .hStdOutput =
205+ stdout_handle ? stdout_handle : GetStdHandle (STD_OUTPUT_HANDLE);
206+
207+ if (startupinfoex.StartupInfo .hStdError )
208+ inherited_handles.push_back (startupinfoex.StartupInfo .hStdError );
209+ if (startupinfoex.StartupInfo .hStdInput )
210+ inherited_handles.push_back (startupinfoex.StartupInfo .hStdInput );
211+ if (startupinfoex.StartupInfo .hStdOutput )
212+ inherited_handles.push_back (startupinfoex.StartupInfo .hStdOutput );
213+
214+ for (size_t i = 0 ; i < launch_info.GetNumFileActions (); ++i) {
215+ const FileAction *act = launch_info.GetFileActionAtIndex (i);
216+ if (act->GetAction () == FileAction::eFileActionDuplicate &&
217+ act->GetFD () == act->GetActionArgument ())
218+ inherited_handles.push_back (reinterpret_cast <HANDLE>(act->GetFD ()));
219+ }
220+
221+ if (inherited_handles.empty ())
222+ return inherited_handles;
223+
224+ if (!UpdateProcThreadAttribute (
225+ startupinfoex.lpAttributeList , /* dwFlags=*/ 0 ,
226+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data (),
227+ inherited_handles.size () * sizeof (HANDLE),
228+ /* lpPreviousValue=*/ nullptr , /* lpReturnSize=*/ nullptr ))
229+ return llvm::mapWindowsError (::GetLastError ());
230+
231+ return inherited_handles;
232+ }
233+
194234HANDLE
195235ProcessLauncherWindows::GetStdioHandle (const ProcessLaunchInfo &launch_info,
196236 int fd) {
0 commit comments