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