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