brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.1 KiB · d62d8db Raw
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