[Lldb-commits] [lldb] f1585a4 - Windows: support `DoLoadImage`

Saleem Abdulrasool via lldb-commits lldb-commits at lists.llvm.org
Sat Dec 4 11:12:08 PST 2021


Author: Saleem Abdulrasool
Date: 2021-12-04T11:11:47-08:00
New Revision: f1585a4b47cc9c08c9a4c10058597f4b7468c227

URL: https://github.com/llvm/llvm-project/commit/f1585a4b47cc9c08c9a4c10058597f4b7468c227
DIFF: https://github.com/llvm/llvm-project/commit/f1585a4b47cc9c08c9a4c10058597f4b7468c227.diff

LOG: Windows: support `DoLoadImage`

This implements `DoLoadImage` and `UnloadImage` in the Windows platform
plugin modelled after the POSIX platform plugin.  This was previously
unimplemented and resulted in a difficult to decipher error without any
logging.

This implementation is intended to support enables the use of LLDB's
Swift REPL on Windows.

Paths which are added to the library search path are persistent and
applied to all subsequent loads.  This can be adjusted in the future by
storing all the cookies and restoring the path prior to returning from
the helper.  However, the dynamic path count makes this a bit more
challenging.

Reviewed By: @JDevlieghere
Differential Revision: https://reviews.llvm.org/D77287

Added: 
    lldb/test/Shell/Process/Windows/process_load.cpp

Modified: 
    lldb/source/Plugins/Platform/Windows/CMakeLists.txt
    lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
    lldb/source/Plugins/Platform/Windows/PlatformWindows.h

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Platform/Windows/CMakeLists.txt b/lldb/source/Plugins/Platform/Windows/CMakeLists.txt
index 49a197cdaff36..28c174dc4d95e 100644
--- a/lldb/source/Plugins/Platform/Windows/CMakeLists.txt
+++ b/lldb/source/Plugins/Platform/Windows/CMakeLists.txt
@@ -6,4 +6,7 @@ add_lldb_library(lldbPluginPlatformWindows PLUGIN
     lldbCore
     lldbHost
     lldbTarget
+
+   LINK_COMPONENTS
+    Support
   )

diff  --git a/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp b/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
index 8714953a9cdb8..0e25e9a8199bd 100644
--- a/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
+++ b/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 @@ Status PlatformWindows::ConnectRemote(Args &args) {
   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 @@ PlatformWindows::GetSoftwareBreakpointTrapOpcode(Target &target,
     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();
+}

diff  --git a/lldb/source/Plugins/Platform/Windows/PlatformWindows.h b/lldb/source/Plugins/Platform/Windows/PlatformWindows.h
index 1708e08f3d46c..34f90843249a5 100644
--- a/lldb/source/Plugins/Platform/Windows/PlatformWindows.h
+++ b/lldb/source/Plugins/Platform/Windows/PlatformWindows.h
@@ -44,6 +44,15 @@ class PlatformWindows : public RemoteAwarePlatform {
 
   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 @@ class PlatformWindows : public RemoteAwarePlatform {
                                          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

diff  --git a/lldb/test/Shell/Process/Windows/process_load.cpp b/lldb/test/Shell/Process/Windows/process_load.cpp
new file mode 100644
index 0000000000000..43bf45865f9ba
--- /dev/null
+++ b/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.


        


More information about the lldb-commits mailing list