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   return true;
173 }
174 
175 llvm::Expected<HostThread> Host::StartMonitoringChildProcess(
176     const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid,
177     bool monitor_signals) {
178   return HostThread();
179 }
180 
181 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
182   Status error;
183   if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
184     FileSpec expand_tool_spec = HostInfo::GetSupportExeDir();
185     if (!expand_tool_spec) {
186       error.SetErrorString("could not find support executable directory for "
187                            "the lldb-argdumper tool");
188       return error;
189     }
190     expand_tool_spec.AppendPathComponent("lldb-argdumper.exe");
191     if (!FileSystem::Instance().Exists(expand_tool_spec)) {
192       error.SetErrorString("could not find the lldb-argdumper tool");
193       return error;
194     }
195 
196     std::string quoted_cmd_string;
197     launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string);
198     std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/');
199     StreamString expand_command;
200 
201     expand_command.Printf("\"%s\" %s", expand_tool_spec.GetPath().c_str(),
202                           quoted_cmd_string.c_str());
203 
204     int status;
205     std::string output;
206     std::string command = expand_command.GetString();
207     RunShellCommand(command.c_str(), launch_info.GetWorkingDirectory(), &status,
208                     nullptr, &output, std::chrono::seconds(10));
209 
210     if (status != 0) {
211       error.SetErrorStringWithFormat("lldb-argdumper exited with error %d",
212                                      status);
213       return error;
214     }
215 
216     auto data_sp = StructuredData::ParseJSON(output);
217     if (!data_sp) {
218       error.SetErrorString("invalid JSON");
219       return error;
220     }
221 
222     auto dict_sp = data_sp->GetAsDictionary();
223     if (!data_sp) {
224       error.SetErrorString("invalid JSON");
225       return error;
226     }
227 
228     auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
229     if (!args_sp) {
230       error.SetErrorString("invalid JSON");
231       return error;
232     }
233 
234     auto args_array_sp = args_sp->GetAsArray();
235     if (!args_array_sp) {
236       error.SetErrorString("invalid JSON");
237       return error;
238     }
239 
240     launch_info.GetArguments().Clear();
241 
242     for (size_t i = 0; i < args_array_sp->GetSize(); i++) {
243       auto item_sp = args_array_sp->GetItemAtIndex(i);
244       if (!item_sp)
245         continue;
246       auto str_sp = item_sp->GetAsString();
247       if (!str_sp)
248         continue;
249 
250       launch_info.GetArguments().AppendArgument(str_sp->GetValue());
251     }
252   }
253 
254   return error;
255 }
256 
257 Environment Host::GetEnvironment() {
258   Environment env;
259   // The environment block on Windows is a contiguous buffer of NULL terminated
260   // strings, where the end of the environment block is indicated by two
261   // consecutive NULLs.
262   LPWCH environment_block = ::GetEnvironmentStringsW();
263   while (*environment_block != L'\0') {
264     std::string current_var;
265     auto current_var_size = wcslen(environment_block) + 1;
266     if (!llvm::convertWideToUTF8(environment_block, current_var)) {
267       environment_block += current_var_size;
268       continue;
269     }
270     if (current_var[0] != '=')
271       env.insert(current_var);
272 
273     environment_block += current_var_size;
274   }
275   return env;
276 }
277