1 //===-- source/Host/windows/Host.cpp ----------------------------*- C++ -*-===// 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/AutoHandle.h" 10 #include "lldb/Host/windows/windows.h" 11 #include <stdio.h> 12 13 #include "lldb/Host/FileSystem.h" 14 #include "lldb/Host/Host.h" 15 #include "lldb/Host/HostInfo.h" 16 #include "lldb/Host/ProcessLaunchInfo.h" 17 #include "lldb/Utility/DataBufferHeap.h" 18 #include "lldb/Utility/DataExtractor.h" 19 #include "lldb/Utility/Log.h" 20 #include "lldb/Utility/ProcessInfo.h" 21 #include "lldb/Utility/Status.h" 22 #include "lldb/Utility/StreamString.h" 23 #include "lldb/Utility/StructuredData.h" 24 25 #include "llvm/Support/ConvertUTF.h" 26 27 // Windows includes 28 #include <tlhelp32.h> 29 30 using namespace lldb; 31 using namespace lldb_private; 32 33 namespace { 34 bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple) { 35 // Open the PE File as a binary file, and parse just enough information to 36 // determine the machine type. 37 File imageBinary; 38 FileSystem::Instance().Open(imageBinary, executable, File::eOpenOptionRead, 39 lldb::eFilePermissionsUserRead); 40 imageBinary.SeekFromStart(0x3c); 41 int32_t peOffset = 0; 42 uint32_t peHead = 0; 43 uint16_t machineType = 0; 44 size_t readSize = sizeof(peOffset); 45 imageBinary.Read(&peOffset, readSize); 46 imageBinary.SeekFromStart(peOffset); 47 imageBinary.Read(&peHead, readSize); 48 if (peHead != 0x00004550) // "PE\0\0", little-endian 49 return false; // Status: Can't find PE header 50 readSize = 2; 51 imageBinary.Read(&machineType, readSize); 52 triple.setVendor(llvm::Triple::PC); 53 triple.setOS(llvm::Triple::Win32); 54 triple.setArch(llvm::Triple::UnknownArch); 55 if (machineType == 0x8664) 56 triple.setArch(llvm::Triple::x86_64); 57 else if (machineType == 0x14c) 58 triple.setArch(llvm::Triple::x86); 59 60 return true; 61 } 62 63 bool GetExecutableForProcess(const AutoHandle &handle, std::string &path) { 64 // Get the process image path. MAX_PATH isn't long enough, paths can 65 // actually be up to 32KB. 66 std::vector<wchar_t> buffer(PATH_MAX); 67 DWORD dwSize = buffer.size(); 68 if (!::QueryFullProcessImageNameW(handle.get(), 0, &buffer[0], &dwSize)) 69 return false; 70 return llvm::convertWideToUTF8(buffer.data(), path); 71 } 72 73 void GetProcessExecutableAndTriple(const AutoHandle &handle, 74 ProcessInstanceInfo &process) { 75 // We may not have permissions to read the path from the process. So start 76 // off by setting the executable file to whatever Toolhelp32 gives us, and 77 // then try to enhance this with more detailed information, but fail 78 // gracefully. 79 std::string executable; 80 llvm::Triple triple; 81 triple.setVendor(llvm::Triple::PC); 82 triple.setOS(llvm::Triple::Win32); 83 triple.setArch(llvm::Triple::UnknownArch); 84 if (GetExecutableForProcess(handle, executable)) { 85 FileSpec executableFile(executable.c_str()); 86 process.SetExecutableFile(executableFile, true); 87 GetTripleForProcess(executableFile, triple); 88 } 89 process.SetArchitecture(ArchSpec(triple)); 90 91 // TODO(zturner): Add the ability to get the process user name. 92 } 93 } 94 95 lldb::thread_t Host::GetCurrentThread() { 96 return lldb::thread_t(::GetCurrentThread()); 97 } 98 99 void Host::Kill(lldb::pid_t pid, int signo) { 100 TerminateProcess((HANDLE)pid, 1); 101 } 102 103 const char *Host::GetSignalAsCString(int signo) { return NULL; } 104 105 FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { 106 FileSpec module_filespec; 107 108 HMODULE hmodule = NULL; 109 if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, 110 (LPCTSTR)host_addr, &hmodule)) 111 return module_filespec; 112 113 std::vector<wchar_t> buffer(PATH_MAX); 114 DWORD chars_copied = 0; 115 do { 116 chars_copied = ::GetModuleFileNameW(hmodule, &buffer[0], buffer.size()); 117 if (chars_copied == buffer.size() && 118 ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) 119 buffer.resize(buffer.size() * 2); 120 } while (chars_copied >= buffer.size()); 121 std::string path; 122 if (!llvm::convertWideToUTF8(buffer.data(), path)) 123 return module_filespec; 124 module_filespec.SetFile(path, FileSpec::Style::native); 125 return module_filespec; 126 } 127 128 uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info, 129 ProcessInstanceInfoList &process_infos) { 130 process_infos.Clear(); 131 132 AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); 133 if (!snapshot.IsValid()) 134 return 0; 135 136 PROCESSENTRY32W pe = {}; 137 pe.dwSize = sizeof(PROCESSENTRY32W); 138 if (Process32FirstW(snapshot.get(), &pe)) { 139 do { 140 AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, 141 pe.th32ProcessID), 142 nullptr); 143 144 ProcessInstanceInfo process; 145 std::string exeFile; 146 llvm::convertWideToUTF8(pe.szExeFile, exeFile); 147 process.SetExecutableFile(FileSpec(exeFile), true); 148 process.SetProcessID(pe.th32ProcessID); 149 process.SetParentProcessID(pe.th32ParentProcessID); 150 GetProcessExecutableAndTriple(handle, process); 151 152 if (match_info.MatchAllProcesses() || match_info.Matches(process)) 153 process_infos.Append(process); 154 } while (Process32NextW(snapshot.get(), &pe)); 155 } 156 return process_infos.GetSize(); 157 } 158 159 bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { 160 process_info.Clear(); 161 162 AutoHandle handle( 163 ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid), 164 nullptr); 165 if (!handle.IsValid()) 166 return false; 167 168 process_info.SetProcessID(pid); 169 GetProcessExecutableAndTriple(handle, process_info); 170 171 // Need to read the PEB to get parent process and command line arguments. 172 173 AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); 174 if (!snapshot.IsValid()) 175 return false; 176 177 PROCESSENTRY32W pe; 178 pe.dwSize = sizeof(PROCESSENTRY32W); 179 if (Process32FirstW(snapshot.get(), &pe)) { 180 do { 181 if (pe.th32ProcessID == pid) { 182 process_info.SetParentProcessID(pe.th32ParentProcessID); 183 return true; 184 } 185 } while (Process32NextW(snapshot.get(), &pe)); 186 } 187 188 return false; 189 } 190 191 llvm::Expected<HostThread> Host::StartMonitoringChildProcess( 192 const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid, 193 bool monitor_signals) { 194 return HostThread(); 195 } 196 197 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { 198 Status error; 199 if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) { 200 FileSpec expand_tool_spec = HostInfo::GetSupportExeDir(); 201 if (!expand_tool_spec) { 202 error.SetErrorString("could not find support executable directory for " 203 "the lldb-argdumper tool"); 204 return error; 205 } 206 expand_tool_spec.AppendPathComponent("lldb-argdumper.exe"); 207 if (!FileSystem::Instance().Exists(expand_tool_spec)) { 208 error.SetErrorString("could not find the lldb-argdumper tool"); 209 return error; 210 } 211 212 std::string quoted_cmd_string; 213 launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string); 214 std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/'); 215 StreamString expand_command; 216 217 expand_command.Printf("\"%s\" %s", expand_tool_spec.GetPath().c_str(), 218 quoted_cmd_string.c_str()); 219 220 int status; 221 std::string output; 222 std::string command = expand_command.GetString(); 223 RunShellCommand(command.c_str(), launch_info.GetWorkingDirectory(), &status, 224 nullptr, &output, std::chrono::seconds(10)); 225 226 if (status != 0) { 227 error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", 228 status); 229 return error; 230 } 231 232 auto data_sp = StructuredData::ParseJSON(output); 233 if (!data_sp) { 234 error.SetErrorString("invalid JSON"); 235 return error; 236 } 237 238 auto dict_sp = data_sp->GetAsDictionary(); 239 if (!data_sp) { 240 error.SetErrorString("invalid JSON"); 241 return error; 242 } 243 244 auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); 245 if (!args_sp) { 246 error.SetErrorString("invalid JSON"); 247 return error; 248 } 249 250 auto args_array_sp = args_sp->GetAsArray(); 251 if (!args_array_sp) { 252 error.SetErrorString("invalid JSON"); 253 return error; 254 } 255 256 launch_info.GetArguments().Clear(); 257 258 for (size_t i = 0; i < args_array_sp->GetSize(); i++) { 259 auto item_sp = args_array_sp->GetItemAtIndex(i); 260 if (!item_sp) 261 continue; 262 auto str_sp = item_sp->GetAsString(); 263 if (!str_sp) 264 continue; 265 266 launch_info.GetArguments().AppendArgument(str_sp->GetValue()); 267 } 268 } 269 270 return error; 271 } 272 273 Environment Host::GetEnvironment() { 274 Environment env; 275 // The environment block on Windows is a contiguous buffer of NULL terminated 276 // strings, where the end of the environment block is indicated by two 277 // consecutive NULLs. 278 LPWCH environment_block = ::GetEnvironmentStringsW(); 279 while (*environment_block != L'\0') { 280 std::string current_var; 281 auto current_var_size = wcslen(environment_block) + 1; 282 if (!llvm::convertWideToUTF8(environment_block, current_var)) { 283 environment_block += current_var_size; 284 continue; 285 } 286 if (current_var[0] != '=') 287 env.insert(current_var); 288 289 environment_block += current_var_size; 290 } 291 return env; 292 } 293