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.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
164uint32_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
416Status 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
443Status 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
459ProcessSP 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
509lldb::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
547void 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
556bool PlatformWindows::CanDebugProcess() { return true; }
557
558ConstString 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
567size_t
568PlatformWindows::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
602std::unique_ptr<UtilityFunction>
603PlatformWindows::MakeLoadImageUtilityFunction(ExecutionContext &context,
604 Status &status) {
605 // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance?
606 static constexpr const char kLoaderDecls[] = R"(
607extern "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
643struct __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
653void * __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
728Status 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"(
733extern "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

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