Skip to content

[Breaking change]: Single-file applications no longer always look for native libraries in executable directory #46356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 of 3 tasks
elinor-fung opened this issue May 22, 2025 · 0 comments
Assignees
Labels
breaking-change Indicates a .NET Core breaking change 🗺️ reQUEST Triggers an issue to be imported into Quest.

Comments

@elinor-fung
Copy link
Member

Description

Previously, in single-file .NET applications, the directory of the single-file executable was added to the NATIVE_DLL_SEARCH_DIRECTORIES property during startup. This means that the application directory was always probed when loading unmanaged libraries. On non-Windows with NativeAOT, the rpath was set to the application directory by default, such that it also always looked for native libraries in the application directory.

The application directory is no longer added to NATIVE_DLL_SEARCH_DIRECTORIES in single-file and the rpath i setting has been removed in NativeAOT. In both cases, DllImportSearchPath.AssemblyDirectory (included in the default behaviour for p/invokes) means the application directory - specifying that flag or leaving the default will look in the application directory. Specifying flags without that value will no longer look in the application directory.

Issue: dotnet/runtime#114717
PR: dotnet/runtime#115236

Version

.NET 10 Preview 6

Previous behavior

Single-file applications always looked in the application directory when loading native libraries. NativeAOT applications on non-Windows always looked in the application directory when loading native libraries.

This meant that the following p/invoke would actually look in the application directory for lib and load it from there if it exists:

[DllImport("lib")
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
static extern void Method()

New behavior

Single-file applications only look in the application directory if the search paths for a native library load indicate including the assembly directory.

// Look in System32 on Windows, OS search on non-Windows
[DllImport("lib")
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
static extern void Method()

// Look next to the single-file app because assembly directory means application directory for single-file
[DllImport("lib")
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
static extern void Method()

// Look next to the single-file app (because we search assembly directory by default), then default OS search
[DllImport("lib")
static extern void Method()

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

The existing behaviour - always looking in the application directory even if search paths exclude it - has caused confusion in the past. It is also inconsistent with how the search flags are handled in regular (non-single-file, non-NativeAOT) .NET applications.

Recommended action

If the application/assembly directory is desired for a p/invoke or native library load and was not previously specified, specify DllImportSearchPath.AssemblyDirectory.

If the RPATH setting is desired in NativeAOT, explicitly add the corresponding linker args to your project: https://learn.microsoft.com/dotnet/core/deploying/native-aot/interop#linking

Feature area

Interop

Affected APIs

@elinor-fung elinor-fung added the breaking-change Indicates a .NET Core breaking change label May 22, 2025
@dotnet-policy-service dotnet-policy-service bot added the ⌚ Not Triaged Not triaged label May 22, 2025
@dotnetrepoman dotnetrepoman bot added the 🗺️ mapQUEST Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it. label Jun 2, 2025
@gewarren gewarren added 🗺️ reQUEST Triggers an issue to be imported into Quest. and removed 🗺️ mapQUEST Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it. labels Jun 2, 2025
@dotnet-policy-service dotnet-policy-service bot removed the ⌚ Not Triaged Not triaged label Jun 2, 2025
@dotnetrepoman dotnetrepoman bot added the 🗺️ mapQUEST Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it. label Jun 2, 2025
@dotnet-policy-service dotnet-policy-service bot removed the 🗺️ mapQUEST Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it. label Jun 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change Indicates a .NET Core breaking change 🗺️ reQUEST Triggers an issue to be imported into Quest.
Projects
Status: 🔖 Ready
Development

No branches or pull requests

2 participants