xref: /openbsd/gnu/llvm/lldb/source/Host/windows/Host.cpp (revision f6aab3d8)
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 
GetTripleForProcess(const FileSpec & executable,llvm::Triple & triple)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 
GetExecutableForProcess(const AutoHandle & handle,std::string & path)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 
GetProcessExecutableAndTriple(const AutoHandle & handle,ProcessInstanceInfo & process)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 
GetCurrentThread()101 lldb::thread_t Host::GetCurrentThread() {
102   return lldb::thread_t(::GetCurrentThread());
103 }
104 
Kill(lldb::pid_t pid,int signo)105 void Host::Kill(lldb::pid_t pid, int signo) {
106   TerminateProcess((HANDLE)pid, 1);
107 }
108 
GetSignalAsCString(int signo)109 const char *Host::GetSignalAsCString(int signo) { return NULL; }
110 
GetModuleFileSpecForHostAddress(const void * host_addr)111 FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) {
112   FileSpec module_filespec;
113 
114   HMODULE hmodule = NULL;
115   if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
116                            (LPCTSTR)host_addr, &hmodule))
117     return module_filespec;
118 
119   std::vector<wchar_t> buffer(PATH_MAX);
120   DWORD chars_copied = 0;
121   do {
122     chars_copied = ::GetModuleFileNameW(hmodule, &buffer[0], buffer.size());
123     if (chars_copied == buffer.size() &&
124         ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
125       buffer.resize(buffer.size() * 2);
126   } while (chars_copied >= buffer.size());
127   std::string path;
128   if (!llvm::convertWideToUTF8(buffer.data(), path))
129     return module_filespec;
130   module_filespec.SetFile(path, FileSpec::Style::native);
131   return module_filespec;
132 }
133 
FindProcessesImpl(const ProcessInstanceInfoMatch & match_info,ProcessInstanceInfoList & process_infos)134 uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
135                                  ProcessInstanceInfoList &process_infos) {
136   process_infos.clear();
137 
138   AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
139   if (!snapshot.IsValid())
140     return 0;
141 
142   PROCESSENTRY32W pe = {};
143   pe.dwSize = sizeof(PROCESSENTRY32W);
144   if (Process32FirstW(snapshot.get(), &pe)) {
145     do {
146       AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
147                                       pe.th32ProcessID),
148                         nullptr);
149 
150       ProcessInstanceInfo process;
151       std::string exeFile;
152       llvm::convertWideToUTF8(pe.szExeFile, exeFile);
153       process.SetExecutableFile(FileSpec(exeFile), true);
154       process.SetProcessID(pe.th32ProcessID);
155       process.SetParentProcessID(pe.th32ParentProcessID);
156       GetProcessExecutableAndTriple(handle, process);
157 
158       if (match_info.MatchAllProcesses() || match_info.Matches(process))
159         process_infos.push_back(process);
160     } while (Process32NextW(snapshot.get(), &pe));
161   }
162   return process_infos.size();
163 }
164 
GetProcessInfo(lldb::pid_t pid,ProcessInstanceInfo & process_info)165 bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
166   process_info.Clear();
167 
168   AutoHandle handle(
169       ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid),
170       nullptr);
171   if (!handle.IsValid())
172     return false;
173 
174   process_info.SetProcessID(pid);
175   GetProcessExecutableAndTriple(handle, process_info);
176 
177   // Need to read the PEB to get parent process and command line arguments.
178 
179   AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
180   if (!snapshot.IsValid())
181     return false;
182 
183   PROCESSENTRY32W pe;
184   pe.dwSize = sizeof(PROCESSENTRY32W);
185   if (Process32FirstW(snapshot.get(), &pe)) {
186     do {
187       if (pe.th32ProcessID == pid) {
188         process_info.SetParentProcessID(pe.th32ParentProcessID);
189         return true;
190       }
191     } while (Process32NextW(snapshot.get(), &pe));
192   }
193 
194   return false;
195 }
196 
StartMonitoringChildProcess(const Host::MonitorChildProcessCallback & callback,lldb::pid_t pid)197 llvm::Expected<HostThread> Host::StartMonitoringChildProcess(
198     const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) {
199   return HostThread();
200 }
201 
ShellExpandArguments(ProcessLaunchInfo & launch_info)202 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
203   Status error;
204   if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
205     FileSpec expand_tool_spec = HostInfo::GetSupportExeDir();
206     if (!expand_tool_spec) {
207       error.SetErrorString("could not find support executable directory for "
208                            "the lldb-argdumper tool");
209       return error;
210     }
211     expand_tool_spec.AppendPathComponent("lldb-argdumper.exe");
212     if (!FileSystem::Instance().Exists(expand_tool_spec)) {
213       error.SetErrorString("could not find the lldb-argdumper tool");
214       return error;
215     }
216 
217     std::string quoted_cmd_string;
218     launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string);
219     std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/');
220     StreamString expand_command;
221 
222     expand_command.Printf("\"%s\" %s", expand_tool_spec.GetPath().c_str(),
223                           quoted_cmd_string.c_str());
224 
225     int status;
226     std::string output;
227     std::string command = expand_command.GetString().str();
228     Status e =
229         RunShellCommand(command.c_str(), launch_info.GetWorkingDirectory(),
230                         &status, nullptr, &output, std::chrono::seconds(10));
231 
232     if (e.Fail())
233       return e;
234 
235     if (status != 0) {
236       error.SetErrorStringWithFormat("lldb-argdumper exited with error %d",
237                                      status);
238       return error;
239     }
240 
241     auto data_sp = StructuredData::ParseJSON(output);
242     if (!data_sp) {
243       error.SetErrorString("invalid JSON");
244       return error;
245     }
246 
247     auto dict_sp = data_sp->GetAsDictionary();
248     if (!dict_sp) {
249       error.SetErrorString("invalid JSON");
250       return error;
251     }
252 
253     auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
254     if (!args_sp) {
255       error.SetErrorString("invalid JSON");
256       return error;
257     }
258 
259     auto args_array_sp = args_sp->GetAsArray();
260     if (!args_array_sp) {
261       error.SetErrorString("invalid JSON");
262       return error;
263     }
264 
265     launch_info.GetArguments().Clear();
266 
267     for (size_t i = 0; i < args_array_sp->GetSize(); i++) {
268       auto item_sp = args_array_sp->GetItemAtIndex(i);
269       if (!item_sp)
270         continue;
271       auto str_sp = item_sp->GetAsString();
272       if (!str_sp)
273         continue;
274 
275       launch_info.GetArguments().AppendArgument(str_sp->GetValue());
276     }
277   }
278 
279   return error;
280 }
281 
GetEnvironment()282 Environment Host::GetEnvironment() {
283   Environment env;
284   // The environment block on Windows is a contiguous buffer of NULL terminated
285   // strings, where the end of the environment block is indicated by two
286   // consecutive NULLs.
287   LPWCH environment_block = ::GetEnvironmentStringsW();
288   while (*environment_block != L'\0') {
289     std::string current_var;
290     auto current_var_size = wcslen(environment_block) + 1;
291     if (!llvm::convertWideToUTF8(environment_block, current_var)) {
292       environment_block += current_var_size;
293       continue;
294     }
295     if (current_var[0] != '=')
296       env.insert(current_var);
297 
298     environment_block += current_var_size;
299   }
300   return env;
301 }
302