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
37using namespace lldb;
38using namespace lldb_private;
39
40LLDB_PLUGIN_DEFINE(PlatformWindows)
41
42static uint32_t g_initialize_count = 0;
43
44PlatformSP 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
86llvm::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
91void 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
108void 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
119PlatformWindows::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
133Status 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
165uint32_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
432Status 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
460Status 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
477ProcessSP 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
528lldb::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
567void 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
576bool PlatformWindows::CanDebugProcess() { return true; }
577
578ConstString 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
587size_t
588PlatformWindows::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
622std::unique_ptr<UtilityFunction>
623PlatformWindows::MakeLoadImageUtilityFunction(ExecutionContext &context,
624 Status &status) {
625 // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance?
626 static constexpr const char kLoaderDecls[] = R"(
627extern "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
663struct __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
673void * __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
751Status 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"(
756extern "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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp