Skip to content

Native Library Loader API #27267

Closed
Closed

Description

@annaaniol

This issue introduces a NativeLibrary abstraction for loading native libraries from managed code in a platform independent way. It provides two key functionalities

  • A facility to register a callback to implement custom native library load logic from managed codeper assembly.
  • A few methods to handle native library manipulations from managed code.

Context

This API is introduced as part of implementing the Dllmap feature. This issue introduces a generic callback strategy, where DllMap is one specific implementation using the API.

Approved API

namespace System.Runtime.InteropServices
{
   /// <summary>
   /// A delegate used to resolve native libraries via callback.
   /// </summary>
   /// <param name="libraryName">The native library to resolve</param>
   /// <param name="assembly">The assembly requesting the resolution</param>
   /// <param name="DllImportSearchPath?">The DllImportSearchPathsAttribute on the PInvoke, if any. 
   ///         Otherwise, the DllImportSearchPathsAttribute on the assembly, if any. Otherwise null.
   /// </param>
  /// <returns>The handle for the loaded native library on success, null on failure</returns>  
   public delegate IntPtr DllImportResolver(string libraryName,
                                             Assembly assembly,
                                             DllImportSearchPath? searchPath);

    public static partial class NativeLibrary
    {
        // Typical or for dependencies which must be there or failure should happen.

        public static IntPtr Load(string libraryPath);
        public static IntPtr Load(string libraryName,
                                  Assembly assembly,
                                  DllImportSearchPath? searchPath);

        // For fast probing scenarios:

        public static bool TryLoad(string libraryPath,
                                   out IntPtr handle);
        public static bool TryLoad(string libraryName,
                                   Assembly assembly,
                                   DllImportSearchPath? searchPath,
                                   out IntPtr handle);

        public static void Free(IntPtr handle);

        public static IntPtr GetExport(IntPtr handle, string name);
        public static bool TryGetExport(IntPtr handle, string name, out IntPtr address);

       /// <summary>
       /// Set a callback for resolving native library imports from an assembly.
       /// This per-assembly callback is the first attempt to resolve native library loads 
       /// initiated by this assembly.
       ///
       /// Only one resolver callback can be registered per assembly. Trying to register a second
       /// callback fails with InvalidOperationException.
       /// </summary>
       /// <param name="assembly">The assembly for which the callback is registered</param>
       /// <param name="resolver">The callback to register</param>
       /// <exception cref="System.ArgumentNullException">If assembly or callback is null</exception>
       /// <exception cref="System.InvalidOperationException">If a callback is already set for this assembly</exception>
        public static void SetDllImportResolver(Assembly assembly, DllImportResolver resolver);
    }
}

Related

Earlier Proposal

namespace System.Runtime.InteropServices
{
    /// <summary>
    ///  NativeLibrary class is an abstraction for loading native libraries
    ///  from managed code in a platform independent way.
    /// </summary>
    public class NativeLibrary
    {
        /// <summary>
        ///  NativeLibrary constructor, given a name and a library handle
        /// </summary>
        /// <param name="libraryName">The library Name</param>
        /// <param name="handle">The library handle obtained from the appropriate system call</param>
        /// <exception cref="System.ArgumentNullException">Thrown when libraryName equals null or handle equals IntPtr.Zero.</exception>
        public NativeLibrary(string libraryName, IntPtr handle)
        {
            Name = libraryName ?? throw new ArgumentNullException("Name of a NativeLibrary can't be set to null.");
            if (handle == IntPtr.Zero)
                throw new ArgumentNullException("Handle of a NativeLibrary can't be set to IntPtr.Zero.");
            Handle = handle;
        }

        /// <summary>
        /// The simple Library loader 
        /// This is a wrapper around OS loader, using "default" flags
        /// This method facilitates platform independent native library load in the simple case
        /// More complex loads will need to PInvoke to the platform-specific system calls.
        /// </summary>
        /// <param name="libraryName">The name of the native library to be loaded</param>
        /// <returns>The NativeLibrary object for the loaded library</returns>  
        /// <exception cref="System.DllNotFoundException ">Thrown if the library can't be found.</exception>
        /// <exception cref="System.BadImageFormatException">Thrown if the library is not valid.</exception>
        public static NativeLibrary Load(string libraryName);

