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