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