This revision was automatically updated to reflect the committed changes.
compnerd marked an inline comment as done.
Closed by commit rGf1585a4b47cc: Windows: support `DoLoadImage` (authored by 
compnerd).

Changed prior to commit:
  https://reviews.llvm.org/D77287?vs=390249&id=391847#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77287/new/

https://reviews.llvm.org/D77287

Files:
  lldb/source/Plugins/Platform/Windows/CMakeLists.txt
  lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
  lldb/source/Plugins/Platform/Windows/PlatformWindows.h
  lldb/test/Shell/Process/Windows/process_load.cpp

Index: lldb/test/Shell/Process/Windows/process_load.cpp
===================================================================
--- /dev/null
+++ lldb/test/Shell/Process/Windows/process_load.cpp
@@ -0,0 +1,12 @@
+// clang-format off
+
+// REQUIRES: system-windows
+// RUN: %build --compiler=clang-cl -o %t.exe -- %s
+// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -o "b main" -o "process launch" -o "process load kernel32.dll" | FileCheck %s
+
+int main(int argc, char *argv[]) {
+  return 0;
+}
+
+// CHECK: "Loading "kernel32.dll"...ok{{.*}}
+// CHECK: Image 0 loaded.
Index: lldb/source/Plugins/Platform/Windows/PlatformWindows.h
===================================================================
--- lldb/source/Plugins/Platform/Windows/PlatformWindows.h
+++ lldb/source/Plugins/Platform/Windows/PlatformWindows.h
@@ -44,6 +44,15 @@
 
   lldb_private::Status DisconnectRemote() override;
 
+  uint32_t DoLoadImage(lldb_private::Process *process,
+                       const lldb_private::FileSpec &remote_file,
+                       const std::vector<std::string> *paths,
+                       lldb_private::Status &error,
+                       lldb_private::FileSpec *loaded_path) override;
+
+  lldb_private::Status UnloadImage(lldb_private::Process *process,
+                                   uint32_t image_token) override;
+
   lldb::ProcessSP DebugProcess(lldb_private::ProcessLaunchInfo &launch_info,
                                lldb_private::Debugger &debugger,
                                lldb_private::Target &target,
@@ -71,6 +80,15 @@
                                          BreakpointSite *bp_site) override;
 
   std::vector<ArchSpec> m_supported_architectures;
+
+private:
+  std::unique_ptr<lldb_private::UtilityFunction>
+  MakeLoadImageUtilityFunction(lldb_private::ExecutionContext &context,
+                               lldb_private::Status &status);
+
+  lldb_private::Status EvaluateLoaderExpression(lldb_private::Process *process,
+                                                const char *expression,
+                                                lldb::ValueObjectSP &value);
 };
 
 } // namespace lldb_private
Index: lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
===================================================================
--- lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
+++ lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
@@ -19,10 +19,20 @@
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/PluginManager.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
 #include "lldb/Host/HostInfo.h"
+#include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Utility/Status.h"
 
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/Support/ConvertUTF.h"
+
 using namespace lldb;
 using namespace lldb_private;
 
@@ -151,6 +161,283 @@
   return error;
 }
 
+uint32_t PlatformWindows::DoLoadImage(Process *process,
+                                      const FileSpec &remote_file,
+                                      const std::vector<std::string> *paths,
+                                      Status &error, FileSpec *loaded_image) {
+  DiagnosticManager diagnostics;
+
+  if (loaded_image)
+    loaded_image->Clear();
+
+  ThreadSP thread = process->GetThreadList().GetExpressionExecutionThread();
+  if (!thread) {
+    error.SetErrorString("LoadLibrary error: no thread available to invoke LoadLibrary");
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  ExecutionContext context;
+  thread->CalculateExecutionContext(context);
+
+  Status status;
+  UtilityFunction *loader =
+      process->GetLoadImageUtilityFunction(this, [&]() -> std::unique_ptr<UtilityFunction> {
+        return MakeLoadImageUtilityFunction(context, status);
+      });
+  if (loader == nullptr)
+    return LLDB_INVALID_IMAGE_TOKEN;
+
+  FunctionCaller *invocation = loader->GetFunctionCaller();
+  if (!invocation) {
+    error.SetErrorString("LoadLibrary error: could not get function caller");
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  /* Convert name */
+  llvm::SmallVector<llvm::UTF16, 261> name;
+  if (!llvm::convertUTF8ToUTF16String(remote_file.GetPath(), name)) {
+    error.SetErrorString("LoadLibrary error: could not convert path to UCS2");
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  name.emplace_back(L'\0');
+
+  /* Inject name paramter into inferior */
+  lldb::addr_t injected_name =
+      process->AllocateMemory(name.size() * sizeof(llvm::UTF16),
+                              ePermissionsReadable | ePermissionsWritable,
+                              status);
+  if (injected_name == LLDB_INVALID_ADDRESS) {
+    error.SetErrorStringWithFormat("LoadLibrary error: unable to allocate memory for name: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  auto name_cleanup = llvm::make_scope_exit([process, injected_name]() {
+    process->DeallocateMemory(injected_name);
+  });
+
+  process->WriteMemory(injected_name, name.data(),
+                       name.size() * sizeof(llvm::UTF16), status);
+  if (status.Fail()) {
+    error.SetErrorStringWithFormat("LoadLibrary error: unable to write name: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  /* Inject paths parameter into inferior */
+  lldb::addr_t injected_paths{0x0};
+  llvm::Optional<llvm::detail::scope_exit<std::function<void()>>> paths_cleanup;
+  if (paths) {
+    llvm::SmallVector<llvm::UTF16, 261> search_paths;
+
+    for (const auto &path : *paths) {
+      if (path.empty())
+        continue;
+
+      llvm::SmallVector<llvm::UTF16, 261> buffer;
+      if (!llvm::convertUTF8ToUTF16String(path, buffer))
+        continue;
+
+      search_paths.append(std::begin(buffer), std::end(buffer));
+      search_paths.emplace_back(L'\0');
+    }
+    search_paths.emplace_back(L'\0');
+
+    injected_paths =
+        process->AllocateMemory(search_paths.size() * sizeof(llvm::UTF16),
+                                ePermissionsReadable | ePermissionsWritable,
+                                status);
+    if (injected_paths == LLDB_INVALID_ADDRESS) {
+      error.SetErrorStringWithFormat("LoadLibrary error: unable to allocate memory for paths: %s",
+                                     status.AsCString());
+      return LLDB_INVALID_IMAGE_TOKEN;
+    }
+
+    paths_cleanup.emplace([process, injected_paths]() {
+      process->DeallocateMemory(injected_paths);
+    });
+
+    process->WriteMemory(injected_paths, search_paths.data(),
+                         search_paths.size() * sizeof(llvm::UTF16), status);
+    if (status.Fail()) {
+      error.SetErrorStringWithFormat("LoadLibrary error: unable to write paths: %s",
+                                     status.AsCString());
+      return LLDB_INVALID_IMAGE_TOKEN;
+    }
+  }
+
+  /* Inject wszModulePath into inferior */
+  // FIXME(compnerd) should do something better for the length?
+  // GetModuleFileNameA is likely limited to PATH_MAX rather than the NT path
+  // limit.
+  unsigned injected_length = 261;
+
+  lldb::addr_t injected_module_path =
+      process->AllocateMemory(injected_length + 1,
+                              ePermissionsReadable | ePermissionsWritable,
+                              status);
+  if (injected_module_path == LLDB_INVALID_ADDRESS) {
+    error.SetErrorStringWithFormat("LoadLibrary error: unable to allocate memory for module location: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  auto injected_module_path_cleanup =
+      llvm::make_scope_exit([process, injected_module_path]() {
+    process->DeallocateMemory(injected_module_path);
+  });
+
+  /* Inject __lldb_LoadLibraryResult into inferior */
+  const uint32_t word_size = process->GetAddressByteSize();
+  lldb::addr_t injected_result =
+      process->AllocateMemory(3 * word_size,
+                              ePermissionsReadable | ePermissionsWritable,
+                              status);
+  if (status.Fail()) {
+    error.SetErrorStringWithFormat("LoadLibrary error: could not allocate memory for result: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  auto result_cleanup = llvm::make_scope_exit([process, injected_result]() {
+    process->DeallocateMemory(injected_result);
+  });
+
+  process->WritePointerToMemory(injected_result + word_size,
+                                injected_module_path, status);
+  if (status.Fail()) {
+    error.SetErrorStringWithFormat("LoadLibrary error: could not initialize result: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  // XXX(compnerd) should we use the compiler to get the sizeof(unsigned)?
+  process->WriteScalarToMemory(injected_result + 2 * word_size,
+                               Scalar{injected_length}, sizeof(unsigned),
+                               status);
+  if (status.Fail()) {
+    error.SetErrorStringWithFormat("LoadLibrary error: could not initialize result: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  /* Setup Formal Parameters */
+  ValueList parameters = invocation->GetArgumentValues();
+  parameters.GetValueAtIndex(0)->GetScalar() = injected_name;
+  parameters.GetValueAtIndex(1)->GetScalar() = injected_paths;
+  parameters.GetValueAtIndex(2)->GetScalar() = injected_result;
+
+  lldb::addr_t injected_parameters = LLDB_INVALID_ADDRESS;
+  diagnostics.Clear();
+  if (!invocation->WriteFunctionArguments(context, injected_parameters,
+                                          parameters, diagnostics)) {
+    error.SetErrorStringWithFormat("LoadLibrary error: unable to write function parameters: %s",
+                                   diagnostics.GetString().c_str());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  auto parameter_cleanup = llvm::make_scope_exit([invocation, &context, injected_parameters]() {
+    invocation->DeallocateFunctionResults(context, injected_parameters);
+  });
+
+  TypeSystemClang *ast =
+      ScratchTypeSystemClang::GetForTarget(process->GetTarget());
+  if (!ast) {
+    error.SetErrorString("LoadLibrary error: unable to get (clang) type system");
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  /* Setup Return Type */
+  CompilerType VoidPtrTy = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+  Value value;
+  value.SetCompilerType(VoidPtrTy);
+
+  /* Invoke expression */
+  EvaluateExpressionOptions options;
+  options.SetExecutionPolicy(eExecutionPolicyAlways);
+  options.SetLanguage(eLanguageTypeC_plus_plus);
+  options.SetIgnoreBreakpoints(true);
+  options.SetUnwindOnError(true);
+  // LoadLibraryEx{A,W}/FreeLibrary cannot raise exceptions which we can handle.
+  // They may potentially throw SEH exceptions which we do not know how to
+  // handle currently.
+  options.SetTrapExceptions(false);
+  options.SetTimeout(process->GetUtilityExpressionTimeout());
+  options.SetIsForUtilityExpr(true);
+
+  ExpressionResults result =
+      invocation->ExecuteFunction(context, &injected_parameters, options,
+                                  diagnostics, value);
+  if (result != eExpressionCompleted) {
+    error.SetErrorStringWithFormat("LoadLibrary error: failed to execute LoadLibrary helper: %s",
+                                   diagnostics.GetString().c_str());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  /* Read result */
+  lldb::addr_t token = process->ReadPointerFromMemory(injected_result, status);
+  if (status.Fail()) {
+    error.SetErrorStringWithFormat("LoadLibrary error: could not read the result: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  if (token == NULL) {
+    // XXX(compnerd) should we use the compiler to get the sizeof(unsigned)?
+    uint64_t error_code =
+        process->ReadUnsignedIntegerFromMemory(injected_result + 2 * word_size + sizeof(unsigned),
+                                               word_size, 0, status);
+    if (status.Fail()) {
+      error.SetErrorStringWithFormat("LoadLibrary error: could not read error status: %s",
+                                     status.AsCString());
+      return LLDB_INVALID_IMAGE_TOKEN;
+    }
+
+    error.SetErrorStringWithFormat("LoadLibrary Error: %lu", error_code);
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  std::string module_path;
+  process->ReadCStringFromMemory(injected_module_path, module_path, status);
+  if (status.Fail()) {
+    error.SetErrorStringWithFormat("LoadLibrary error: could not read module path: %s",
+                                   status.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+
+  if (loaded_image)
+    loaded_image->SetFile(module_path, llvm::sys::path::Style::native);
+  return process->AddImageToken(token);
+}
+
+Status PlatformWindows::UnloadImage(Process *process, uint32_t image_token) {
+  const addr_t address = process->GetImagePtrFromToken(image_token);
+  if (address == LLDB_INVALID_ADDRESS)
+    return Status("invalid image token");
+
+  StreamString expression;
+  expression.Printf("FreeLibrary((HMODULE)0x%" PRIx64 ")", address);
+
+  ValueObjectSP value;
+  Status result =
+      EvaluateLoaderExpression(process, expression.GetData(), value);
+  if (result.Fail())
+    return result;
+
+  if (value->GetError().Fail())
+    return value->GetError();
+
+  Scalar scalar;
+  if (value->ResolveValue(scalar)) {
+    if (scalar.UInt(1))
+      return Status("expression failed: \"%s\"", expression.GetData());
+    process->ResetImageToken(image_token);
+  }
+
+  return Status();
+}
+
 Status PlatformWindows::DisconnectRemote() {
   Status error;
 
@@ -307,3 +594,188 @@
     return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site);
   }
 }
+
+std::unique_ptr<UtilityFunction>
+PlatformWindows::MakeLoadImageUtilityFunction(ExecutionContext &context,
+                                              Status &status) {
+  // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance?
+  static constexpr const char kLoaderDecls[] = R"(
+extern "C" {
+// errhandlingapi.h
+
+// `LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS`
+//
+// Directories in the standard search path are not searched. This value cannot
+// be combined with `LOAD_WITH_ALTERED_SEARCH_PATH`.
+//
+// This value represents the recommended maximum number of directories an
+// application should include in its DLL search path.
+#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
+
+// WINBASEAPI DWORD WINAPI GetLastError(VOID);
+/* __declspec(dllimport) */ uint32_t __stdcall GetLastError();
+
+// libloaderapi.h
+
+// WINBASEAPI DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory(LPCWSTR);
+/* __declspec(dllimport) */ void * __stdcall AddDllDirectory(const wchar_t *);
+
+// WINBASEAPI BOOL WINAPI FreeModule(HMODULE);
+/* __declspec(dllimport) */ int __stdcall FreeModule(void *hLibModule);
+
+// WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
+/* __declspec(dllimport) */ uint32_t GetModuleFileNameA(void *, char *, uint32_t);
+
+// WINBASEAPI HMODULE WINAPI LoadLibraryExW(LPCWSTR, HANDLE, DWORD);
+/* __declspec(dllimport) */ void * __stdcall LoadLibraryExW(const wchar_t *, void *, uint32_t);
+
+// corecrt_wstring.h
+
+// _ACRTIMP size_t __cdecl wcslen(wchar_t const *_String);
+/* __declspec(dllimport) */ size_t __cdecl wcslen(const wchar_t *);
+
+// lldb specific code
+
+struct __lldb_LoadLibraryResult {
+  void *ImageBase;
+  char *ModulePath;
+  unsigned Length;
+  unsigned ErrorCode;
+};
+
+_Static_assert(sizeof(struct __lldb_LoadLibraryResult) <= 3 * sizeof(void *),
+               "__lldb_LoadLibraryResult size mismatch");
+
+void * __lldb_LoadLibraryHelper(const wchar_t *name, const wchar_t *paths,
+                                __lldb_LoadLibraryResult *result) {
+  for (const wchar_t *path = paths; path; ) {
+    (void)AddDllDirectory(path);
+    path += wcslen(path) + 1;
+  }
+
+  result->ImageBase = LoadLibraryExW(name, nullptr,
+                                     LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+  if (result->ImageBase == nullptr)
+    result->ErrorCode = GetLastError();
+  else
+    result->Length = GetModuleFileNameA(result->ImageBase, result->ModulePath,
+                                        result->Length);
+
+  return result->ImageBase;
+}
+}
+  )";
+
+  static constexpr const char kName[] = "__lldb_LoadLibraryHelper";
+
+  ProcessSP process = context.GetProcessSP();
+  Target &target = process->GetTarget();
+
+  auto function = target.CreateUtilityFunction(std::string{kLoaderDecls}, kName,
+                                               eLanguageTypeC_plus_plus,
+                                               context);
+  if (!function) {
+    std::string error = llvm::toString(function.takeError());
+    status.SetErrorStringWithFormat("LoadLibrary error: could not create utility function: %s",
+                                    error.c_str());
+    return nullptr;
+  }
+
+  TypeSystemClang *ast = ScratchTypeSystemClang::GetForTarget(target);
+  if (!ast)
+    return nullptr;
+
+  CompilerType VoidPtrTy = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+  CompilerType WCharPtrTy = ast->GetBasicType(eBasicTypeWChar).GetPointerType();
+
+  ValueList parameters;
+
+  Value value;
+  value.SetValueType(Value::ValueType::Scalar);
+
+  value.SetCompilerType(WCharPtrTy);
+  parameters.PushValue(value);  // name
+  parameters.PushValue(value);  // paths
+
+  value.SetCompilerType(VoidPtrTy);
+  parameters.PushValue(value);  // result
+
+  Status error;
+  std::unique_ptr<UtilityFunction> utility{std::move(*function)};
+  utility->MakeFunctionCaller(VoidPtrTy, parameters, context.GetThreadSP(),
+                              error);
+  if (error.Fail()) {
+    status.SetErrorStringWithFormat("LoadLibrary error: could not create function caller: %s",
+                                    error.AsCString());
+    return nullptr;
+  }
+
+  if (!utility->GetFunctionCaller()) {
+    status.SetErrorString("LoadLibrary error: could not get function caller");
+    return nullptr;
+  }
+
+  return utility;
+}
+
+Status PlatformWindows::EvaluateLoaderExpression(Process *process,
+                                                 const char *expression,
+                                                 ValueObjectSP &value) {
+  // FIXME(compnerd) `-fdeclspec` is not passed to the clang instance?
+  static constexpr const char kLoaderDecls[] = R"(
+extern "C" {
+// libloaderapi.h
+
+// WINBASEAPI DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory(LPCWSTR);
+/* __declspec(dllimport) */ void * __stdcall AddDllDirectory(const wchar_t *);
+
+// WINBASEAPI BOOL WINAPI FreeModule(HMODULE);
+/* __declspec(dllimport) */ int __stdcall FreeModule(void *);
+
+// WINBASEAPI DWORD WINAPI GetModuleFileNameA(HMODULE, LPSTR, DWORD);
+/* __declspec(dllimport) */ uint32_t GetModuleFileNameA(void *, char *, uint32_t);
+
+// WINBASEAPI HMODULE WINAPI LoadLibraryExW(LPCWSTR, HANDLE, DWORD);
+/* __declspec(dllimport) */ void * __stdcall LoadLibraryExW(const wchar_t *, void *, uint32_t);
+}
+  )";
+
+  if (DynamicLoader *loader = process->GetDynamicLoader()) {
+    Status result = loader->CanLoadImage();
+    if (result.Fail())
+      return result;
+  }
+
+  ThreadSP thread = process->GetThreadList().GetExpressionExecutionThread();
+  if (!thread)
+    return Status("selected thread is invalid");
+
+  StackFrameSP frame = thread->GetStackFrameAtIndex(0);
+  if (!frame)
+    return Status("frame 0 is invalid");
+
+  ExecutionContext context;
+  frame->CalculateExecutionContext(context);
+
+  EvaluateExpressionOptions options;
+  options.SetUnwindOnError(true);
+  options.SetIgnoreBreakpoints(true);
+  options.SetExecutionPolicy(eExecutionPolicyAlways);
+  options.SetLanguage(eLanguageTypeC_plus_plus);
+  // LoadLibraryEx{A,W}/FreeLibrary cannot raise exceptions which we can handle.
+  // They may potentially throw SEH exceptions which we do not know how to
+  // handle currently.
+  options.SetTrapExceptions(false);
+  options.SetTimeout(process->GetUtilityExpressionTimeout());
+
+  Status error;
+  ExpressionResults result = UserExpression::Evaluate(
+      context, options, expression, kLoaderDecls, value, error);
+  if (result != eExpressionCompleted)
+    return error;
+
+  if (value->GetError().Fail())
+    return value->GetError();
+
+  return Status();
+}
Index: lldb/source/Plugins/Platform/Windows/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Platform/Windows/CMakeLists.txt
+++ lldb/source/Plugins/Platform/Windows/CMakeLists.txt
@@ -6,4 +6,7 @@
     lldbCore
     lldbHost
     lldbTarget
+
+   LINK_COMPONENTS
+    Support
   )
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to