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