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