runtime: [Preview 4] dotnet sdk check fails when runtimes installed via script

Running dotnet sdk check throws this exception using dotnet preview4.

➜ dotnet sdk check
System.DllNotFoundException: Unable to load shared library 'hostfxr' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(libhostfxr, 1): image not found
   at Microsoft.DotNet.NativeWrapper.Interop.hostfxr_get_dotnet_environment_info(String dotnet_root, IntPtr reserved, hostfxr_get_dotnet_environment_info_result_fn result, IntPtr result_context)
   at Microsoft.DotNet.NativeWrapper.NETBundlesNativeWrapper.GetDotnetEnvironmentInfo(String dotnetExeDirectory) in Microsoft.DotNet.NativeWrapper.dll:token 0x600000f+0x20
   at Microsoft.DotNet.Tools.Sdk.Check.SdkCheckCommand.Execute() in dotnet.dll:token 0x60004e0+0x13
   at Microsoft.DotNet.Cli.DotNetTopLevelCommandBase.RunCommand(String[] args) in dotnet.dll:token 0x60008af+0x5e
   at Microsoft.DotNet.Tools.Sdk.SdkCommand.Run(String[] args) in dotnet.dll:token 0x60004ce+0x6
   at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, TimeSpan startupTime, ITelemetry telemetryClient) in dotnet.dll:token 0x6000918+0x2e8
   at Microsoft.DotNet.Cli.Program.Main(String[] args) in dotnet.dll:token 0x6000916+0x6f

Here are the results of my call to dotnet --list-sdks

➜ dotnet --list-sdks
2.1.202 [/Users/khalidabuhakmeh/.dotnet/sdk]
2.1.816 [/Users/khalidabuhakmeh/.dotnet/sdk]
3.0.103 [/Users/khalidabuhakmeh/.dotnet/sdk]
3.1.409 [/Users/khalidabuhakmeh/.dotnet/sdk]
5.0.203 [/Users/khalidabuhakmeh/.dotnet/sdk]
6.0.100-preview.3.21202.5 [/Users/khalidabuhakmeh/.dotnet/sdk]
6.0.100-preview.4.21255.9 [/Users/khalidabuhakmeh/.dotnet/sdk]

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 33 (31 by maintainers)

Most upvoted comments

Just to summarize - the plan is to:

  • Modify the muxer (dotnet.exe) to set a new runtime property called HOSTFXR_PATH (simplified the name) when launching SDK/CLI (dotnet.dll).
  • SDK is expected to read this value via AppContext.GetData("HOSTFXR_PATH")
  • SDK is expected to use this value to fix the DllImport problems - I’ll let interop people advice what is the best way to do this (pre-load via NativeLibrary or use DllImportResolver, or something else)

After discussing this with a couple of people I like the idea of muxer helping SDK here. Let me first describe the possible solutions:

  • The most correct one is to find the already loaded hostfxr in the process. Unfortunately runtime doesn’t have this capability and dlopen doesn’t do it either. But we do have a solution for this in nethost (well, hopefully - we should try if that solution actually works on M1)
    • Unfortunately calling nethost from SDK is tricky (ideally we would use it as a NuGet dependency, but that bring along quite a few other files which we would rather not ship in the SDK)
  • Find the hostfxr on disk - this is not so bad - there are many things relying on the relative location of dotnet and hostfxr so adding another would not be the end of it.
  • Modify the muxer (dotnet) to pass the path to the hostfxr to the SDK somehow.

I like the last solution the best (although technically the cleanest one is using nethost) since it’s relatively cheap and relatively clean. (Kudos for this goes to @agocke). I also agree with @elinor-fung that the muxer should not do this unconditionally. But the muxer knows that it’s running SDK - it has special code path to actually execute any SDK command, so it can easily do this only when running SDK. With that there are 2 options:

  • Muxer would set some new runtime property, for example HOSTFXR_FULL_PATH. SDK would read this via AppContext and use it whichever way it wants to (in this case probably using it in the DllImportResolver).
  • Muxer would add the path to hostfxr to the NATIVE_DLL_SEARCH_DIRECTORIES

I find the first one (special new property) cleaner, but it’s more work. If others think that just setting the NATIVE_DLL_SEARCH_DIRECTORIES is not too bad, I’d be OK with it as well.

[DllImport("hostfxr")] is doing what we expect it (or any DllImport with just the library name) should, in my opinion - basically going through any extension points and runtime-configured search paths and then it is just ‘let the OS do its default load’ (so whatever dlopen/LoadLibrary does on that OS).

Making the SDK implement a DllImportResolver (as @mateoatr had) makes sense to me. This is the same as would be needed for any other native library that is not part of the app’s deps.json and not next to the app. Unfortunately, that would mean the SDK would have knowledge of the install layout baked into it - I think Process.Modules only has the main module on macOS, so the implementation couldn’t use that to find the hostfxr that is already loaded.

The other possibility I see is having the host add the hostfxr path to NATIVE_DLL_SEARCH_DIRECTORIES. I don’t think it would make sense to always add it, since it would affect other p/invokes (add an extra lookup), so I think it would need to be conditional. That would avoid the SDK (or any other app that wants to invoke into hostfxr) needing to figure out install layout, but I don’t see it as a common use case, so I wouldn’t want some new behaviour/configuration for the host to keep maintaining and supporting just for this one internal case.

Code is here: https://github.com/dotnet/sdk/blob/a30e465a2e2ea4e2550f319a2dc088daaafe5649/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs#L92

Looks like the assumption in the SDK is that hostfxr is automatically located, but I’m not sure how that’s supposed to work.

Awesome, @mateoatr do you want me to move this issue to runtime?

Not sure where should this issue be (that being said, I don’t mind moving this to runtime). I’m investigating, but it looks like dynamic linker environment variables (DYLD_LIBRARY_PATH) are being purged, thus failing to locate libhostfxr.dylib (specified in the DllImport).