[Lldb-commits] [lldb] [lldb][windows] add sibling-import fallback to LoadImage helper (PR #197218)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Tue May 12 08:07:27 PDT 2026
https://github.com/charles-zablit created https://github.com/llvm/llvm-project/pull/197218
`PlatformWindows::DoLoadImage` injects `__lldb_LoadLibraryHelper` which registers each caller-supplied search path via `AddDllDirectory()` and calls `LoadLibraryExW(name, nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)`. In practice however, this flow fails for a common layout where a DLL lives next to its sibling imports. LoadLibraryExW returns ERROR_CANT_RESOLVE_FILENAME and the load is reported as unrecoverable, even though every required DLL is present in one of the search paths.
This patch adds a fallback after the first `LoadLibraryExW(name, nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)` call: if it fails, iterate the caller-provided search paths and try `LoadLibraryExW(path, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH)`. `LOAD_WITH_ALTERED_SEARCH_PATH` instructs the Windows loader to add the DLL's own directory to the dependency search.
See https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa.
This enables 2 tests and fixes an issue when running tests on Windows in swiftlang lldb.
>From 49460d66ef2a6a6ed75a8d4c8c0fd302f61ea2a3 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 12 May 2026 16:00:13 +0100
Subject: [PATCH] [lldb][windows] add sibling-import fallback to LoadImage
helper
---
.../Platform/Windows/PlatformWindows.cpp | 31 +++++++++++++++++++
.../TestSBCommandReturnObject.py | 3 --
.../multiple-targets/TestMultipleTargets.py | 3 --
3 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp b/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
index 9e11b66068381..6f358d8bf4980 100644
--- a/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
+++ b/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
@@ -645,6 +645,11 @@ extern "C" {
// application should include in its DLL search path.
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
+// If this value is used, and lpFileName specifies an absolute path, the system
+// uses the alternate file search strategy to find associated executable
+// modules.
+#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
+
// WINBASEAPI DWORD WINAPI GetLastError(VOID);
/* __declspec(dllimport) */ uint32_t __stdcall GetLastError();
@@ -688,6 +693,32 @@ void * __lldb_LoadLibraryHelper(const wchar_t *name, const wchar_t *paths,
result->ImageBase = LoadLibraryExW(name, nullptr,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+
+ // Fallback: if the AddDllDirectory + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS path
+ // failed to find the library, iterate the search paths ourselves and
+ // load by absolute path using LOAD_WITH_ALTERED_SEARCH_PATH, which makes
+ // Windows use the loaded DLL's own directory to resolve its sibling imports.
+ if (result->ImageBase == nullptr) {
+ wchar_t full[4096];
+ for (const wchar_t *path = paths; path && *path; path += wcslen(path) + 1) {
+ size_t plen = wcslen(path);
+ size_t nlen = wcslen(name);
+ // Need room for: path + '\\' + name + '\0'
+ if (plen + 1 + nlen + 1 > 4096)
+ continue;
+ wchar_t *p = full;
+ for (size_t i = 0; i < plen; ++i)
+ *p++ = path[i];
+ *p++ = L'\\';
+ for (size_t i = 0; i <= nlen; ++i) // copy name including trailing '\0'
+ *p++ = name[i];
+ result->ImageBase = LoadLibraryExW(full, nullptr,
+ LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (result->ImageBase != nullptr)
+ break;
+ }
+ }
+
if (result->ImageBase == nullptr)
result->ErrorCode = GetLastError();
else
diff --git a/lldb/test/API/api/command-return-object/TestSBCommandReturnObject.py b/lldb/test/API/api/command-return-object/TestSBCommandReturnObject.py
index 41a39f0597bad..aa00acfca2f66 100644
--- a/lldb/test/API/api/command-return-object/TestSBCommandReturnObject.py
+++ b/lldb/test/API/api/command-return-object/TestSBCommandReturnObject.py
@@ -12,9 +12,6 @@ class TestSBCommandReturnObject(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@skipIfNoSBHeaders
- @expectedFailureAll(
- oslist=["windows"], archs=["i[3-6]86", "x86_64"], bugnumber="llvm.org/pr43570"
- )
@skipIfHostIncompatibleWithTarget
def test_sb_command_return_object(self):
self.driver_exe = self.getBuildArtifact("command-return-object")
diff --git a/lldb/test/API/api/multiple-targets/TestMultipleTargets.py b/lldb/test/API/api/multiple-targets/TestMultipleTargets.py
index 2652c9235a671..ba2feb0d823ca 100644
--- a/lldb/test/API/api/multiple-targets/TestMultipleTargets.py
+++ b/lldb/test/API/api/multiple-targets/TestMultipleTargets.py
@@ -15,9 +15,6 @@ class TestMultipleTargets(TestBase):
@skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
@skipIfNoSBHeaders
- @expectedFailureAll(
- oslist=["windows"], archs=["i[3-6]86", "x86_64"], bugnumber="llvm.org/pr20282"
- )
@expectedFlakeyNetBSD
@skipIfHostIncompatibleWithTarget
def test_multiple_targets(self):
More information about the lldb-commits
mailing list