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 <[email protected]> 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): _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
