Skip to content

Get the correct library loading functions at runtime #894

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

Merged
merged 2 commits into from
Jun 26, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Get the correct library loading functions at runtime
  • Loading branch information
filmor committed Jun 25, 2019
commit 1dd2ee1b02449b85eeee6120c88a4092dc78851a
7 changes: 4 additions & 3 deletions src/embed_tests/TestRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using NUnit.Framework;
using Python.Runtime;
using Python.Runtime.Platform;

namespace Python.EmbeddingTest
{
Expand All @@ -26,10 +27,10 @@ public static void PlatformCache()
{
Runtime.Runtime.Initialize();

Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(Runtime.Runtime.MachineType.Other));
Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(MachineType.Other));
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.MachineName));

Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(Runtime.Runtime.OperatingSystemType.Other));
Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(OperatingSystemType.Other));
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.OperatingSystemName));

// Don't shut down the runtime: if the python engine was initialized
Expand All @@ -39,7 +40,7 @@ public static void PlatformCache()
[Test]
public static void Py_IsInitializedValue()
{
Runtime.Runtime.Py_Finalize();
Runtime.Runtime.Py_Finalize();
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
Runtime.Runtime.Py_Initialize();
Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized());
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/Python.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@
<Compile Include="typemanager.cs" />
<Compile Include="typemethod.cs" />
<Compile Include="Util.cs" />
<Compile Include="platform\Types.cs" />
<Compile Include="platform\LibraryLoader.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(PythonInteropFile)' != '' ">
<Compile Include="$(PythonInteropFile)" />
Expand Down
153 changes: 153 additions & 0 deletions src/runtime/platform/LibraryLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using System.Runtime.InteropServices;

namespace Python.Runtime.Platform
{
interface ILibraryLoader
{
IntPtr Load(string dllToLoad);

IntPtr GetFunction(IntPtr hModule, string procedureName);

bool Free(IntPtr hModule);
}

static class LibraryLoader
{
public static ILibraryLoader Get(OperatingSystemType os)
{
switch (os)
{
case OperatingSystemType.Windows:
return new WindowsLoader();
case OperatingSystemType.Darwin:
return new DarwinLoader();
case OperatingSystemType.Linux:
return new LinuxLoader();
default:
throw new Exception($"This operating system ({os}) is not supported");
}
}
}

class LinuxLoader : 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";

public IntPtr Load(string fileName)
{
return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL);
}

public bool Free(IntPtr handle)
{
dlclose(handle);
return true;
}

public IntPtr GetFunction(IntPtr dllHandle, string name)
{
// look in the exe if dllHandle is NULL
if (dllHandle == IntPtr.Zero)
{
dllHandle = RTLD_DEFAULT;
}

// clear previous errors if any
dlerror();
IntPtr res = dlsym(dllHandle, name);
IntPtr errPtr = dlerror();
if (errPtr != IntPtr.Zero)
{
throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
}
return res;
}

[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 fileName)
{
return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL);
}

public bool Free(IntPtr handle)
{
dlclose(handle);
return true;
}

public IntPtr GetFunction(IntPtr dllHandle, string name)
{
// look in the exe if dllHandle is NULL
if (dllHandle == IntPtr.Zero)
{
dllHandle = RTLD_DEFAULT;
}

// clear previous errors if any
dlerror();
IntPtr res = dlsym(dllHandle, name);
IntPtr errPtr = dlerror();
if (errPtr != IntPtr.Zero)
{
throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
}
return res;
}

