using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Text; namespace Brotli { internal class NativeLibraryLoader { internal static bool IsWindows = false; internal static bool IsLinux = false; internal static bool IsMacOSX = false; internal static bool IsNetCore = false; internal static bool Is64Bit = false; static NativeLibraryLoader() { #if NET35 || NET40 IsWindows=true; #else IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); IsMacOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); IsNetCore = RuntimeInformation.FrameworkDescription.StartsWith(".NET Core"); #endif if (!IsWindows && !IsLinux && !IsMacOSX) { throw new InvalidOperationException("Unsupported platform."); } Is64Bit = IntPtr.Size == 8; } // dlopen flags const int RtldLazy = 1; const int RtldGlobal = 8; readonly string _libraryPath; internal IntPtr Handle { get; private set; } internal NativeLibraryLoader(string libraryPath) { _libraryPath = libraryPath; Handle = LoadLibrary(_libraryPath, out var errorMsg); if (!String.IsNullOrEmpty(errorMsg)) { throw new BrotliException($"unable to load library {libraryPath}"); } } public static IntPtr GetWin32ModuleHandle(String moduleName) { IntPtr r = IntPtr.Zero; foreach (ProcessModule mod in Process.GetCurrentProcess().Modules) { if (mod.ModuleName == moduleName) { r = mod.BaseAddress; break; } } return r; } /// /// Loads method in the library /// /// The methold of the library /// method address private IntPtr LoadSymbol(string symbolName) { if (IsWindows) { return WindowsLoader.GetProcAddress(Handle, symbolName); } if (IsLinux) { if (IsNetCore) { return CoreCLRLoader.dlsym(Handle, symbolName); } return LinuxLoader.dlsym(Handle, symbolName); } if (IsMacOSX) { return MacOSXLoader.dlsym(Handle, symbolName); } throw new InvalidOperationException("Unsupported platform."); } public void FillDelegate(out T delegateType) where T : class { var typeName = typeof(T).Name; var kt = "Delegate"; if (typeName.EndsWith(kt)) { typeName = typeName.Substring(0, typeName.Length - kt.Length); } delegateType = GetNativeMethodDelegate(typeName); } public T GetNativeMethodDelegate(string methodName) where T : class { var ptr = LoadSymbol(methodName); if (ptr == IntPtr.Zero) { throw new MissingMethodException(string.Format("The native method \"{0}\" does not exist", methodName)); } return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; // generic version not available in .NET45 } internal static string[] GetPossibleRuntimeDirectories() { #if NET35 || NET40 var assemblyDirectory = Path.GetDirectoryName(typeof(LibPathBootStrapper).Assembly.Location); #else var assemblyDirectory = Path.GetDirectoryName(typeof(LibPathBootStrapper).GetTypeInfo().Assembly.Location); #endif //var platform = "win"; //if (IsLinux) //{ // platform= "linux"; //} //if (IsMacOSX) //{ // platform= "osx"; //} //string runtimesDirectory = string.Format("runtimes/{0}/native", platform); string runtimesDirectory; if (Is64Bit) { runtimesDirectory = "x64"; // string.Format("x64/{0}", platform); } else { runtimesDirectory = "x86"; // string.Format("x86/{0}", platform); } string runtimesFullDirectory = Path.Combine(assemblyDirectory,runtimesDirectory); var iisBaseDirectory = $"{AppDomain.CurrentDomain.BaseDirectory}bin/{runtimesDirectory}"; var execAssemblyDirectory= System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\","")+"/"+ runtimesDirectory; #if NET35 var netCoreAppStyleDirectory = Path.Combine(Path.Combine(assemblyDirectory, "../.."), runtimesDirectory); #else var netCoreAppStyleDirectory = Path.Combine(assemblyDirectory, "../..", runtimesDirectory); #endif string[] paths = new[] { assemblyDirectory, runtimesFullDirectory, runtimesDirectory, netCoreAppStyleDirectory,iisBaseDirectory,execAssemblyDirectory }; paths = paths.Select(v => v.Replace('/', Path.DirectorySeparatorChar)).ToArray(); return paths; } internal static bool FreeLibrary(IntPtr handle) { string errorMsg = null; if (IsWindows) { return WindowsLoader.FreeLibrary(handle); } if (IsLinux) { if (IsNetCore) { return UnloadLibraryPosix(CoreCLRLoader.dlclose, CoreCLRLoader.dlerror, handle, out errorMsg); } return UnloadLibraryPosix(LinuxLoader.dlclose, LinuxLoader.dlerror, handle, out errorMsg); } if (IsMacOSX) { return UnloadLibraryPosix(MacOSXLoader.dlclose, MacOSXLoader.dlerror, handle, out errorMsg); } throw new InvalidOperationException("Unsupported platform."); } /// /// Load library /// internal static IntPtr LoadLibrary(string libraryPath, out string errorMsg) { if (IsWindows) { errorMsg = null; //var handle = GetWin32ModuleHandle(libraryPath); //if (handle != IntPtr.Zero) return handle; var handle= WindowsLoader.LoadLibrary(libraryPath); if (handle== IntPtr.Zero) { throw new System.ComponentModel.Win32Exception($"failed to load library {libraryPath}"); } return handle; } if (IsLinux) { if (IsNetCore) { return LoadLibraryPosix(CoreCLRLoader.dlopen, CoreCLRLoader.dlerror, libraryPath, out errorMsg); } return LoadLibraryPosix(LinuxLoader.dlopen, LinuxLoader.dlerror, libraryPath, out errorMsg); } if (IsMacOSX) { return LoadLibraryPosix(MacOSXLoader.dlopen, MacOSXLoader.dlerror, libraryPath, out errorMsg); } throw new InvalidOperationException("Unsupported platform."); } static IntPtr LoadLibraryPosix(Func dlopenFunc, Func dlerrorFunc, string libraryPath, out string errorMsg) { errorMsg = null; IntPtr ret = dlopenFunc(libraryPath, RtldGlobal + RtldLazy); if (ret == IntPtr.Zero) { errorMsg = Marshal.PtrToStringAnsi(dlerrorFunc()); } return ret; } static bool UnloadLibraryPosix(Func dlcloseFunc, Func dlerrorFunc, IntPtr handle,out string errorMsg) { errorMsg = null; var r = dlcloseFunc(handle); if (r!=0) { errorMsg= Marshal.PtrToStringAnsi(dlerrorFunc()); return false; } return true; } } }