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/PluginManager.h"
22 #include "lldb/Host/HostInfo.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Utility/Status.h"
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 
29 LLDB_PLUGIN_DEFINE(PlatformWindows)
30 
31 static uint32_t g_initialize_count = 0;
32 
33 namespace {
34 class SupportedArchList {
35 public:
SupportedArchList()36   SupportedArchList() {
37     AddArch(ArchSpec("i686-pc-windows"));
38     AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault));
39     AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32));
40     AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64));
41     AddArch(ArchSpec("i386-pc-windows"));
42   }
43 
Count() const44   size_t Count() const { return m_archs.size(); }
45 
operator [](int idx)46   const ArchSpec &operator[](int idx) { return m_archs[idx]; }
47 
48 private:
AddArch(const ArchSpec & spec)49   void AddArch(const ArchSpec &spec) {
50     auto iter = std::find_if(
51         m_archs.begin(), m_archs.end(),
52         [spec](const ArchSpec &rhs) { return spec.IsExactMatch(rhs); });
53     if (iter != m_archs.end())
54       return;
55     if (spec.IsValid())
56       m_archs.push_back(spec);
57   }
58 
59   std::vector<ArchSpec> m_archs;
60 };
61 } // anonymous namespace
62 
CreateInstance(bool force,const lldb_private::ArchSpec * arch)63 PlatformSP PlatformWindows::CreateInstance(bool force,
64                                            const lldb_private::ArchSpec *arch) {
65   // The only time we create an instance is when we are creating a remote
66   // windows platform
67   const bool is_host = false;
68 
69   bool create = force;
70   if (!create && arch && arch->IsValid()) {
71     const llvm::Triple &triple = arch->GetTriple();
72     switch (triple.getVendor()) {
73     case llvm::Triple::PC:
74       create = true;
75       break;
76 
77     case llvm::Triple::UnknownVendor:
78       create = !arch->TripleVendorWasSpecified();
79       break;
80 
81     default:
82       break;
83     }
84 
85     if (create) {
86       switch (triple.getOS()) {
87       case llvm::Triple::Win32:
88         break;
89 
90       case llvm::Triple::UnknownOS:
91         create = arch->TripleOSWasSpecified();
92         break;
93 
94       default:
95         create = false;
96         break;
97       }
98     }
99   }
100   if (create)
101     return PlatformSP(new PlatformWindows(is_host));
102   return PlatformSP();
103 }
104 
GetPluginNameStatic(bool is_host)105 lldb_private::ConstString PlatformWindows::GetPluginNameStatic(bool is_host) {
106   if (is_host) {
107     static ConstString g_host_name(Platform::GetHostPlatformName());
108     return g_host_name;
109   } else {
110     static ConstString g_remote_name("remote-windows");
111     return g_remote_name;
112   }
113 }
114 
GetPluginDescriptionStatic(bool is_host)115 const char *PlatformWindows::GetPluginDescriptionStatic(bool is_host) {
116   return is_host ? "Local Windows user platform plug-in."
117                  : "Remote Windows user platform plug-in.";
118 }
119 
GetPluginName()120 lldb_private::ConstString PlatformWindows::GetPluginName() {
121   return GetPluginNameStatic(IsHost());
122 }
123 
Initialize()124 void PlatformWindows::Initialize() {
125   Platform::Initialize();
126 
127   if (g_initialize_count++ == 0) {
128 #if defined(_WIN32)
129     // Force a host flag to true for the default platform object.
130     PlatformSP default_platform_sp(new PlatformWindows(true));
131     default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
132     Platform::SetHostPlatform(default_platform_sp);
133 #endif
134     PluginManager::RegisterPlugin(
135         PlatformWindows::GetPluginNameStatic(false),
136         PlatformWindows::GetPluginDescriptionStatic(false),
137         PlatformWindows::CreateInstance);
138   }
139 }
140 
Terminate()141 void PlatformWindows::Terminate() {
142   if (g_initialize_count > 0) {
143     if (--g_initialize_count == 0) {
144       PluginManager::UnregisterPlugin(PlatformWindows::CreateInstance);
145     }
146   }
147 
148   Platform::Terminate();
149 }
150 
151 /// Default Constructor
PlatformWindows(bool is_host)152 PlatformWindows::PlatformWindows(bool is_host) : RemoteAwarePlatform(is_host) {}
153 
ConnectRemote(Args & args)154 Status PlatformWindows::ConnectRemote(Args &args) {
155   Status error;
156   if (IsHost()) {
157     error.SetErrorStringWithFormat(
158         "can't connect to the host platform '%s', always connected",
159         GetPluginName().AsCString());
160   } else {
161     if (!m_remote_platform_sp)
162       m_remote_platform_sp =
163           Platform::Create(ConstString("remote-gdb-server"), error);
164 
165     if (m_remote_platform_sp) {
166       if (error.Success()) {
167         if (m_remote_platform_sp) {
168           error = m_remote_platform_sp->ConnectRemote(args);
169         } else {
170           error.SetErrorString(
171               "\"platform connect\" takes a single argument: <connect-url>");
172         }
173       }
174     } else
175       error.SetErrorString("failed to create a 'remote-gdb-server' platform");
176 
177     if (error.Fail())
178       m_remote_platform_sp.reset();
179   }
180 
181   return error;
182 }
183 
DisconnectRemote()184 Status PlatformWindows::DisconnectRemote() {
185   Status error;
186 
187   if (IsHost()) {
188     error.SetErrorStringWithFormat(
189         "can't disconnect from the host platform '%s', always connected",
190         GetPluginName().AsCString());
191   } else {
192     if (m_remote_platform_sp)
193       error = m_remote_platform_sp->DisconnectRemote();
194     else
195       error.SetErrorString("the platform is not currently connected");
196   }
197   return error;
198 }
199 
DebugProcess(ProcessLaunchInfo & launch_info,Debugger & debugger,Target * target,Status & error)200 ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info,
201                                         Debugger &debugger, Target *target,
202                                         Status &error) {
203   // Windows has special considerations that must be followed when launching or
204   // attaching to a process.  The key requirement is that when launching or
205   // attaching to a process, you must do it from the same the thread that will
206   // go into a permanent loop which will then receive debug events from the
207   // process.  In particular, this means we can't use any of LLDB's generic
208   // mechanisms to do it for us, because it doesn't have the special knowledge
209   // required for setting up the background thread or passing the right flags.
210   //
211   // Another problem is that that LLDB's standard model for debugging a process
212   // is to first launch it, have it stop at the entry point, and then attach to
213   // it.  In Windows this doesn't quite work, you have to specify as an
214   // argument to CreateProcess() that you're going to debug the process.  So we
215   // override DebugProcess here to handle this.  Launch operations go directly
216   // to the process plugin, and attach operations almost go directly to the
217   // process plugin (but we hijack the events first).  In essence, we
218   // encapsulate all the logic of Launching and Attaching in the process
219   // plugin, and PlatformWindows::DebugProcess is just a pass-through to get to
220   // the process plugin.
221 
222   if (IsRemote()) {
223     if (m_remote_platform_sp)
224       return m_remote_platform_sp->DebugProcess(launch_info, debugger, target,
225                                                 error);
226     else
227       error.SetErrorString("the platform is not currently connected");
228   }
229 
230   if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) {
231     // This is a process attach.  Don't need to launch anything.
232     ProcessAttachInfo attach_info(launch_info);
233     return Attach(attach_info, debugger, target, error);
234   } else {
235     ProcessSP process_sp = target->CreateProcess(
236         launch_info.GetListener(), launch_info.GetProcessPluginName(), nullptr,
237         false);
238 
239     // We need to launch and attach to the process.
240     launch_info.GetFlags().Set(eLaunchFlagDebug);
241     if (process_sp)
242       error = process_sp->Launch(launch_info);
243 
244     return process_sp;
245   }
246 }
247 
Attach(ProcessAttachInfo & attach_info,Debugger & debugger,Target * target,Status & error)248 lldb::ProcessSP PlatformWindows::Attach(ProcessAttachInfo &attach_info,
249                                         Debugger &debugger, Target *target,
250                                         Status &error) {
251   error.Clear();
252   lldb::ProcessSP process_sp;
253   if (!IsHost()) {
254     if (m_remote_platform_sp)
255       process_sp =
256           m_remote_platform_sp->Attach(attach_info, debugger, target, error);
257     else
258       error.SetErrorString("the platform is not currently connected");
259     return process_sp;
260   }
261 
262   if (target == nullptr) {
263     TargetSP new_target_sp;
264     FileSpec emptyFileSpec;
265     ArchSpec emptyArchSpec;
266 
267     error = debugger.GetTargetList().CreateTarget(
268         debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
269     target = new_target_sp.get();
270   }
271 
272   if (!target || error.Fail())
273     return process_sp;
274 
275   debugger.GetTargetList().SetSelectedTarget(target);
276 
277   const char *plugin_name = attach_info.GetProcessPluginName();
278   process_sp = target->CreateProcess(
279       attach_info.GetListenerForProcess(debugger), plugin_name, nullptr, false);
280 
281   process_sp->HijackProcessEvents(attach_info.GetHijackListener());
282   if (process_sp)
283     error = process_sp->Attach(attach_info);
284 
285   return process_sp;
286 }
287 
GetSupportedArchitectureAtIndex(uint32_t idx,ArchSpec & arch)288 bool PlatformWindows::GetSupportedArchitectureAtIndex(uint32_t idx,
289                                                       ArchSpec &arch) {
290   static SupportedArchList architectures;
291 
292   if (idx >= architectures.Count())
293     return false;
294   arch = architectures[idx];
295   return true;
296 }
297 
GetStatus(Stream & strm)298 void PlatformWindows::GetStatus(Stream &strm) {
299   Platform::GetStatus(strm);
300 
301 #ifdef _WIN32
302   llvm::VersionTuple version = HostInfo::GetOSVersion();
303   strm << "      Host: Windows " << version.getAsString() << '\n';
304 #endif
305 }
306 
CanDebugProcess()307 bool PlatformWindows::CanDebugProcess() { return true; }
308 
GetFullNameForDylib(ConstString basename)309 ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) {
310   if (basename.IsEmpty())
311     return basename;
312 
313   StreamString stream;
314   stream.Printf("%s.dll", basename.GetCString());
315   return ConstString(stream.GetString());
316 }
317