Skip to content
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

[Breaking change]: .NET Linux applications do not look in netcoredeps sub-directory for native libraries #45777

Closed
1 of 3 tasks
elinor-fung opened this issue Apr 10, 2025 · 2 comments · Fixed by #45778
Closed
1 of 3 tasks
Assignees
Labels
breaking-change Indicates a .NET Core breaking change 📌 seQUESTered Identifies that an issue has been imported into Quest.

Comments

@elinor-fung
Copy link
Member

elinor-fung commented Apr 10, 2025

Description

To support third-party dependencies in self-contained Linux applications, .NET applications had their RPATH set to $ORIGIN/netcoredeps such that they would look for libraries used by the runtime in that location. This also had the side effect that all native library loads (that is, including p/invokes defined by the user) would also look in the netcoredeps subdirectory.

This behaviour was an artifact of the complicated dependencies of .NET at the time - a situation which has since improved. In .NET 8 and later, .NET linux applications no longer look in the netcoredeps subdirectory when loading native libraries.

See dotnet/runtime#114393

Version

.NET 8

Previous behavior

.NET Linux applications looked in a netcoredeps subdirectory next to the application executable when loading native libraries.

New behavior

.NET Linux applications no longer look in a netcoredeps subdirectory next to the application executable when loading native libraries.

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 netcoredeps behaviour was introduced to handle complicated .NET dependencies on third-party libraries - which should not be an issue in recent versions of .NET. It was never intended to be used for other native libraries.

It affected more than its intend use case, that use case itself had no support story, and the mechanism it used is not the modern recommended path for ELF platforms.

Recommended action

If users were using this mechanism for p/invokes, DllImportResolver' or ResolvingUnmanagedDll` should be used instead for custom resolution.

If users want an RPATH set in their deployment, they can modify the ELF file explicitly.

Feature area

Interop

Affected APIs

DllImport
NativeLibrary.Load


Associated WorkItem - 419669

@elinor-fung elinor-fung added the breaking-change Indicates a .NET Core breaking change label Apr 10, 2025
@dotnet-policy-service dotnet-policy-service bot added the ⌚ Not Triaged Not triaged label Apr 10, 2025
@CamSoper CamSoper added 🗺️ reQUEST Triggers an issue to be imported into Quest. and removed ⌚ Not Triaged Not triaged labels Apr 10, 2025
@CamSoper CamSoper moved this from 🔖 Ready to 🏗 In progress in dotnet/docs April 2025 sprint project Apr 10, 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 Apr 10, 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 Apr 10, 2025
@am11
Copy link
Member

am11 commented Apr 10, 2025

Should we drop the RUNPATH and add note for .NET 10 as @jkotas suggested? It would be nice to not rely on it.

BTW, in SDK 9.0 on Ubuntu, this is the RUNPATH situation:

$ find /usr/lib/dotnet -exec sh -c 'echo "RUNPATH-{}" && readelf -d {}' \; 2>/dev/null | grep 'RUNPATH' | grep -B1 'RUNPATH)'

RUNPATH-/usr/lib/dotnet/shared/Microsoft.NETCore.App/9.0.3/createdump
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN]
--
RUNPATH-/usr/lib/dotnet/shared/Microsoft.NETCore.App/9.0.3/libmscordbi.so
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN]
--
RUNPATH-/usr/lib/dotnet/packs/Microsoft.NETCore.App.Runtime.ubuntu.25.04-arm64/9.0.3/runtimes/ubuntu.25.04-arm64/native/createdump
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN]
--
RUNPATH-/usr/lib/dotnet/packs/Microsoft.NETCore.App.Runtime.ubuntu.25.04-arm64/9.0.3/runtimes/ubuntu.25.04-arm64/native/libmscordbi.so
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN]
--
RUNPATH-/usr/lib/dotnet/packs/runtime.ubuntu.25.04-arm64.Microsoft.DotNet.ILCompiler/9.0.3/tools/ilc
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN]
--
RUNPATH-/usr/lib/dotnet/packs/Microsoft.NETCore.App.Host.ubuntu.25.04-arm64/9.0.3/runtimes/ubuntu.25.04-arm64/native/apphost
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN/netcoredeps]
RUNPATH-/usr/lib/dotnet/packs/Microsoft.NETCore.App.Host.ubuntu.25.04-arm64/9.0.3/runtimes/ubuntu.25.04-arm64/native/singlefilehost
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN/netcoredeps]
--
RUNPATH-/usr/lib/dotnet/sdk/9.0.104/AppHostTemplate/apphost
 0x000000000000001d (RUNPATH)            Library runpath: [$ORIGIN/netcoredeps]

Should we replace $ORIGIN/netcoredeps with $ORIGIN in corehost variants for consistency, if nothing else? It will only benefit PublishSingleFile that native lib load from same path as executable would be possible.

@elinor-fung
Copy link
Member Author

I don't think we want apphost to always load from its directory. Since it is the user's app, I think any configuration like that should be left to the developer. (Single-file currently does always look in the app directory, which has surprised some folks - for example, dotnet/runtime#101857 - and we've been considering taking a breaking change to stop doing that)

@sequestor sequestor bot added 📌 seQUESTered Identifies that an issue has been imported into Quest. and removed 🗺️ reQUEST Triggers an issue to be imported into Quest. labels Apr 11, 2025
@github-project-automation github-project-automation bot moved this from 🏗 In progress to ✅ Done in dotnet/docs April 2025 sprint project Apr 14, 2025
@github-project-automation github-project-automation bot moved this from 🏗 In progress to ✅ Done in dotnet/docs April 2025 sprint project Apr 14, 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 📌 seQUESTered Identifies that an issue has been imported into Quest.
Projects
3 participants