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