1 //===-- PlatformWindows.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 "PlatformWindows.h"
10 
11 #include <stdio.h>
12 #if defined(_WIN32)
13 #include "lldb/Host/windows/windows.h"
14 #include <winsock2.h>
15 #endif
16 
17 #include "lldb/Breakpoint/BreakpointLocation.h"
18 #include "lldb/Breakpoint/BreakpointSite.h"
19 #include "lldb/Core/Debugger.h"
20 #include "lldb/Core/Module.h"
21 #include "lldb/Core/ModuleSpec.h"
22 #include "lldb/Core/PluginManager.h"
23 #include "lldb/Host/HostInfo.h"
24 #include "lldb/Target/Process.h"
25 #include "lldb/Utility/Status.h"
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 
30 LLDB_PLUGIN_DEFINE(PlatformWindows)
31 
32 static uint32_t g_initialize_count = 0;
33 
34 namespace {
35 class SupportedArchList {
36 public:
37   SupportedArchList() {
38     AddArch(ArchSpec("i686-pc-windows"));
39     AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault));
40     AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32));
41     AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64));
42     AddArch(ArchSpec("i386-pc-windows"));
43   }
44 
45   size_t Count() const { return m_archs.size(); }
46 
47   const ArchSpec &operator[](int idx) { return m_archs[idx]; }
48 
49 private:
50   void AddArch(const ArchSpec &spec) {
51     auto iter = std::find_if(
52         m_archs.begin(), m_archs.end(),
53         [spec](const ArchSpec &rhs) { return spec.IsExactMatch(rhs); });
54     if (iter != m_archs.end())
55       return;
56     if (spec.IsValid())
57       m_archs.push_back(spec);
58   }
59 
60   std::vector<ArchSpec> m_archs;
61 };
62 } // anonymous namespace
63 
64 PlatformSP PlatformWindows::CreateInstance(bool force,
65                                            const lldb_private::ArchSpec *arch) {
66   // The only time we create an instance is when we are creating a remote
67   // windows platform
68   const bool is_host = false;
69 
70   bool create = force;
71   if (!create && arch && arch->IsValid()) {
72     const llvm::Triple &triple = arch->GetTriple();
73     switch (triple.getVendor()) {
74     case llvm::Triple::PC:
75       create = true;
76       break;
77 
78     case llvm::Triple::UnknownVendor:
79       create = !arch->TripleVendorWasSpecified();
80       break;
81 
82     default:
83       break;
84     }
85 
86     if (create) {
87       switch (triple.getOS()) {
88       case llvm::Triple::Win32:
89         break;
90 
91       case llvm::Triple::UnknownOS:
92         create = arch->TripleOSWasSpecified();
93         break;
94 
95       default:
96         create = false;
97         break;
98       }
99     }
100   }
101   if (create)
102     return PlatformSP(new PlatformWindows(is_host));
103   return PlatformSP();
104 }
105 
106 lldb_private::ConstString PlatformWindows::GetPluginNameStatic(bool is_host) {
107   if (is_host) {
108     static ConstString g_host_name(Platform::GetHostPlatformName());
109     return g_host_name;
110   } else {
111     static ConstString g_remote_name("remote-windows");
112     return g_remote_name;
113   }
114 }
115 
116 const char *PlatformWindows::GetPluginDescriptionStatic(bool is_host) {
117   return is_host ? "Local Windows user platform plug-in."
118                  : "Remote Windows user platform plug-in.";
119 }
120 
121 lldb_private::ConstString PlatformWindows::GetPluginName() {
122   return GetPluginNameStatic(IsHost());
123 }
124 
125 void PlatformWindows::Initialize() {
126   Platform::Initialize();
127 
128   if (g_initialize_count++ == 0) {
129 #if defined(_WIN32)
130     // Force a host flag to true for the default platform object.
131     PlatformSP default_platform_sp(new PlatformWindows(true));
132     default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
133     Platform::SetHostPlatform(default_platform_sp);
134 #endif
135     PluginManager::RegisterPlugin(
136         PlatformWindows::GetPluginNameStatic(false),
137         PlatformWindows::GetPluginDescriptionStatic(false),
138         PlatformWindows::CreateInstance);
139   }
140 }
141 
142 void PlatformWindows::Terminate() {
143   if (g_initialize_count > 0) {
144     if (--g_initialize_count == 0) {
145       PluginManager::UnregisterPlugin(PlatformWindows::CreateInstance);
146     }
147   }
148 
149   Platform::Terminate();
150 }
151 
152 /// Default Constructor
153 PlatformWindows::PlatformWindows(bool is_host) : RemoteAwarePlatform(is_host) {}
154 
155 /// Destructor.
156 ///
157 /// The destructor is virtual since this class is designed to be
158 /// inherited from by the plug-in instance.
159 PlatformWindows::~PlatformWindows() = default;
160 
161 Status PlatformWindows::ResolveExecutable(
162     const ModuleSpec &ms, lldb::ModuleSP &exe_module_sp,
163     const FileSpecList *module_search_paths_ptr) {
164   Status error;
165   // Nothing special to do here, just use the actual file and architecture
166 
167   char exe_path[PATH_MAX];
168   ModuleSpec resolved_module_spec(ms);
169 
170   if (IsHost()) {
171     // if we cant resolve the executable loation based on the current path
172     // variables
173     if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) {
174       resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
175       resolved_module_spec.GetFileSpec().SetFile(exe_path,
176                                                  FileSpec::Style::native);
177       FileSystem::Instance().Resolve(resolved_module_spec.GetFileSpec());
178     }
179 
180     if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
181       FileSystem::Instance().ResolveExecutableLocation(
182           resolved_module_spec.GetFileSpec());
183 
184     if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
185       error.Clear();
186     else {
187       ms.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
188       error.SetErrorStringWithFormat("unable to find executable for '%s'",
189                                      exe_path);
190     }
191   } else {
192     if (m_remote_platform_sp) {
193       error =
194           GetCachedExecutable(resolved_module_spec, exe_module_sp,
195                               module_search_paths_ptr, *m_remote_platform_sp);
196     } else {
197       // We may connect to a process and use the provided executable (Don't use
198       // local $PATH).
199       if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec()))
200         error.Clear();
201       else
202         error.SetErrorStringWithFormat("the platform is not currently "
203                                        "connected, and '%s' doesn't exist in "
204                                        "the system root.",
205                                        exe_path);
206     }
207   }
208 
209   if (error.Success()) {
210     if (resolved_module_spec.GetArchitecture().IsValid()) {
211       error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
212                                           nullptr, nullptr, nullptr);
213 
214       if (!exe_module_sp || exe_module_sp->GetObjectFile() == nullptr) {
215         exe_module_sp.reset();
216         error.SetErrorStringWithFormat(
217             "'%s' doesn't contain the architecture %s",
218             resolved_module_spec.GetFileSpec().GetPath().c_str(),
219             resolved_module_spec.GetArchitecture().GetArchitectureName());
220       }
221     } else {
222       // No valid architecture was specified, ask the platform for the
223       // architectures that we should be using (in the correct order) and see
224       // if we can find a match that way
225       StreamString arch_names;
226       for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
227                idx, resolved_module_spec.GetArchitecture());
228            ++idx) {
229         error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
230                                             module_search_paths_ptr, nullptr,
231                                             nullptr);
232         // Did we find an executable using one of the
233         if (error.Success()) {
234           if (exe_module_sp && exe_module_sp->GetObjectFile())
235             break;
236           else
237             error.SetErrorToGenericError();
238         }
239 
240         if (idx > 0)
241           arch_names.PutCString(", ");
242         arch_names.PutCString(
243             resolved_module_spec.GetArchitecture().GetArchitectureName());
244       }
245 
246       if (error.Fail() || !exe_module_sp) {
247         if (FileSystem::Instance().Readable(
248                 resolved_module_spec.GetFileSpec())) {
249           error.SetErrorStringWithFormat(
250               "'%s' doesn't contain any '%s' platform architectures: %s",
251               resolved_module_spec.GetFileSpec().GetPath().c_str(),
252               GetPluginName().GetCString(), arch_names.GetData());
253         } else {
254           error.SetErrorStringWithFormat(
255               "'%s' is not readable",
256               resolved_module_spec.GetFileSpec().GetPath().c_str());
257         }
258       }
259     }
260   }
261 
262   return error;
263 }
264 
265 Status PlatformWindows::ConnectRemote(Args &args) {
266   Status error;
267   if (IsHost()) {
268     error.SetErrorStringWithFormat(
269         "can't connect to the host platform '%s', always connected",
270         GetPluginName().AsCString());
271   } else {
272     if (!m_remote_platform_sp)
273       m_remote_platform_sp =
274           Platform::Create(ConstString("remote-gdb-server"), error);
275 
276     if (m_remote_platform_sp) {
277       if (error.Success()) {
278         if (m_remote_platform_sp) {
279           error = m_remote_platform_sp->ConnectRemote(args);
280         } else {
281           error.SetErrorString(
282               "\"platform connect\" takes a single argument: <connect-url>");
283         }
284       }
285     } else
286       error.SetErrorString("failed to create a 'remote-gdb-server' platform");
287 
288     if (error.Fail())
289       m_remote_platform_sp.reset();
290   }
291 
292   return error;
293 }
294 
295 Status PlatformWindows::DisconnectRemote() {
296   Status error;
297 
298   if (IsHost()) {
299     error.SetErrorStringWithFormat(
300         "can't disconnect from the host platform '%s', always connected",
301         GetPluginName().AsCString());
302   } else {
303     if (m_remote_platform_sp)
304       error = m_remote_platform_sp->DisconnectRemote();
305     else
306       error.SetErrorString("the platform is not currently connected");
307   }
308   return error;
309 }
310 
311 ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info,
312                                         Debugger &debugger, Target *target,
313                                         Status &error) {
314   // Windows has special considerations that must be followed when launching or
315   // attaching to a process.  The key requirement is that when launching or
316   // attaching to a process, you must do it from the same the thread that will
317   // go into a permanent loop which will then receive debug events from the
318   // process.  In particular, this means we can't use any of LLDB's generic
319   // mechanisms to do it for us, because it doesn't have the special knowledge
320   // required for setting up the background thread or passing the right flags.
321   //
322   // Another problem is that that LLDB's standard model for debugging a process
323   // is to first launch it, have it stop at the entry point, and then attach to
324   // it.  In Windows this doesn't quite work, you have to specify as an
325   // argument to CreateProcess() that you're going to debug the process.  So we
326   // override DebugProcess here to handle this.  Launch operations go directly
327   // to the process plugin, and attach operations almost go directly to the
328   // process plugin (but we hijack the events first).  In essence, we
329   // encapsulate all the logic of Launching and Attaching in the process
330   // plugin, and PlatformWindows::DebugProcess is just a pass-through to get to
331   // the process plugin.
332 
333   if (IsRemote()) {
334     if (m_remote_platform_sp)
335       return m_remote_platform_sp->DebugProcess(launch_info, debugger, target,
336                                                 error);
337     else
338       error.SetErrorString("the platform is not currently connected");
339   }
340 
341   if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) {
342     // This is a process attach.  Don't need to launch anything.
343     ProcessAttachInfo attach_info(launch_info);
344     return Attach(attach_info, debugger, target, error);
345   } else {
346     ProcessSP process_sp = target->CreateProcess(
347         launch_info.GetListener(), launch_info.GetProcessPluginName(), nullptr);
348 
349     // We need to launch and attach to the process.
350     launch_info.GetFlags().Set(eLaunchFlagDebug);
351     if (process_sp)
352       error = process_sp->Launch(launch_info);
353 
354     return process_sp;
355   }
356 }
357 
358 lldb::ProcessSP PlatformWindows::Attach(ProcessAttachInfo &attach_info,
359                                         Debugger &debugger, Target *target,
360                                         Status &error) {
361   error.Clear();
362   lldb::ProcessSP process_sp;
363   if (!IsHost()) {
364     if (m_remote_platform_sp)
365       process_sp =
366           m_remote_platform_sp->Attach(attach_info, debugger, target, error);
367     else
368       error.SetErrorString("the platform is not currently connected");
369     return process_sp;
370   }
371 
372   if (target == nullptr) {
373     TargetSP new_target_sp;
374     FileSpec emptyFileSpec;
375     ArchSpec emptyArchSpec;
376 
377     error = debugger.GetTargetList().CreateTarget(
378         debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
379     target = new_target_sp.get();
380   }
381 
382   if (!target || error.Fail())
383     return process_sp;
384 
385   debugger.GetTargetList().SetSelectedTarget(target);
386 
387   const char *plugin_name = attach_info.GetProcessPluginName();
388   process_sp = target->CreateProcess(
389       attach_info.GetListenerForProcess(debugger), plugin_name, nullptr);
390 
391   process_sp->HijackProcessEvents(attach_info.GetHijackListener());
392   if (process_sp)
393     error = process_sp->Attach(attach_info);
394 
395   return process_sp;
396 }
397 
398 bool PlatformWindows::GetSupportedArchitectureAtIndex(uint32_t idx,
399                                                       ArchSpec &arch) {
400   static SupportedArchList architectures;
401 
402   if (idx >= architectures.Count())
403     return false;
404   arch = architectures[idx];
405   return true;
406 }
407 
408 void PlatformWindows::GetStatus(Stream &strm) {
409   Platform::GetStatus(strm);
410 
411 #ifdef _WIN32
412   llvm::VersionTuple version = HostInfo::GetOSVersion();
413   strm << "      Host: Windows " << version.getAsString() << '\n';
414 #endif
415 }
416 
417 bool PlatformWindows::CanDebugProcess() { return true; }
418 
419 ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) {
420   if (basename.IsEmpty())
421     return basename;
422 
423   StreamString stream;
424   stream.Printf("%s.dll", basename.GetCString());
425   return ConstString(stream.GetString());
426 }
427