[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
{
private const string NativeDll = "kernel32.dll";

[DllImport(NativeDll)]
static extern IntPtr LoadLibrary(string dllToLoad);

public IntPtr Load(string dllToLoad) => WindowsLoader.LoadLibrary(dllToLoad);

[DllImport(NativeDll)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

public IntPtr GetFunction(IntPtr hModule, string procedureName) => WindowsLoader.GetProcAddress(hModule, procedureName);


[DllImport(NativeDll)]
static extern bool FreeLibrary(IntPtr hModule);

public bool Free(IntPtr hModule) => WindowsLoader.FreeLibrary(hModule);
}
}
22 changes: 22 additions & 0 deletions src/runtime/platform/Types.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Python.Runtime.Platform
{
public enum MachineType
{
i386,
x86_64,
armv7l,
armv8,
Other
};

/// <summary>
/// Operating system type as reported by Python.
/// </summary>
public enum OperatingSystemType
{
Windows,
Darwin,
Linux,
Other
}
}
128 changes: 10 additions & 118 deletions src/runtime/runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,96 +7,7 @@

namespace Python.Runtime
{
[SuppressUnmanagedCodeSecurity]
internal static class NativeMethods
{
#if MONO_LINUX || MONO_OSX
#if NETSTANDARD
private static int RTLD_NOW = 0x2;
#if MONO_LINUX
private static int RTLD_GLOBAL = 0x100;
private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
private const string NativeDll = "libdl.so";
public static IntPtr LoadLibrary(string fileName)
{
return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL);
}
#elif MONO_OSX
private static int RTLD_GLOBAL = 0x8;
private const string NativeDll = "/usr/lib/libSystem.dylib";
private static IntPtr RTLD_DEFAULT = new IntPtr(-2);

public static IntPtr LoadLibrary(string fileName)
{
return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL);
}
#endif
#else
private static int RTLD_NOW = 0x2;
private static int RTLD_SHARED = 0x20;
#if MONO_OSX
private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
private const string NativeDll = "__Internal";
#elif MONO_LINUX
private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
private const string NativeDll = "libdl.so";
#endif

public static IntPtr LoadLibrary(string fileName)
{
return dlopen(fileName, RTLD_NOW | RTLD_SHARED);
}
#endif


public static void FreeLibrary(IntPtr handle)
{
dlclose(handle);
}

public static IntPtr GetProcAddress(IntPtr dllHandle, string name)
{
// look in the exe if dllHandle is NULL
if (dllHandle == IntPtr.Zero)
{
dllHandle = RTLD_DEFAULT;
}

// clear previous errors if any
dlerror();
IntPtr res = dlsym(dllHandle, name);
IntPtr errPtr = dlerror();
if (errPtr != IntPtr.Zero)
{
throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
}
return res;
}

[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();
#else // Windows
private const string NativeDll = "kernel32.dll";

[DllImport(NativeDll)]
public static extern IntPtr LoadLibrary(string dllToLoad);

[DllImport(NativeDll)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

[DllImport(NativeDll)]
public static extern bool FreeLibrary(IntPtr hModule);
#endif
}
using Python.Runtime.Platform;

/// <summary>
/// Encapsulates the low-level Python C API. Note that it is
Expand Down Expand Up @@ -197,17 +108,6 @@ public class Runtime
// .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;

/// <summary>
/// Operating system type as reported by Python.
/// </summary>
public enum OperatingSystemType
{
Windows,
Darwin,
Linux,
Other
}

static readonly Dictionary<string, OperatingSystemType> OperatingSystemTypeMapping = new Dictionary<string, OperatingSystemType>()
{
{ "Windows", OperatingSystemType.Windows },
Expand All @@ -225,14 +125,6 @@ public enum OperatingSystemType
/// </summary>
public static string OperatingSystemName { get; private set; }

public enum MachineType
{
i386,
x86_64,
armv7l,
armv8,
Other
};

/// <summary>
/// Map lower-case version of the python machine name to the processor
Expand Down Expand Up @@ -397,24 +289,24 @@ internal static void Initialize(bool initSigs = false)

Error = new IntPtr(-1);

// Initialize data about the platform we're running on. We need
// this for the type manager and potentially other details. Must
// happen after caching the python types, above.
InitializePlatformData();

IntPtr dllLocal = IntPtr.Zero;
var loader = LibraryLoader.Get(OperatingSystem);

if (_PythonDll != "__Internal")
{
dllLocal = NativeMethods.LoadLibrary(_PythonDll);
dllLocal = loader.Load(_PythonDll);
}
_PyObject_NextNotImplemented = NativeMethods.GetProcAddress(dllLocal, "_PyObject_NextNotImplemented");
_PyObject_NextNotImplemented = loader.GetFunction(dllLocal, "_PyObject_NextNotImplemented");

#if !(MONO_LINUX || MONO_OSX)
if (dllLocal != IntPtr.Zero)
{
NativeMethods.FreeLibrary(dllLocal);
loader.Free(dllLocal);
}
#endif
// Initialize data about the platform we're running on. We need
// this for the type manager and potentially other details. Must
// happen after caching the python types, above.
InitializePlatformData();

// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
Expand Down
Loading