Why Assembly.Location is not available in a Native AOT shared library?
#122457
-
|
I verified that static class Extensions
{
/// <summary>
/// Try to get the native shared library path
/// </summary>
public static string GuessNativeModuleLocation(this Assembly assembly)
{
string? path;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
path = getNativeLibraryLocationWindows(assembly);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
path = getNativeLibraryLocationApple(assembly);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
path = getNativeModuleLocationLinux(assembly);
else
throw new NotSupportedException($"Unsupported platform {RuntimeInformation.OSDescription}");
if (path == null)
throw new Exception("Unable to retrieve the library path");
return path;
}
private static nint getNativeHandle(Assembly assembly)
{
var sharedLibraryName = Path.GetFileNameWithoutExtension(assembly.GetName().Name!);
return NativeLibrary.Load(sharedLibraryName, assembly, null);
}
[MethodImpl(MethodImplOptions.NoInlining)]
static string getNativeLibraryLocationWindows(Assembly assembly)
{
IntPtr handle = IntPtr.Zero;
try
{
handle = getNativeHandle(assembly);
var fileName = new StringBuilder(255);
GetModuleFileName(handle, fileName, 255);
return fileName.ToString();
}
finally
{
NativeLibrary.Free(handle);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
static unsafe string? getNativeLibraryLocationApple(Assembly assembly)
{
var mainProgramHandle = NativeLibrary.GetMainProgramHandle();
var _dyld_image_count = (delegate* unmanaged[Cdecl]<uint>)NativeLibrary.GetExport(mainProgramHandle, "_dyld_image_count");
var _dyld_get_image_name = (delegate* unmanaged[Cdecl]<uint, IntPtr>)NativeLibrary.GetExport(mainProgramHandle, "_dyld_get_image_name");
var dlopen = (delegate* unmanaged[Cdecl]<IntPtr, int, IntPtr>)NativeLibrary.GetExport(mainProgramHandle, "dlopen");
var dlclose = (delegate* unmanaged[Cdecl]<IntPtr, int>)NativeLibrary.GetExport(mainProgramHandle, "dlclose");
uint count = _dyld_image_count();
if (count == 0)
return null;
for (int i = (int)count - 1; i >= 0; i--)
{
IntPtr namePtr = _dyld_get_image_name((uint)i);
if (namePtr == IntPtr.Zero)
continue;
const int RTLD_LAZY = 0x1;
// Probe by dlopen the image name (this returns an existing handle if already loaded).
var probeHandle = dlopen(namePtr, RTLD_LAZY);
if (probeHandle == IntPtr.Zero)
continue;
dlclose(probeHandle);
string? imageName = Marshal.PtrToStringUTF8(namePtr);
if (!string.IsNullOrEmpty(imageName))
{
if (Path.GetFileNameWithoutExtension(imageName) == assembly.GetName()?.Name)
return imageName;
}
}
return null;
}
[MethodImpl(MethodImplOptions.NoInlining)]
static unsafe string? getNativeModuleLocationLinux(Assembly assembly)
{
IntPtr handle = IntPtr.Zero;
try
{
handle = getNativeHandle(assembly);
// RTLD_DI_LINKMAP is a GNU extension; value is 2 on glibc.
const int RTLD_DI_LINKMAP = 2;
link_map* linkMapPtr;
var dlinfo = (delegate* unmanaged[Cdecl]<IntPtr, int, link_map**, int>)NativeLibrary.GetExport(NativeLibrary.GetMainProgramHandle(), "dlinfo");
int rc = dlinfo(handle, RTLD_DI_LINKMAP, &linkMapPtr);
if (rc != 0 || linkMapPtr == null)
return null;
if (linkMapPtr->l_name == IntPtr.Zero)
return null;
string? path = Marshal.PtrToStringUTF8(linkMapPtr->l_name);
return string.IsNullOrWhiteSpace(path) ? null : path!;
}
finally
{
NativeLibrary.Free(handle);
}
}
// Minimal managed representation of `struct link_map` from <link.h>.
[StructLayout(LayoutKind.Sequential)]
struct link_map
{
public IntPtr l_addr; // base address
public IntPtr l_name; // char *
public IntPtr l_ld; // ElfW(Dyn) *
public IntPtr l_next; // struct link_map *
public IntPtr l_prev; // struct link_map *
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint GetModuleFileName([In] IntPtr hModule, [Out] StringBuilder lpFilename, [In] uint nSize);
} |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 7 replies
-
It is intentional because assemblies does not map to files after NativeAOT compiling. A native executable or shared library always consists of multiple assemblies.
The attached code is incorrect. Although it's a convention that the generated native object matches the name of your entry assembly, it can't match other assemblies used. For example it can never deal with |
Beta Was this translation helpful? Give feedback.
-
|
Within a Native AOT shared library, a more reliable way to determine the path of the library is to infer it from any exported symbol, even a dummy one. It follows the proof of concept code: |
Beta Was this translation helpful? Give feedback.
It is intentional because assemblies does not map to files after NativeAOT compiling. A native executable or shared library always consists of multiple assemblies.
Assembly.Locationalways returns null under NativeAOT.The attached code is incorrect. Although it's a convention that the generated native object matches the name of your entry assembly, it can't match other assemblies used. For example it can never deal with
typeof(object).Assembly.