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 
CreateInstance(bool force,const lldb_private::ArchSpec * arch)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 
GetPluginDescriptionStatic(bool is_host)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 
Initialize()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         PlatformWindows::GetPluginNameStatic(false),
103         PlatformWindows::GetPluginDescriptionStatic(false),
104         PlatformWindows::CreateInstance);
105   }
106 }
107 
Terminate()108 void PlatformWindows::Terminate() {
109   if (g_initialize_count > 0) {
110     if (--g_initialize_count == 0) {
111       PluginManager::UnregisterPlugin(PlatformWindows::CreateInstance);
112     }
113   }
114 
115   Platform::Terminate();
116 }
117 
118 /// Default Constructor
PlatformWindows(bool is_host)119 PlatformWindows::PlatformWindows(bool is_host) : RemoteAwarePlatform(is_host) {
120   const auto &AddArch = [&](const ArchSpec &spec) {
121     if (llvm::any_of(m_supported_architectures, [spec](const ArchSpec &rhs) {
122           return spec.IsExactMatch(rhs);
123         }))
124       return;
125     if (spec.IsValid())
126       m_supported_architectures.push_back(spec);
127   };
128   AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault));
129   AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32));
130   AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64));
131 }
132 
ConnectRemote(Args & args)133 Status PlatformWindows::ConnectRemote(Args &args) {
134   Status error;
135   if (IsHost()) {
136     error.SetErrorStringWithFormatv(
137         "can't connect to the host platform '{0}', always connected",
138         GetPluginName());
139   } else {
140     if (!m_remote_platform_sp)
141       m_remote_platform_sp =
142           platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(
143               /*force=*/true, 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 
DoLoadImage(Process * process,const FileSpec & remote_file,const std::vector<std::string> * paths,Status & error,FileSpec * loaded_image)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(context);
181 
182   Status status;
183   UtilityFunction *loader =
184       process->GetLoadImageUtilityFunction(this, [&]() -> 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(remote_file.GetPath(), name)) {
199     error.SetErrorString("LoadLibrary error: could not convert path to UCS2");
200     return LLDB_INVALID_IMAGE_TOKEN;
201   }
202   name.emplace_back(L'\0');
203 
204   /* Inject name paramter into inferior */
205   lldb::addr_t injected_name =
206       process->AllocateMemory(name.size() * sizeof(llvm::UTF16),
207                               ePermissionsReadable | ePermissionsWritable,
208                               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([process, injected_name]() {
216     process->DeallocateMemory(injected_name);
217   });
218 
219   process->WriteMemory(injected_name, name.data(),
220                        name.size() * sizeof(llvm::UTF16), 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(path, buffer))
239         continue;
240 
241       search_paths.append(std::begin(buffer), std::end(buffer));
242       search_paths.emplace_back(L'\0');
243     }
244     search_paths.emplace_back(L'\0');
245 
246     injected_paths =
247         process->AllocateMemory(search_paths.size() * sizeof(llvm::UTF16),
248                                 ePermissionsReadable | ePermissionsWritable,
249                                 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([process, injected_paths]() {
257       process->DeallocateMemory(injected_paths);
258     });
259 
260     process->WriteMemory(injected_paths, search_paths.data(),
261                          search_paths.size() * sizeof(llvm::UTF16), 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(injected_length + 1,
277                               ePermissionsReadable | ePermissionsWritable,
278                               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([process, injected_module_path]() {
287     process->DeallocateMemory(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(3 * word_size,
294                               ePermissionsReadable | ePermissionsWritable,
295                               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([process, injected_result]() {
303     process->DeallocateMemory(injected_result);
304   });
305 
306   process->WritePointerToMemory(injected_result + word_size,
307                                 injected_module_path, 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(injected_result + 2 * word_size,
316                                Scalar{injected_length}, sizeof(unsigned),
317                                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(0)->GetScalar() = injected_name;
327   parameters.GetValueAtIndex(1)->GetScalar() = injected_paths;
328   parameters.GetValueAtIndex(2)->GetScalar() = injected_result;
329 
330   lldb::addr_t injected_parameters = LLDB_INVALID_ADDRESS;
331   diagnostics.Clear();
332   if (!invocation->WriteFunctionArguments(context, injected_parameters,
333                                           parameters, 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([invocation, &context, injected_parameters]() {
341         invocation->DeallocateFunctionResults(context, injected_parameters);
342       });
343 
344   TypeSystemClangSP scratch_ts_sp =
345       ScratchTypeSystemClang::GetForTarget(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(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(context, &injected_parameters, options,
373                                   diagnostics, 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(injected_result, 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(injected_result + 2 * word_size + sizeof(unsigned),
392                                                word_size, 0, 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(injected_module_path, module_path, 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(module_path, llvm::sys::path::Style::native);
413   return process->AddImageToken(token);
414 }
415 
UnloadImage(Process * process,uint32_t image_token)416 Status PlatformWindows::UnloadImage(Process *process, uint32_t image_token) {
417   const addr_t address = process->GetImagePtrFromToken(image_token);
418   if (address == LLDB_INVALID_ADDRESS)
419     return Status("invalid image token");
420 
421   StreamString expression;
422   expression.Printf("FreeLibrary((HMODULE)0x%" PRIx64 ")", address);
423 
424   ValueObjectSP value;
425   Status result =
426       EvaluateLoaderExpression(process, 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(1))
436       return Status("expression failed: \"%s\"", expression.GetData());
437     process->ResetImageToken(image_token);
438   }
439 
440   return Status();
441 }
442 
DisconnectRemote()443 Status PlatformWindows::DisconnectRemote() {
444   Status error;
445 
446   if (IsHost()) {
447     error.SetErrorStringWithFormatv(
448         "can't disconnect from the host platform '{0}', always connected",
449         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 
DebugProcess(ProcessLaunchInfo & launch_info,Debugger & debugger,Target & target,Status & error)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 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, error);
493   }
494 
495   ProcessSP process_sp =
496       target.CreateProcess(launch_info.GetListener(),
497                            launch_info.GetProcessPluginName(), nullptr, false);
498 
499   process_sp->HijackProcessEvents(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 
Attach(ProcessAttachInfo & attach_info,Debugger & debugger,Target * target,Status & error)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, "", "", eLoadDependentsNo, nullptr, new_target_sp);
530     target = new_target_sp.get();
531   }
532 
533   if (!target || error.Fail())
534     return process_sp;
535 
536   const char *plugin_name = attach_info.GetProcessPluginName();
537   process_sp = target->CreateProcess(
538       attach_info.GetListenerForProcess(debugger), plugin_name, nullptr, false);
539 
540   process_sp->HijackProcessEvents(attach_info.GetHijackListener());
541   if (process_sp)
542     error = process_sp->Attach(attach_info);
543 
544   return process_sp;
545 }
546 
GetStatus(Stream & strm)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 
CanDebugProcess()556 bool PlatformWindows::CanDebugProcess() { return true; }
557 
GetFullNameForDylib(ConstString basename)558 ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) {
559   if (basename.IsEmpty())
560     return basename;
561 
562   StreamString stream;
563   stream.Printf("%s.dll", basename.GetCString());
564   return ConstString(stream.GetString());
565 }
566 
567 size_t
GetSoftwareBreakpointTrapOpcode(Target & target,BreakpointSite * bp_site)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>
MakeLoadImageUtilityFunction(ExecutionContext & context,Status & status)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(std::string{kLoaderDecls}, kName,
679                                                eLanguageTypeC_plus_plus,
680                                                context);
681   if (!function) {
682     std::string error = llvm::toString(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(eBasicTypeVoid).GetPointerType();
695   CompilerType WCharPtrTy =
696       scratch_ts_sp->GetBasicType(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(VoidPtrTy, parameters, 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 
EvaluateLoaderExpression(Process * process,const char * expression,ValueObjectSP & value)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(0);
761   if (!frame)
762     return Status("frame 0 is invalid");
763 
764   ExecutionContext context;
765   frame->CalculateExecutionContext(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       context, options, expression, kLoaderDecls, 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