        /// <summary>
        /// Load a native library
        /// This loader API exposes high-level functionality. Given a library name, it searches 
        /// specific paths based on runtime configuration and attributes of the calling module.
        /// This Load() method works at a 'lower-level' than the managed call-backs for native 
        /// library loading. That is, the NativeLibrary.Load() methods do not call back into
        /// * The Per-assembly function registered via NativeLibrary.RegisterNativeLibraryLoadCallback()
        /// * Or AssemblyLoadContext.LoadUnmanagedDll()
        /// </summary>
        /// <param name="libraryName">The name of the native library to be loaded</param>
        /// <param name="dllImportSearchPath">The search path</param>
        /// <param name="assembly">The assembly loading the native library</param>
        /// <returns>The NativeLibrary object for the loaded library</returns>  
        /// <exception cref="System.DllNotFoundException ">Thrown if the library can't be found.</exception>
        /// <exception cref="System.BadImageFormatException">Thrown if the library is not valid.</exception>
        public static NativeLibrary Load(string libraryName, DllImportSearchPath dllImportSearchPath, Assembly assembly);

        /// <summary>
        /// Close a Native library
        /// This function is a simple wrapper around platform-specific OS calls.
        /// </summary>
        /// <returns>True, if the operation succeeds</returns>  
        /// <exception cref="System.InvalidOperationException">If the NativeLibrary object is not backed by an open handle</exception>
        public bool Free();

        /// <summary>
        /// Get the address of a Symbol
        /// This is a simple wrapper around OS calls, and does not perform any name mangling.
        /// </summary>
        /// <returns>The address of the symbol, if it exists in this NativeLibrary</returns>  
        /// <exception cref="System.InvalidOperationException">If the NativeLibrary object is not backed by an open handle</exception>
        /// <exception cref="System.ArgumentException">If the symbol is not found</exception>
        public IntPtr GetProcAddress(string name);

        /// <summary>
        ///  The actual library handle obtained via LoadLibrary() or dlOpen() operations
        /// </summary>
        public IntPtr Handle { get; }

        /// <summary>
        /// The name referenced by the importing assembly
        /// ToString() can return more detailed/debugging information (ex: mapped-name, loaded location, etc)
        /// </summary>
        public string Name { get; } // Will Return the name referenced by the importing assembly, set only for debugging purpose.

        /// <summary>
        /// Method to register a callback to perform NativeLibraryLoads
        /// The callbacks are registered per-assembly, and can implement custom native library load logic
        /// At most one callback can be registered per assembly
        /// Callbacks can be unregistered by passing a null 'callback' parameter
        /// </summary>
        /// <param name="assembly">Assembly with which this callback should be associated </param>
        /// <param name="callback">The callback function</param>
        /// <returns>True, if the callback was updated</returns>  
        /// <exception cref="System.ArgumentNullException">If the assembly argument is null</exception>
        /// <exception cref="System.InvalidOperationException">Thrown when there is already a callback registered for the specified assembly.</exception>
        public static bool RegisterNativeLibraryLoadCallback(Assembly assembly, Func<string, DllImportSearchPath, Assembly, NativeLibrary> callback);
    }
}

Activity

  1. changed the title [-]NativeLibrary class for loading native libraries and funtion pointers, Dllmap specific events[/-] [+]NativeLibrary class for loading native libraries and funtion pointers, LoadNativeLibrary and GetNativeEntrypoint events[/+] on Aug 29, 2018
  2. changed the title [-]NativeLibrary class for loading native libraries and funtion pointers, LoadNativeLibrary and GetNativeEntrypoint events[/-] [+]NativeLibrary class for loading native libraries and RegisterNativeLibraryLoadCallback [/+] on Sep 5, 2018
  3. changed the title [-]NativeLibrary class for loading native libraries and RegisterNativeLibraryLoadCallback [/-] [+]Native Library Loader API[/+] on Oct 8, 2018
  4. 10 remaining items

  5. added a commit that references this issue on Jan 17, 2019
  6. added this to the 3.0 milestone on Jan 31, 2020
  7. ghost locked as resolved and limited conversation to collaborators on Dec 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions