251 lines · cpp
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-exception6//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/ScopeExit.h"14#include "llvm/ADT/SmallVector.h"15#include "llvm/Support/ConvertUTF.h"16#include "llvm/Support/Program.h"17 18#include <string>19#include <vector>20 21using namespace lldb;22using namespace lldb_private;23 24/// Create a UTF-16 environment block to use with CreateProcessW.25///26/// The buffer is a sequence of null-terminated UTF-16 strings, followed by an27/// extra L'\0' (two bytes of 0). An empty environment must have one28/// empty string, followed by an extra L'\0'.29///30/// The keys are sorted to comply with the CreateProcess API calling convention.31///32/// Ensure that the resulting buffer is used in conjunction with33/// CreateProcessW and be sure that dwCreationFlags includes34/// CREATE_UNICODE_ENVIRONMENT.35///36/// \param env The Environment object to convert.37/// \returns The sorted sequence of environment variables and their values,38/// separated by null terminators. The vector is guaranteed to never be empty.39static std::vector<wchar_t> CreateEnvironmentBufferW(const Environment &env) {40 std::vector<std::wstring> env_entries;41 for (const auto &KV : env) {42 std::wstring wentry;43 if (llvm::ConvertUTF8toWide(Environment::compose(KV), wentry))44 env_entries.push_back(std::move(wentry));45 }46 std::sort(env_entries.begin(), env_entries.end(),47 [](const std::wstring &a, const std::wstring &b) {48 return _wcsicmp(a.c_str(), b.c_str()) < 0;49 });50 51 std::vector<wchar_t> buffer;52 for (const auto &env_entry : env_entries) {53 buffer.insert(buffer.end(), env_entry.begin(), env_entry.end());54 buffer.push_back(L'\0');55 }56 57 if (buffer.empty())58 buffer.push_back(L'\0'); // If there are no environment variables, we have59 // to ensure there are 4 zero bytes in the buffer.60 buffer.push_back(L'\0');61 62 return buffer;63}64 65/// Flattens an Args object into a Windows command-line wide string.66///67/// Returns an empty string if args is empty.68///69/// \param args The Args object to flatten.70/// \returns A wide string containing the flattened command line.71static llvm::ErrorOr<std::wstring>72GetFlattenedWindowsCommandStringW(Args args) {73 if (args.empty())74 return L"";75 76 std::vector<llvm::StringRef> args_ref;77 for (auto &entry : args.entries())78 args_ref.push_back(entry.ref());79 80 return llvm::sys::flattenWindowsCommandLine(args_ref);81}82 83HostProcess84ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,85 Status &error) {86 error.Clear();87 88 std::string executable;89 STARTUPINFOEXW startupinfoex = {};90 STARTUPINFOW &startupinfo = startupinfoex.StartupInfo;91 PROCESS_INFORMATION pi = {};92 93 HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);94 HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);95 HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);96 auto close_handles = llvm::make_scope_exit([&] {97 if (stdin_handle)98 ::CloseHandle(stdin_handle);99 if (stdout_handle)100 ::CloseHandle(stdout_handle);101 if (stderr_handle)102 ::CloseHandle(stderr_handle);103 });104 105 startupinfo.cb = sizeof(startupinfoex);106 startupinfo.dwFlags |= STARTF_USESTDHANDLES;107 startupinfo.hStdError =108 stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);109 startupinfo.hStdInput =110 stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);111 startupinfo.hStdOutput =112 stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);113 114 std::vector<HANDLE> inherited_handles;115 if (startupinfo.hStdError)116 inherited_handles.push_back(startupinfo.hStdError);117 if (startupinfo.hStdInput)118 inherited_handles.push_back(startupinfo.hStdInput);119 if (startupinfo.hStdOutput)120 inherited_handles.push_back(startupinfo.hStdOutput);121 122 SIZE_T attributelist_size = 0;123 InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr,124 /*dwAttributeCount=*/1, /*dwFlags=*/0,125 &attributelist_size);126 127 startupinfoex.lpAttributeList =128 static_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attributelist_size));129 auto free_attributelist =130 llvm::make_scope_exit([&] { free(startupinfoex.lpAttributeList); });131 if (!InitializeProcThreadAttributeList(startupinfoex.lpAttributeList,132 /*dwAttributeCount=*/1, /*dwFlags=*/0,133 &attributelist_size)) {134 error = Status(::GetLastError(), eErrorTypeWin32);135 return HostProcess();136 }137 auto delete_attributelist = llvm::make_scope_exit(138 [&] { DeleteProcThreadAttributeList(startupinfoex.lpAttributeList); });139 for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) {140 const FileAction *act = launch_info.GetFileActionAtIndex(i);141 if (act->GetAction() == FileAction::eFileActionDuplicate &&142 act->GetFD() == act->GetActionArgument())143 inherited_handles.push_back(reinterpret_cast<HANDLE>(act->GetFD()));144 }145 if (!inherited_handles.empty()) {146 if (!UpdateProcThreadAttribute(147 startupinfoex.lpAttributeList, /*dwFlags=*/0,148 PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(),149 inherited_handles.size() * sizeof(HANDLE),150 /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) {151 error = Status(::GetLastError(), eErrorTypeWin32);152 return HostProcess();153 }154 }155 156 const char *hide_console_var =157 getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");158 if (hide_console_var &&159 llvm::StringRef(hide_console_var).equals_insensitive("true")) {160 startupinfo.dwFlags |= STARTF_USESHOWWINDOW;161 startupinfo.wShowWindow = SW_HIDE;162 }163 164 DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT |165 EXTENDED_STARTUPINFO_PRESENT;166 if (launch_info.GetFlags().Test(eLaunchFlagDebug))167 flags |= DEBUG_ONLY_THIS_PROCESS;168 169 if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO))170 flags &= ~CREATE_NEW_CONSOLE;171 172 std::vector<wchar_t> environment =173 CreateEnvironmentBufferW(launch_info.GetEnvironment());174 175 auto wcommandLineOrErr =176 GetFlattenedWindowsCommandStringW(launch_info.GetArguments());177 if (!wcommandLineOrErr) {178 error = Status(wcommandLineOrErr.getError());179 return HostProcess();180 }181 std::wstring wcommandLine = *wcommandLineOrErr;182 // If the command line is empty, it's best to pass a null pointer to tell183 // CreateProcessW to use the executable name as the command line. If the184 // command line is not empty, its contents may be modified by CreateProcessW.185 WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0];186 187 std::wstring wexecutable, wworkingDirectory;188 llvm::ConvertUTF8toWide(launch_info.GetExecutableFile().GetPath(),189 wexecutable);190 llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(),191 wworkingDirectory);192 193 BOOL result = ::CreateProcessW(194 wexecutable.c_str(), pwcommandLine, NULL, NULL,195 /*bInheritHandles=*/!inherited_handles.empty(), flags, environment.data(),196 wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),197 reinterpret_cast<STARTUPINFOW *>(&startupinfoex), &pi);198 199 if (!result) {200 // Call GetLastError before we make any other system calls.201 error = Status(::GetLastError(), eErrorTypeWin32);202 // Note that error 50 ("The request is not supported") will occur if you203 // try debug a 64-bit inferior from a 32-bit LLDB.204 }205 206 if (result) {207 // Do not call CloseHandle on pi.hProcess, since we want to pass that back208 // through the HostProcess.209 ::CloseHandle(pi.hThread);210 }211 212 if (!result)213 return HostProcess();214 215 return HostProcess(pi.hProcess);216}217 218HANDLE219ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,220 int fd) {221 const FileAction *action = launch_info.GetFileActionForFD(fd);222 if (action == nullptr)223 return NULL;224 SECURITY_ATTRIBUTES secattr = {};225 secattr.nLength = sizeof(SECURITY_ATTRIBUTES);226 secattr.bInheritHandle = TRUE;227 228 llvm::StringRef path = action->GetPath();229 DWORD access = 0;230 DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;231 DWORD create = 0;232 DWORD flags = 0;233 if (fd == STDIN_FILENO) {234 access = GENERIC_READ;235 create = OPEN_EXISTING;236 flags = FILE_ATTRIBUTE_READONLY;237 }238 if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {239 access = GENERIC_WRITE;240 create = CREATE_ALWAYS;241 if (fd == STDERR_FILENO)242 flags = FILE_FLAG_WRITE_THROUGH;243 }244 245 std::wstring wpath;246 llvm::ConvertUTF8toWide(path, wpath);247 HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,248 flags, NULL);249 return (result == INVALID_HANDLE_VALUE) ? NULL : result;250}251