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