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