From 483f1922fda4b477b6dfa549b65e068183c7700c Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Wed, 24 Mar 2021 22:05:02 -0700 Subject: [PATCH] try libdl.so.2 on Linux before libdl.so also refactored LibraryLoader to share code between Linux and Mac fixes https://github.com/pythonnet/pythonnet/issues/1422 --- src/runtime/platform/LibDL.cs | 109 ++++++++++++++++++++++++++ src/runtime/platform/LibraryLoader.cs | 104 +++--------------------- 2 files changed, 121 insertions(+), 92 deletions(-) create mode 100644 src/runtime/platform/LibDL.cs diff --git a/src/runtime/platform/LibDL.cs b/src/runtime/platform/LibDL.cs new file mode 100644 index 000000000..3bf2a2057 --- /dev/null +++ b/src/runtime/platform/LibDL.cs @@ -0,0 +1,109 @@ +#pragma warning disable IDE1006 // Naming Styles (interface for native functions) +using System; +using System.Runtime.InteropServices; + +namespace Python.Runtime.Platform +{ + interface ILibDL + { + IntPtr dlopen(string fileName, int flags); + IntPtr dlsym(IntPtr handle, string symbol); + int dlclose(IntPtr handle); + IntPtr dlerror(); + + int RTLD_NOW { get; } + int RTLD_GLOBAL { get; } + IntPtr RTLD_DEFAULT { get; } + } + + class LinuxLibDL : ILibDL + { + private const string NativeDll = "libdl.so"; + + public int RTLD_NOW => 0x2; + public int RTLD_GLOBAL => 0x100; + public IntPtr RTLD_DEFAULT => IntPtr.Zero; + + public static ILibDL GetInstance() + { + try + { + ILibDL libdl2 = new LinuxLibDL2(); + // call dlerror to ensure library is resolved + libdl2.dlerror(); + return libdl2; + } catch (DllNotFoundException) + { + return new LinuxLibDL(); + } + } + + IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags); + IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol); + int ILibDL.dlclose(IntPtr handle) => dlclose(handle); + IntPtr ILibDL.dlerror() => dlerror(); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr dlopen(string fileName, int flags); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int dlclose(IntPtr handle); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr dlerror(); + } + + class LinuxLibDL2 : ILibDL + { + private const string NativeDll = "libdl.so.2"; + + public int RTLD_NOW => 0x2; + public int RTLD_GLOBAL => 0x100; + public IntPtr RTLD_DEFAULT => IntPtr.Zero; + + IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags); + IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol); + int ILibDL.dlclose(IntPtr handle) => dlclose(handle); + IntPtr ILibDL.dlerror() => dlerror(); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr dlopen(string fileName, int flags); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int dlclose(IntPtr handle); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr dlerror(); + } + + class MacLibDL : ILibDL + { + public int RTLD_NOW => 0x2; + public int RTLD_GLOBAL => 0x8; + const string NativeDll = "/usr/lib/libSystem.dylib"; + public IntPtr RTLD_DEFAULT => new(-2); + + IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags); + IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol); + int ILibDL.dlclose(IntPtr handle) => dlclose(handle); + IntPtr ILibDL.dlerror() => dlerror(); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr dlopen(string fileName, int flags); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] + private static extern int dlclose(IntPtr handle); + + [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr dlerror(); + } +} diff --git a/src/runtime/platform/LibraryLoader.cs b/src/runtime/platform/LibraryLoader.cs index 099b9d6cd..e361f87e4 100644 --- a/src/runtime/platform/LibraryLoader.cs +++ b/src/runtime/platform/LibraryLoader.cs @@ -28,9 +28,9 @@ public static ILibraryLoader Instance if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) _instance = new WindowsLoader(); else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - _instance = new LinuxLoader(); + _instance = new PosixLoader(LinuxLibDL.GetInstance()); else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - _instance = new DarwinLoader(); + _instance = new PosixLoader(new MacLibDL()); else throw new PlatformNotSupportedException( "This operating system is not supported" @@ -42,87 +42,19 @@ public static ILibraryLoader Instance } } - class LinuxLoader : ILibraryLoader + class PosixLoader : ILibraryLoader { - private static int RTLD_NOW = 0x2; - private static int RTLD_GLOBAL = 0x100; - private static IntPtr RTLD_DEFAULT = IntPtr.Zero; - private const string NativeDll = "libdl.so"; + private readonly ILibDL libDL; - public IntPtr Load(string dllToLoad) - { - ClearError(); - var res = dlopen(dllToLoad, RTLD_NOW | RTLD_GLOBAL); - if (res == IntPtr.Zero) - { - var err = GetError(); - throw new DllNotFoundException($"Could not load {dllToLoad} with flags RTLD_NOW | RTLD_GLOBAL: {err}"); - } - - return res; - } - - public void Free(IntPtr handle) - { - dlclose(handle); - } - - public IntPtr GetFunction(IntPtr dllHandle, string name) - { - // look in the exe if dllHandle is NULL - if (dllHandle == IntPtr.Zero) - { - dllHandle = RTLD_DEFAULT; - } - - ClearError(); - IntPtr res = dlsym(dllHandle, name); - if (res == IntPtr.Zero) - { - var err = GetError(); - throw new MissingMethodException($"Failed to load symbol {name}: {err}"); - } - return res; - } - - void ClearError() + public PosixLoader(ILibDL libDL) { - dlerror(); - } - - string GetError() - { - var res = dlerror(); - if (res != IntPtr.Zero) - return Marshal.PtrToStringAnsi(res); - else - return null; + this.libDL = libDL ?? throw new ArgumentNullException(nameof(libDL)); } - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr dlopen(string fileName, int flags); - - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - private static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] - private static extern int dlclose(IntPtr handle); - - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr dlerror(); - } - - class DarwinLoader : ILibraryLoader - { - private static int RTLD_NOW = 0x2; - private static int RTLD_GLOBAL = 0x8; - private const string NativeDll = "/usr/lib/libSystem.dylib"; - private static IntPtr RTLD_DEFAULT = new IntPtr(-2); - public IntPtr Load(string dllToLoad) { ClearError(); - var res = dlopen(dllToLoad, RTLD_NOW | RTLD_GLOBAL); + var res = libDL.dlopen(dllToLoad, libDL.RTLD_NOW | libDL.RTLD_GLOBAL); if (res == IntPtr.Zero) { var err = GetError(); @@ -134,7 +66,7 @@ public IntPtr Load(string dllToLoad) public void Free(IntPtr handle) { - dlclose(handle); + libDL.dlclose(handle); } public IntPtr GetFunction(IntPtr dllHandle, string name) @@ -142,11 +74,11 @@ public IntPtr GetFunction(IntPtr dllHandle, string name) // look in the exe if dllHandle is NULL if (dllHandle == IntPtr.Zero) { - dllHandle = RTLD_DEFAULT; + dllHandle = libDL.RTLD_DEFAULT; } ClearError(); - IntPtr res = dlsym(dllHandle, name); + IntPtr res = libDL.dlsym(dllHandle, name); if (res == IntPtr.Zero) { var err = GetError(); @@ -157,29 +89,17 @@ public IntPtr GetFunction(IntPtr dllHandle, string name) void ClearError() { - dlerror(); + libDL.dlerror(); } string GetError() { - var res = dlerror(); + var res = libDL.dlerror(); if (res != IntPtr.Zero) return Marshal.PtrToStringAnsi(res); else return null; } - - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - public static extern IntPtr dlopen(String fileName, int flags); - - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] - private static extern IntPtr dlsym(IntPtr handle, String symbol); - - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] - private static extern int dlclose(IntPtr handle); - - [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr dlerror(); } class WindowsLoader : ILibraryLoader