Fix native library resolution when a same-named DLL exists in a PATH directory - #100
Fix native library resolution when a same-named DLL exists in a PATH directory#100oxygen-dioxide wants to merge 1 commit into
Conversation
Signed-off-by: unknown <1463567152@qq.com>
4e467ee to
0e3818a
Compare
|
Thanks for tracking this down. Your diagnosis is right and The main one is scope. Once a resolver is attached, the runtime consults it for every P/Invoke in that assembly, including ones bound for OS libraries, and Second, Third, the two Last thing, and this one is not mine to waive. The sign-off is a legal attestation rather than a formality. Under the Developer Certificate of Origin you are certifying that you wrote this code or otherwise hold the rights to it, and that you have the right to submit it under the MIT license Verso ships under. That attestation is what establishes the provenance of the intellectual property in the project, and it only carries weight if it is attributable to an identifiable person. The DCO comes from the Linux kernel, which states the rule plainly: your real name, no pseudonyms and no anonymous contributions. I checked the first three against a small test app rather than going by the docs, so let me know if you want it. |
Replace locked List<string> search dirs with volatile string[] snapshots to avoid locking during runtime load handlers; writers swap in new arrays via Append. Register an AppDomain.AssemblyLoad handler that attaches a DllImportResolver to assemblies loaded from package directories so package-owned native libraries are preferred ahead of normal probing. Add ResolvePackageNative, TryLoadFrom, IsFromPackageDirectory, IsRegisteredDirectory, Normalize and related helpers, and refactor unmanaged/managed resolution to use the snapshot arrays. Include safety try/catch around assembly-load wiring. Update tests to cover IsRegisteredDirectory behavior. Diagnosed by [@oxygen-dioxide](https://github.com/oxygen-dioxide) (#91 and #100) Signed-off-by: Torrey Betts <torrey.betts@gmail.com>
|
Sorry to see this closed. I reproduced it here against a stray libSkiaSharp that the runtime's own probing finds before the package's own copy, and confirmed that a DllImportResolver is the only hook that runs early enough to beat it. The fix is in for the next release. You'll be credited for the diagnosis in the release notes. Thanks for continuing to work on this, especially after I closed the issue twice on the wrong theory. If you have something else you'd like to send our way, I'd be glad to look at it. |
Summary
Notebooks that load native dependencies via
#r "nuget: ..."fail with a native library version mismatch if an unrelated copy of the library happens to live in any directory on thePATH.Issue #91 will be fixed
Minimal reproduction
C:\native-pollution, and add it to yourPATH.Before the fix: the run fails with the
libSkiaSharp (80.2) is incompatible ... [119.0, 120.0)error shown above.After the fix: all cells succeed; ScottPlot renders using the 3.119.0 native asset from the NuGet cache.
Control: remove
libSkiaSharp.dllfrom the folder (or drop the folder fromPATH) and the notebook works both before and after the fix.Root cause
Native resolution was handled only through
AssemblyLoadContext.Default.ResolvingUnmanagedDll(src/Verso/Kernels/NativeLibraryResolver.cs). Per the .NET unmanaged loading algorithm, that event fires last, only after the runtime's default native probing has failed. On Windows the default probing usesLoadLibrary, whose search includes the directories listed inPATH.So when a stray
libSkiaSharp.dll(e.g. version 80.2 shipped inside some other app's folder that is onPATH) is found byLoadLibrary, the wrong native library is loaded successfully — and Verso's resolver never gets a chance to supply the correct one. The same code works in a csproj because there the matching native asset is copied to the app output directory and is found first by default probing.How I fixed it
Install a per-assembly
NativeLibrary.SetDllImportResolveron every managed assembly loaded from a NuGet package directory. ADllImportResolveris consulted before the default probing, so the native asset extracted from the referenced NuGet package always wins over unrelated copies found throughPATH.The resolver reuses the existing search logic (
TryResolveNative), including the same-package-version preference, and is attached both when we load an assembly through theResolvinghandler and for any assembly loaded directly from a package directory (tracked viaAppDomain.AssemblyLoad). TheResolvingUnmanagedDllhandler is kept as a fallback for libraries not found by default probing (e.g.e_sqlite3).