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