| 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 | #include <optional> |
| 13 | #if defined(_WIN32) |
| 14 | #include "lldb/Host/windows/windows.h" |
| 15 | #include <winsock2.h> |
| 16 | #endif |
| 17 | |
| 18 | #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" |
| 19 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
| 20 | #include "lldb/Breakpoint/BreakpointLocation.h" |
| 21 | #include "lldb/Breakpoint/BreakpointSite.h" |
| 22 | #include "lldb/Core/Debugger.h" |
| 23 | #include "lldb/Core/Module.h" |
| 24 | #include "lldb/Core/PluginManager.h" |
| 25 | #include "lldb/Expression/DiagnosticManager.h" |
| 26 | #include "lldb/Expression/FunctionCaller.h" |
| 27 | #include "lldb/Expression/UserExpression.h" |
| 28 | #include "lldb/Expression/UtilityFunction.h" |
| 29 | #include "lldb/Host/HostInfo.h" |
| 30 | #include "lldb/Target/DynamicLoader.h" |
| 31 | #include "lldb/Target/Process.h" |
| 32 | #include "lldb/Utility/Status.h" |
| 33 | |
| 34 | #include "llvm/ADT/ScopeExit.h" |
| 35 | #include "llvm/Support/ConvertUTF.h" |
| 36 | |
| 37 | using namespace lldb; |
| 38 | using namespace lldb_private; |
| 39 | |
| 40 | LLDB_PLUGIN_DEFINE(PlatformWindows) |
| 41 | |
| 42 | static uint32_t g_initialize_count = 0; |
| 43 | |
| 44 | PlatformSP PlatformWindows::CreateInstance(bool force, |
| 45 | const lldb_private::ArchSpec *arch) { |
| 46 | // The only time we create an instance is when we are creating a remote |
| 47 | // windows platform |
| 48 | const bool is_host = false; |
| 49 | |
| 50 | bool create = force; |
| 51 | if (!create && arch && arch->IsValid()) { |
| 52 | const llvm::Triple &triple = arch->GetTriple(); |
| 53 | switch (triple.getVendor()) { |
| 54 | case llvm::Triple::PC: |
| 55 | create = true; |
| 56 | break; |
| 57 | |
| 58 | case llvm::Triple::UnknownVendor: |
| 59 | create = !arch->TripleVendorWasSpecified(); |
| 60 | break; |
| 61 | |
| 62 | default: |
| 63 | break; |
| 64 | } |
| 65 | |
| 66 | if (create) { |
| 67 | switch (triple.getOS()) { |
| 68 | case llvm::Triple::Win32: |
| 69 | break; |
| 70 | |
| 71 | case llvm::Triple::UnknownOS: |
| 72 | create = arch->TripleOSWasSpecified(); |
| 73 | break; |
| 74 | |
| 75 | default: |
| 76 | create = false; |
| 77 | break; |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | if (create) |
| 82 | return PlatformSP(new PlatformWindows(is_host)); |
| 83 | return PlatformSP(); |
| 84 | } |
| 85 | |
| 86 | llvm::StringRef PlatformWindows::GetPluginDescriptionStatic(bool is_host) { |
| 87 | return is_host ? "Local Windows user platform plug-in." |
| 88 | : "Remote Windows user platform plug-in." ; |
| 89 | } |
| 90 | |
| 91 | void PlatformWindows::Initialize() { |
| 92 | Platform::Initialize(); |
| 93 | |
| 94 | if (g_initialize_count++ == 0) { |
| 95 | #if defined(_WIN32) |
| 96 | // Force a host flag to true for the default platform object. |
| 97 | PlatformSP default_platform_sp(new PlatformWindows(true)); |
| 98 | default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); |
| 99 | Platform::SetHostPlatform(default_platform_sp); |
| 100 | #endif |
| 101 | PluginManager::RegisterPlugin( |
| 102 | name: PlatformWindows::GetPluginNameStatic(is_host: false), |
| 103 | description: PlatformWindows::GetPluginDescriptionStatic(is_host: false), |
| 104 | create_callback: PlatformWindows::CreateInstance); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | void PlatformWindows::Terminate() { |
| 109 | if (g_initialize_count > 0) { |
| 110 | if (--g_initialize_count == 0) { |
| 111 | PluginManager::UnregisterPlugin(create_callback: PlatformWindows::CreateInstance); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | Platform::Terminate(); |
| 116 | } |
| 117 | |
| 118 | /// Default Constructor |
| 119 | PlatformWindows::PlatformWindows(bool is_host) : RemoteAwarePlatform(is_host) { |
| 120 | const auto &AddArch = [&](const ArchSpec &spec) { |
| 121 | if (llvm::any_of(Range&: m_supported_architectures, P: [spec](const ArchSpec &rhs) { |
| 122 | return spec.IsExactMatch(rhs); |
| 123 | })) |
| 124 | return; |
| 125 | if (spec.IsValid()) |
| 126 | m_supported_architectures.push_back(x: spec); |
| 127 | }; |
| 128 | AddArch(HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKindDefault)); |
| 129 | AddArch(HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind32)); |
| 130 | AddArch(HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind64)); |
| 131 | } |
| 132 | |
| 133 | Status PlatformWindows::ConnectRemote(Args &args) { |
| 134 | Status error; |
| 135 | if (IsHost()) { |
| 136 | error = Status::FromErrorStringWithFormatv( |
| 137 | format: "can't connect to the host platform '{0}', always connected" , |
| 138 | args: GetPluginName()); |
| 139 | } else { |
| 140 | if (!m_remote_platform_sp) |
| 141 | m_remote_platform_sp = |
| 142 | platform_gdb_server::PlatformRemoteGDBServer::CreateInstance( |
| 143 | /*force=*/true, arch: nullptr); |
| 144 | |
| 145 | if (m_remote_platform_sp) { |
| 146 | if (error.Success()) { |
| 147 | if (m_remote_platform_sp) { |
| 148 | error = m_remote_platform_sp->ConnectRemote(args); |
| 149 | } else { |
| 150 | error = Status::FromErrorString( |
| 151 | str: "\"platform connect\" takes a single argument: <connect-url>" ); |
| 152 | } |
| 153 | } |
| 154 | } else |
| 155 | error = Status::FromErrorString( |
| 156 | str: "failed to create a 'remote-gdb-server' platform" ); |
| 157 | |
| 158 | if (error.Fail()) |
| 159 | m_remote_platform_sp.reset(); |
| 160 | } |
| 161 | |
| 162 | return error; |
| 163 | } |
| 164 | |
| 165 | uint32_t PlatformWindows::DoLoadImage(Process *process, |
| 166 | const FileSpec &remote_file, |
| 167 | const std::vector<std::string> *paths, |
| 168 | Status &error, FileSpec *loaded_image) { |
| 169 | DiagnosticManager diagnostics; |
| 170 | |
| 171 | if (loaded_image) |
| 172 | loaded_image->Clear(); |
| 173 | |
| 174 | ThreadSP thread = process->GetThreadList().GetExpressionExecutionThread(); |
| 175 | if (!thread) { |
| 176 | error = Status::FromErrorString( |
| 177 | str: "LoadLibrary error: no thread available to invoke LoadLibrary" ); |
| 178 | return LLDB_INVALID_IMAGE_TOKEN; |
| 179 | } |
| 180 | |
| 181 | ExecutionContext context; |
| 182 | thread->CalculateExecutionContext(exe_ctx&: context); |
| 183 | |
| 184 | Status status; |
| 185 | UtilityFunction *loader = |
| 186 | process->GetLoadImageUtilityFunction(platform: this, factory: [&]() -> std::unique_ptr<UtilityFunction> { |
| 187 | return MakeLoadImageUtilityFunction(context, status); |
| 188 | }); |
| 189 | if (loader == nullptr) |
| 190 | return LLDB_INVALID_IMAGE_TOKEN; |
| 191 | |
| 192 | FunctionCaller *invocation = loader->GetFunctionCaller(); |
| 193 | if (!invocation) { |
| 194 | error = Status::FromErrorString( |
| 195 | str: "LoadLibrary error: could not get function caller" ); |
| 196 | return LLDB_INVALID_IMAGE_TOKEN; |
| 197 | } |
| 198 | |
| 199 | /* Convert name */ |
| 200 | llvm::SmallVector<llvm::UTF16, 261> name; |
| 201 | if (!llvm::convertUTF8ToUTF16String(SrcUTF8: remote_file.GetPath(), DstUTF16&: name)) { |
| 202 | error = Status::FromErrorString( |
| 203 | str: "LoadLibrary error: could not convert path to UCS2" ); |
| 204 | return LLDB_INVALID_IMAGE_TOKEN; |
| 205 | } |
| 206 | name.emplace_back(Args: L'\0'); |
| 207 | |
| 208 | /* Inject name paramter into inferior */ |
| 209 | lldb::addr_t injected_name = |
| 210 | process->AllocateMemory(size: name.size() * sizeof(llvm::UTF16), |
| 211 | permissions: ePermissionsReadable | ePermissionsWritable, |
| 212 | error&: status); |
| 213 | if (injected_name == LLDB_INVALID_ADDRESS) { |
| 214 | error = Status::FromErrorStringWithFormat( |
| 215 | format: "LoadLibrary error: unable to allocate memory for name: %s" , |
| 216 | status.AsCString()); |
| 217 | return LLDB_INVALID_IMAGE_TOKEN; |
| 218 | } |
| 219 | |
| 220 | auto name_cleanup = llvm::make_scope_exit(F: [process, injected_name]() { |
| 221 | process->DeallocateMemory(ptr: injected_name); |
| 222 | }); |
| 223 | |
| 224 | process->WriteMemory(vm_addr: injected_name, buf: name.data(), |
| 225 | size: name.size() * sizeof(llvm::UTF16), error&: status); |
| 226 | if (status.Fail()) { |
| 227 | error = Status::FromErrorStringWithFormat( |
| 228 | format: "LoadLibrary error: unable to write name: %s" , status.AsCString()); |
| 229 | return LLDB_INVALID_IMAGE_TOKEN; |
| 230 | } |
| 231 | |
| 232 | /* Inject paths parameter into inferior */ |
| 233 | lldb::addr_t injected_paths{0x0}; |
| 234 | std::optional<llvm::detail::scope_exit<std::function<void()>>> paths_cleanup; |
| 235 | if (paths) { |
| 236 | llvm::SmallVector<llvm::UTF16, 261> search_paths; |
| 237 | |
| 238 | for (const auto &path : *paths) { |
| 239 | if (path.empty()) |
| 240 | continue; |
| 241 | |
| 242 | llvm::SmallVector<llvm::UTF16, 261> buffer; |
| 243 | if (!llvm::convertUTF8ToUTF16String(SrcUTF8: path, DstUTF16&: buffer)) |
| 244 | continue; |
| 245 | |
| 246 | search_paths.append(in_start: std::begin(cont&: buffer), in_end: std::end(cont&: buffer)); |
| 247 | search_paths.emplace_back(Args: L'\0'); |
| 248 | } |
| 249 | search_paths.emplace_back(Args: L'\0'); |
| 250 | |
| 251 | injected_paths = |
| 252 | process->AllocateMemory(size: search_paths.size() * sizeof(llvm::UTF16), |
| 253 | permissions: ePermissionsReadable | ePermissionsWritable, |
| 254 | error&: status); |
| 255 | if (injected_paths == LLDB_INVALID_ADDRESS) { |
| 256 | error = Status::FromErrorStringWithFormat( |
| 257 | format: "LoadLibrary error: unable to allocate memory for paths: %s" , |
| 258 | status.AsCString()); |
| 259 | return LLDB_INVALID_IMAGE_TOKEN; |
| 260 | } |
| 261 | |
| 262 | paths_cleanup.emplace(args: [process, injected_paths]() { |
| 263 | process->DeallocateMemory(ptr: injected_paths); |
| 264 | }); |
| 265 | |
| 266 | process->WriteMemory(vm_addr: injected_paths, buf: search_paths.data(), |
| 267 | size: search_paths.size() * sizeof(llvm::UTF16), error&: status); |
| 268 | if (status.Fail()) { |
| 269 | error = Status::FromErrorStringWithFormat( |
| 270 | format: "LoadLibrary error: unable to write paths: %s" , status.AsCString()); |
| 271 | return LLDB_INVALID_IMAGE_TOKEN; |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /* Inject wszModulePath into inferior */ |
| 276 | // FIXME(compnerd) should do something better for the length? |
| 277 | // GetModuleFileNameA is likely limited to PATH_MAX rather than the NT path |
| 278 | // limit. |
| 279 | unsigned injected_length = 261; |
| 280 | |
| 281 | lldb::addr_t injected_module_path = |
| 282 | process->AllocateMemory(size: injected_length + 1, |
| 283 | permissions: ePermissionsReadable | ePermissionsWritable, |
| 284 | error&: status); |
| 285 | if (injected_module_path == LLDB_INVALID_ADDRESS) { |
| 286 | error = Status::FromErrorStringWithFormat( |
| 287 | format: "LoadLibrary error: unable to allocate memory for module location: %s" , |
| 288 | status.AsCString()); |
| 289 | return LLDB_INVALID_IMAGE_TOKEN; |
| 290 | } |
| 291 | |
| 292 | auto injected_module_path_cleanup = |
| 293 | llvm::make_scope_exit(F: [process, injected_module_path]() { |
| 294 | process->DeallocateMemory(ptr: injected_module_path); |
| 295 | }); |
| 296 | |
| 297 | /* Inject __lldb_LoadLibraryResult into inferior */ |
| 298 | const uint32_t word_size = process->GetAddressByteSize(); |
| 299 | lldb::addr_t injected_result = |
| 300 | process->AllocateMemory(size: 3 * word_size, |
| 301 | permissions: ePermissionsReadable | ePermissionsWritable, |
| 302 | error&: status); |
| 303 | if (status.Fail()) { |
| 304 | error = Status::FromErrorStringWithFormat( |
| 305 | format: "LoadLibrary error: could not allocate memory for result: %s" , |
| 306 | status.AsCString()); |
| 307 | return LLDB_INVALID_IMAGE_TOKEN; |
| 308 | } |
| 309 | |
| 310 | auto result_cleanup = llvm::make_scope_exit(F: [process, injected_result]() { |
| 311 | process->DeallocateMemory(ptr: injected_result); |
| 312 | }); |
| 313 | |
| 314 | process->WritePointerToMemory(vm_addr: injected_result + word_size, |
| 315 | ptr_value: injected_module_path, error&: status); |
| 316 | if (status.Fail()) { |
| 317 | error = Status::FromErrorStringWithFormat( |
| 318 | format: "LoadLibrary error: could not initialize result: %s" , |
| 319 | status.AsCString()); |
| 320 | return LLDB_INVALID_IMAGE_TOKEN; |
| 321 | } |
| 322 | |
| 323 | // XXX(compnerd) should we use the compiler to get the sizeof(unsigned)? |
| 324 | process->WriteScalarToMemory(vm_addr: injected_result + 2 * word_size, |
| 325 | scalar: Scalar{injected_length}, size: sizeof(unsigned), |
| 326 | error&: status); |
| 327 | if (status.Fail()) { |
| 328 | error = Status::FromErrorStringWithFormat( |
| 329 | format: "LoadLibrary error: could not initialize result: %s" , |
| 330 | status.AsCString()); |
| 331 | return LLDB_INVALID_IMAGE_TOKEN; |
| 332 | } |
| 333 | |
| 334 | /* Setup Formal Parameters */ |
| 335 | ValueList parameters = invocation->GetArgumentValues(); |
| 336 | parameters.GetValueAtIndex(idx: 0)->GetScalar() = injected_name; |
| 337 | parameters.GetValueAtIndex(idx: 1)->GetScalar() = injected_paths; |
| 338 | parameters.GetValueAtIndex(idx: 2)->GetScalar() = injected_result; |
| 339 | |
| 340 | lldb::addr_t injected_parameters = LLDB_INVALID_ADDRESS; |
| 341 | diagnostics.Clear(); |
| 342 | if (!invocation->WriteFunctionArguments(exe_ctx&: context, args_addr_ref&: injected_parameters, |
| 343 | arg_values&: parameters, diagnostic_manager&: diagnostics)) { |
| 344 | error = Status::FromError(error: diagnostics.GetAsError( |
| 345 | result: eExpressionSetupError, |
| 346 | message: "LoadLibrary error: unable to write function parameters:" )); |
| 347 | return LLDB_INVALID_IMAGE_TOKEN; |
| 348 | } |
| 349 | |
| 350 | auto parameter_cleanup = |
| 351 | llvm::make_scope_exit(F: [invocation, &context, injected_parameters]() { |
| 352 | invocation->DeallocateFunctionResults(exe_ctx&: context, args_addr: injected_parameters); |
| 353 | }); |
| 354 | |
| 355 | TypeSystemClangSP scratch_ts_sp = |
| 356 | ScratchTypeSystemClang::GetForTarget(target&: process->GetTarget()); |
| 357 | if (!scratch_ts_sp) { |
| 358 | error = Status::FromErrorString( |
| 359 | str: "LoadLibrary error: unable to get (clang) type system" ); |
| 360 | return LLDB_INVALID_IMAGE_TOKEN; |
| 361 | } |
| 362 | |
| 363 | /* Setup Return Type */ |
| 364 | CompilerType VoidPtrTy = |
| 365 | scratch_ts_sp->GetBasicType(type: eBasicTypeVoid).GetPointerType(); |
| 366 | |
| 367 | Value value; |
| 368 | value.SetCompilerType(VoidPtrTy); |
| 369 | |
| 370 | /* Invoke expression */ |
| 371 | EvaluateExpressionOptions options; |
| 372 | options.SetExecutionPolicy(eExecutionPolicyAlways); |
| 373 | options.SetLanguage(eLanguageTypeC_plus_plus); |
| 374 | options.SetIgnoreBreakpoints(true); |
| 375 | options.SetUnwindOnError(true); |
| 376 | // LoadLibraryEx{A,W}/FreeLibrary cannot raise exceptions which we can handle. |
| 377 | // They may potentially throw SEH exceptions which we do not know how to |
| 378 | // handle currently. |
| 379 | options.SetTrapExceptions(false); |
| 380 | options.SetTimeout(process->GetUtilityExpressionTimeout()); |
| 381 | options.SetIsForUtilityExpr(true); |
| 382 | |
| 383 | ExpressionResults result = |
| 384 | invocation->ExecuteFunction(exe_ctx&: context, args_addr_ptr: &injected_parameters, options, |
| 385 | diagnostic_manager&: diagnostics, results&: value); |
| 386 | if (result != eExpressionCompleted) { |
| 387 | error = Status::FromError(error: diagnostics.GetAsError( |
| 388 | result: eExpressionSetupError, |
| 389 | message: "LoadLibrary error: failed to execute LoadLibrary helper:" )); |
| 390 | return LLDB_INVALID_IMAGE_TOKEN; |
| 391 | } |
| 392 | |
| 393 | /* Read result */ |
| 394 | lldb::addr_t token = process->ReadPointerFromMemory(vm_addr: injected_result, error&: status); |
| 395 | if (status.Fail()) { |
| 396 | error = Status::FromErrorStringWithFormat( |
| 397 | format: "LoadLibrary error: could not read the result: %s" , status.AsCString()); |
| 398 | return LLDB_INVALID_IMAGE_TOKEN; |
| 399 | } |
| 400 | |
| 401 | if (!token) { |
| 402 | // XXX(compnerd) should we use the compiler to get the sizeof(unsigned)? |
| 403 | uint64_t error_code = |
| 404 | process->ReadUnsignedIntegerFromMemory(load_addr: injected_result + 2 * word_size + sizeof(unsigned), |
| 405 | byte_size: word_size, fail_value: 0, error&: status); |
| 406 | if (status.Fail()) { |
| 407 | error = Status::FromErrorStringWithFormat( |
| 408 | format: "LoadLibrary error: could not read error status: %s" , |
| 409 | status.AsCString()); |
| 410 | return LLDB_INVALID_IMAGE_TOKEN; |
| 411 | } |
| 412 | |
| 413 | error = Status::FromErrorStringWithFormat(format: "LoadLibrary Error: %" PRIu64, |
| 414 | error_code); |
| 415 | return LLDB_INVALID_IMAGE_TOKEN; |
| 416 | } |
| 417 | |
| 418 | std::string module_path; |
| 419 | process->ReadCStringFromMemory(vm_addr: injected_module_path, out_str&: module_path, error&: status); |
| 420 | if (status.Fail()) { |
| 421 | error = Status::FromErrorStringWithFormat( |
| 422 | format: "LoadLibrary error: could not read module path: %s" , |
| 423 | status.AsCString()); |
| 424 | return LLDB_INVALID_IMAGE_TOKEN; |
| 425 | } |
| 426 | |
| 427 | if (loaded_image) |
| 428 | loaded_image->SetFile(path: module_path, style: llvm::sys::path::Style::native); |
| 429 | return process->AddImageToken(image_ptr: token); |
| 430 | } |
| 431 | |
| 432 | Status PlatformWindows::UnloadImage(Process *process, uint32_t image_token) { |
| 433 | const addr_t address = process->GetImagePtrFromToken(token: image_token); |
| 434 | if (address == LLDB_INVALID_IMAGE_TOKEN) |
| 435 | return Status::FromErrorString(str: "invalid image token" ); |
| 436 | |
| 437 | StreamString expression; |
| 438 | expression.Printf(format: "FreeLibrary((HMODULE)0x%" PRIx64 ")" , address); |
| 439 | |
| 440 | ValueObjectSP value; |
| 441 | Status result = |
| 442 | EvaluateLoaderExpression(process, expression: expression.GetData(), value); |
| 443 | if (result.Fail()) |
| 444 | return result; |
| 445 | |
| 446 | if (value->GetError().Fail()) |
| 447 | return value->GetError().Clone(); |
| 448 | |
| 449 | Scalar scalar; |
| 450 | if (value->ResolveValue(scalar)) { |
| 451 | if (scalar.UInt(fail_value: 1)) |
| 452 | return Status::FromErrorStringWithFormat(format: "expression failed: \"%s\"" , |
| 453 | expression.GetData()); |
| 454 | process->ResetImageToken(token: image_token); |
| 455 | } |
| 456 | |
| 457 | return Status(); |
| 458 | } |
| 459 | |
| 460 | Status PlatformWindows::DisconnectRemote() { |
| 461 | Status error; |
| 462 | |
| 463 | if (IsHost()) { |
| 464 | error = Status::FromErrorStringWithFormatv( |
| 465 | format: "can't disconnect from the host platform '{0}', always connected" , |
| 466 | args: GetPluginName()); |
| 467 | } else { |
| 468 | if (m_remote_platform_sp) |
| 469 | error = m_remote_platform_sp->DisconnectRemote(); |
| 470 | else |
| 471 | error = |
| 472 | Status::FromErrorString(str: "the platform is not currently connected" ); |
| 473 | } |
| 474 | return error; |
| 475 | } |
| 476 | |
| 477 | ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, |
| 478 | Debugger &debugger, Target &target, |
| 479 | Status &error) { |
| 480 | // Windows has special considerations that must be followed when launching or |
| 481 | // attaching to a process. The key requirement is that when launching or |
| 482 | // attaching to a process, you must do it from the same the thread that will |
| 483 | // go into a permanent loop which will then receive debug events from the |
| 484 | // process. In particular, this means we can't use any of LLDB's generic |
| 485 | // mechanisms to do it for us, because it doesn't have the special knowledge |
| 486 | // required for setting up the background thread or passing the right flags. |
| 487 | // |
| 488 | // Another problem is that LLDB's standard model for debugging a process |
| 489 | // is to first launch it, have it stop at the entry point, and then attach to |
| 490 | // it. In Windows this doesn't quite work, you have to specify as an |
| 491 | // argument to CreateProcess() that you're going to debug the process. So we |
| 492 | // override DebugProcess here to handle this. Launch operations go directly |
| 493 | // to the process plugin, and attach operations almost go directly to the |
| 494 | // process plugin (but we hijack the events first). In essence, we |
| 495 | // encapsulate all the logic of Launching and Attaching in the process |
| 496 | // plugin, and PlatformWindows::DebugProcess is just a pass-through to get to |
| 497 | // the process plugin. |
| 498 | |
| 499 | if (IsRemote()) { |
| 500 | if (m_remote_platform_sp) |
| 501 | return m_remote_platform_sp->DebugProcess(launch_info, debugger, target, |
| 502 | error); |
| 503 | else |
| 504 | error = |
| 505 | Status::FromErrorString(str: "the platform is not currently connected" ); |
| 506 | } |
| 507 | |
| 508 | if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { |
| 509 | // This is a process attach. Don't need to launch anything. |
| 510 | ProcessAttachInfo attach_info(launch_info); |
| 511 | return Attach(attach_info, debugger, target: &target, error); |
| 512 | } |
| 513 | |
| 514 | ProcessSP process_sp = |
| 515 | target.CreateProcess(listener_sp: launch_info.GetListener(), |
| 516 | plugin_name: launch_info.GetProcessPluginName(), crash_file: nullptr, can_connect: false); |
| 517 | |
| 518 | process_sp->HijackProcessEvents(listener_sp: launch_info.GetHijackListener()); |
| 519 | |
| 520 | // We need to launch and attach to the process. |
| 521 | launch_info.GetFlags().Set(eLaunchFlagDebug); |
| 522 | if (process_sp) |
| 523 | error = process_sp->Launch(launch_info); |
| 524 | |
| 525 | return process_sp; |
| 526 | } |
| 527 | |
| 528 | lldb::ProcessSP PlatformWindows::Attach(ProcessAttachInfo &attach_info, |
| 529 | Debugger &debugger, Target *target, |
| 530 | Status &error) { |
| 531 | error.Clear(); |
| 532 | lldb::ProcessSP process_sp; |
| 533 | if (!IsHost()) { |
| 534 | if (m_remote_platform_sp) |
| 535 | process_sp = |
| 536 | m_remote_platform_sp->Attach(attach_info, debugger, target, error); |
| 537 | else |
| 538 | error = |
| 539 | Status::FromErrorString(str: "the platform is not currently connected" ); |
| 540 | return process_sp; |
| 541 | } |
| 542 | |
| 543 | if (target == nullptr) { |
| 544 | TargetSP new_target_sp; |
| 545 | FileSpec emptyFileSpec; |
| 546 | ArchSpec emptyArchSpec; |
| 547 | |
| 548 | error = debugger.GetTargetList().CreateTarget( |
| 549 | debugger, user_exe_path: "" , triple_str: "" , get_dependent_modules: eLoadDependentsNo, platform_options: nullptr, target_sp&: new_target_sp); |
| 550 | target = new_target_sp.get(); |
| 551 | } |
| 552 | |
| 553 | if (!target || error.Fail()) |
| 554 | return process_sp; |
| 555 | |
| 556 | process_sp = |
| 557 | target->CreateProcess(listener_sp: attach_info.GetListenerForProcess(debugger), |
| 558 | plugin_name: attach_info.GetProcessPluginName(), crash_file: nullptr, can_connect: false); |
| 559 | |
| 560 | process_sp->HijackProcessEvents(listener_sp: attach_info.GetHijackListener()); |
| 561 | if (process_sp) |
| 562 | error = process_sp->Attach(attach_info); |
| 563 | |
| 564 | return process_sp; |
| 565 | } |
| 566 | |
| 567 | void PlatformWindows::GetStatus(Stream &strm) { |
| 568 | Platform::GetStatus(strm); |
| 569 | |
| 570 | #ifdef _WIN32 |
| 571 | llvm::VersionTuple version = HostInfo::GetOSVersion(); |
| 572 | strm << " Host: Windows " << version.getAsString() << '\n'; |
| 573 | #endif |
| 574 | } |
| 575 | |
| 576 | bool PlatformWindows::CanDebugProcess() { return true; } |
| 577 | |
| 578 | ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) { |
| 579 | if (basename.IsEmpty()) |
| 580 | return basename; |
| 581 | |
| 582 | StreamString stream; |
| 583 | stream.Printf(format: "%s.dll" , basename.GetCString()); |
| 584 | return ConstString(stream.GetString()); |
| 585 | } |
| 586 | |
| 587 | size_t |
| 588 | PlatformWindows::GetSoftwareBreakpointTrapOpcode(Target &target, |
| 589 | BreakpointSite *bp_site) { |
| 590 | ArchSpec arch = target.GetArchitecture(); |
| 591 | assert(arch.IsValid()); |
| 592 | const uint8_t *trap_opcode = nullptr; |
| 593 | size_t trap_opcode_size = 0; |
| 594 | |
| 595 | switch (arch.GetMachine()) { |
| 596 | case llvm::Triple::aarch64: { |
| 597 | static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x3e, 0xd4}; // brk #0xf000 |
| 598 | trap_opcode = g_aarch64_opcode; |
| 599 | trap_opcode_size = sizeof(g_aarch64_opcode); |
| 600 | |
| 601 | if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) |
| 602 | return trap_opcode_size; |
| 603 | return 0; |
| 604 | } break; |
| 605 | |
| 606 | case llvm::Triple::arm: |
| 607 | case llvm::Triple::thumb: { |
| 608 | static const uint8_t g_thumb_opcode[] = {0xfe, 0xde}; // udf #0xfe |
| 609 | trap_opcode = g_thumb_opcode; |
| 610 | trap_opcode_size = sizeof(g_thumb_opcode); |
| 611 | |
| 612 | if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) |
| 613 | return trap_opcode_size; |
| 614 | return 0; |
| 615 | } break; |
| 616 | |
| 617 | default: |
| 618 | return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site); |
| 619 | } |
| 620 | } |
| 621 | |
| 622 | std::unique_ptr<UtilityFunction> |
| 623 | PlatformWindows::MakeLoadImageUtilityFunction(ExecutionContext &context, |
| 624 | Status &status) { |
| 625 | // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance? |
| 626 | static constexpr const char kLoaderDecls[] = R"( |
| 627 | extern "C" { |
| 628 | // errhandlingapi.h |
| 629 | |
| 630 | // `LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS` |
| 631 | // |
| 632 | // Directories in the standard search path are not searched. This value cannot |
| 633 | // be combined with `LOAD_WITH_ALTERED_SEARCH_PATH`. |
| 634 | // |
| 635 | // This value represents the recommended maximum number of directories an |
| 636 | // application should include in its DLL search path. |
| 637 | #define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000 |
| 638 | |
| 639 | // WINBASEAPI DWORD WINAPI GetLastError(VOID); |
| 640 | /* __declspec(dllimport) */ uint32_t __stdcall GetLastError(); |
| 641 | |
| 642 | // libloaderapi.h |
| 643 | |
| 644 | // WINBASEAPI DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory(LPCWSTR); |
| 645 | /* __declspec(dllimport) */ void * __stdcall AddDllDirectory(const wchar_t *); |
| 646 | |
| 647 | // WINBASEAPI BOOL WINAPI FreeModule(HMODULE); |
| 648 | /* __declspec(dllimport) */ int __stdcall FreeModule(void *hLibModule); |
| 649 | |
| 650 | // WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize); |
| 651 | /* __declspec(dllimport) */ uint32_t GetModuleFileNameA(void *, char *, uint32_t); |
| 652 | |
| 653 | // WINBASEAPI HMODULE WINAPI LoadLibraryExW(LPCWSTR, HANDLE, DWORD); |
| 654 | /* __declspec(dllimport) */ void * __stdcall LoadLibraryExW(const wchar_t *, void *, uint32_t); |
| 655 | |
| 656 | // corecrt_wstring.h |
| 657 | |
| 658 | // _ACRTIMP size_t __cdecl wcslen(wchar_t const *_String); |
| 659 | /* __declspec(dllimport) */ size_t __cdecl wcslen(const wchar_t *); |
| 660 | |
| 661 | // lldb specific code |
| 662 | |
| 663 | struct __lldb_LoadLibraryResult { |
| 664 | void *ImageBase; |
| 665 | char *ModulePath; |
| 666 | unsigned Length; |
| 667 | unsigned ErrorCode; |
| 668 | }; |
| 669 | |
| 670 | _Static_assert(sizeof(struct __lldb_LoadLibraryResult) <= 3 * sizeof(void *), |
| 671 | "__lldb_LoadLibraryResult size mismatch"); |
| 672 | |
| 673 | void * __lldb_LoadLibraryHelper(const wchar_t *name, const wchar_t *paths, |
| 674 | __lldb_LoadLibraryResult *result) { |
| 675 | for (const wchar_t *path = paths; path && *path; ) { |
| 676 | (void)AddDllDirectory(path); |
| 677 | path += wcslen(path) + 1; |
| 678 | } |
| 679 | |
| 680 | result->ImageBase = LoadLibraryExW(name, nullptr, |
| 681 | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); |
| 682 | if (result->ImageBase == nullptr) |
| 683 | result->ErrorCode = GetLastError(); |
| 684 | else |
| 685 | result->Length = GetModuleFileNameA(result->ImageBase, result->ModulePath, |
| 686 | result->Length); |
| 687 | |
| 688 | return result->ImageBase; |
| 689 | } |
| 690 | } |
| 691 | )" ; |
| 692 | |
| 693 | static constexpr const char kName[] = "__lldb_LoadLibraryHelper" ; |
| 694 | |
| 695 | ProcessSP process = context.GetProcessSP(); |
| 696 | Target &target = process->GetTarget(); |
| 697 | |
| 698 | auto function = target.CreateUtilityFunction(expression: std::string{kLoaderDecls}, name: kName, |
| 699 | language: eLanguageTypeC_plus_plus, |
| 700 | exe_ctx&: context); |
| 701 | if (!function) { |
| 702 | std::string error = llvm::toString(E: function.takeError()); |
| 703 | status = Status::FromErrorStringWithFormat( |
| 704 | format: "LoadLibrary error: could not create utility function: %s" , |
| 705 | error.c_str()); |
| 706 | return nullptr; |
| 707 | } |
| 708 | |
| 709 | TypeSystemClangSP scratch_ts_sp = |
| 710 | ScratchTypeSystemClang::GetForTarget(target); |
| 711 | if (!scratch_ts_sp) |
| 712 | return nullptr; |
| 713 | |
| 714 | CompilerType VoidPtrTy = |
| 715 | scratch_ts_sp->GetBasicType(type: eBasicTypeVoid).GetPointerType(); |
| 716 | CompilerType WCharPtrTy = |
| 717 | scratch_ts_sp->GetBasicType(type: eBasicTypeWChar).GetPointerType(); |
| 718 | |
| 719 | ValueList parameters; |
| 720 | |
| 721 | Value value; |
| 722 | value.SetValueType(Value::ValueType::Scalar); |
| 723 | |
| 724 | value.SetCompilerType(WCharPtrTy); |
| 725 | parameters.PushValue(value); // name |
| 726 | parameters.PushValue(value); // paths |
| 727 | |
| 728 | value.SetCompilerType(VoidPtrTy); |
| 729 | parameters.PushValue(value); // result |
| 730 | |
| 731 | Status error; |
| 732 | std::unique_ptr<UtilityFunction> utility{std::move(*function)}; |
| 733 | utility->MakeFunctionCaller(return_type: VoidPtrTy, arg_value_list: parameters, compilation_thread: context.GetThreadSP(), |
| 734 | error); |
| 735 | if (error.Fail()) { |
| 736 | status = Status::FromErrorStringWithFormat( |
| 737 | format: "LoadLibrary error: could not create function caller: %s" , |
| 738 | error.AsCString()); |
| 739 | return nullptr; |
| 740 | } |
| 741 | |
| 742 | if (!utility->GetFunctionCaller()) { |
| 743 | status = Status::FromErrorString( |
| 744 | str: "LoadLibrary error: could not get function caller" ); |
| 745 | return nullptr; |
| 746 | } |
| 747 | |
| 748 | return utility; |
| 749 | } |
| 750 | |
| 751 | Status PlatformWindows::EvaluateLoaderExpression(Process *process, |
| 752 | const char *expression, |
| 753 | ValueObjectSP &value) { |
| 754 | // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance? |
| 755 | static constexpr const char kLoaderDecls[] = R"( |
| 756 | extern "C" { |
| 757 | // libloaderapi.h |
| 758 | |
| 759 | // WINBASEAPI DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory(LPCWSTR); |
| 760 | /* __declspec(dllimport) */ void * __stdcall AddDllDirectory(const wchar_t *); |
| 761 | |
| 762 | // WINBASEAPI BOOL WINAPI FreeModule(HMODULE); |
| 763 | /* __declspec(dllimport) */ int __stdcall FreeModule(void *); |
| 764 | |
| 765 | // WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE, LPSTR, DWORD); |
| 766 | /* __declspec(dllimport) */ uint32_t GetModuleFileNameA(void *, char *, uint32_t); |
| 767 | |
| 768 | // WINBASEAPI HMODULE WINAPI LoadLibraryExW(LPCWSTR, HANDLE, DWORD); |
| 769 | /* __declspec(dllimport) */ void * __stdcall LoadLibraryExW(const wchar_t *, void *, uint32_t); |
| 770 | } |
| 771 | )" ; |
| 772 | |
| 773 | if (DynamicLoader *loader = process->GetDynamicLoader()) { |
| 774 | Status result = loader->CanLoadImage(); |
| 775 | if (result.Fail()) |
| 776 | return result; |
| 777 | } |
| 778 | |
| 779 | ThreadSP thread = process->GetThreadList().GetExpressionExecutionThread(); |
| 780 | if (!thread) |
| 781 | return Status::FromErrorString(str: "selected thread is invalid" ); |
| 782 | |
| 783 | StackFrameSP frame = thread->GetStackFrameAtIndex(idx: 0); |
| 784 | if (!frame) |
| 785 | return Status::FromErrorString(str: "frame 0 is invalid" ); |
| 786 | |
| 787 | ExecutionContext context; |
| 788 | frame->CalculateExecutionContext(exe_ctx&: context); |
| 789 | |
| 790 | EvaluateExpressionOptions options; |
| 791 | options.SetUnwindOnError(true); |
| 792 | options.SetIgnoreBreakpoints(true); |
| 793 | options.SetExecutionPolicy(eExecutionPolicyAlways); |
| 794 | options.SetLanguage(eLanguageTypeC_plus_plus); |
| 795 | // LoadLibraryEx{A,W}/FreeLibrary cannot raise exceptions which we can handle. |
| 796 | // They may potentially throw SEH exceptions which we do not know how to |
| 797 | // handle currently. |
| 798 | options.SetTrapExceptions(false); |
| 799 | options.SetTimeout(process->GetUtilityExpressionTimeout()); |
| 800 | |
| 801 | ExpressionResults result = UserExpression::Evaluate( |
| 802 | exe_ctx&: context, options, expr_cstr: expression, expr_prefix: kLoaderDecls, result_valobj_sp&: value); |
| 803 | if (result != eExpressionCompleted) |
| 804 | return value ? value->GetError().Clone() : Status("unknown error" ); |
| 805 | |
| 806 | if (value && value->GetError().Fail()) |
| 807 | return value->GetError().Clone(); |
| 808 | |
| 809 | return Status(); |
| 810 | } |
| 811 | |