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