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