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

Implementation of AppContext.BaseDirectory and DllImportSearchPath.AssemblyDirectory handling on NativeAOT in the SharedLibrary scenario is unintuitive #112290

Open
jkoritzinsky opened this issue Feb 7, 2025 · 9 comments · Fixed by #112457
Labels
area-NativeAOT-coreclr in-pr There is an active PR which will close this issue when it is merged untriaged New issue has not been triaged by the area owner

Comments

@jkoritzinsky
Copy link
Member

On NativeAOT today, DllImportSearchPath.AssemblyDirectory tells the runtime to load the requested library from Path.Combine(AppContext.BaseDirectory, libName). On NativeAOT, AppContext.BaseDirectory is always defined as the directory of the process executable. As a result, trying to load a native shared library from a NativeAOT application will always look next to the process entry module.

This makes sense for NativeAOT applications that produce executables, but this behavior becomes unintuitive for SharedLibrary-based NativeAOT applications. In that scenario, using DllImportSearchPath.AssemblyDirectory will look next to the entry executable of the process, not next to the NativeAOT shared library.

This makes it extremely difficult to load any native shared libraries that are shipped alongside a NativeAOT shared library. Users are forced to manually use P/Invokes to GetModuleFileName and dladdr to determine their path (or, if loaded by another native library, passed these values) and manually call NativeLibrary.Load(string) with the full path to load, losing all of the niceties of LibraryImport/DllImport or the NativeLibrary.Load overload that takes DllImportSearchPath (like prepending lib and appending .so/.dylib on different platforms).

I propose that on NativeAOT SharedLibrary scenarios, the "Assembly directory" that's used is the directory that the NativeAOT shared library is installed in, not the directory where the executable that loads the library is located.

@agocke has also expressed that we should continue to have AppContext.BaseDirectory and the "Assembly directory" be the same, and if we decide to do this to change the AppContext.BaseDirectory path to always be the path to the NativeAOT-produced library or exe that is calling AppContext.BaseDirectory, not always the process executable path.

@max-charlamb hit this while working on the native unwinder work for the cDAC.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Feb 7, 2025
Copy link
Contributor

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

@MichalStrehovsky
Copy link
Member

Previously: #90131

If we can find a mechanism that reliably works crossplatform, it would certainly be an improvement over the current state.

Note that shared libraries could also take advantage of the DirectPInvoke mechanism and have the OS deal with binding (and use OS-level overrides).

Cc @AaronRobinsonMSFT @elinor-fung

@jkoritzinsky
Copy link
Member Author

@agocke and I determined that we could use dladdr on non-Windows and GetModuleFileName on Windows. This is the exact same mechanism that CoreCLR uses to do the same operation as part of loading the JIT.

@agocke
Copy link
Member

agocke commented Feb 8, 2025

As mentioned, I think it’s reasonable to have a different behavior for Native aot libraries vs applications. Only complication is determining which one we’re compiling for, so we can use a different implementation.

I suppose we could use a feature switch for this.

@jkoritzinsky
Copy link
Member Author

We have mechanisms in the bootstrapper and the NativeAOT runtime initialization to know whether or not we're running from an Exe or Library scenario. We could use that to differentiate.

@MichalStrehovsky
Copy link
Member

We have mechanisms in the bootstrapper and the NativeAOT runtime initialization to know whether or not we're running from an Exe or Library scenario. We could use that to differentiate.

That mechanism is a hack added for #91715. I think building native AOT into a static library will make it think we're in a DLL when in fact we don't know. For the hack purposes, it's fine, since event pipe is already unsupported in OutputType!=EXE scenarios and the hack just makes it sort of work.

I think this will need run time probing; we don't know if we're a shared library at compile time.

@steveisok
Copy link
Member

/cc @lateralusX

@lateralusX
Copy link
Member

lateralusX commented Feb 10, 2025

We use something similar for Mono components where we have logic to find other runtime component shared libraries that is side by side with the runtime using this logic:

components_dir (void)

The runtime doesn't know how it will be linked, so it uses a runtime check to get hold of the module owning some symbol at link time in order to get hold of the module owning that address. This scenario doesn't support the case where runtime is static linked into application and the components end up as shared library, but the probing could be extended with a fallback on unix if dladdr didn't find the address, since that would mean we should fallback to use the application path (like we currently do). We use this implementation on all platforms that support dynamic loadable components, so it works on at least on Windows, Linux, OSX, iOS, tvOS and Android.

@jkotas
Copy link
Member

jkotas commented Mar 7, 2025

The fix was reverted in #113211

@jkotas jkotas reopened this Mar 7, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-NativeAOT-coreclr in-pr There is an active PR which will close this issue when it is merged untriaged New issue has not been triaged by the area owner
Projects
Status: No status
6 participants