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