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 all commits
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
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
208 changes: 208 additions & 0 deletions src/runtime/platform/LibraryLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

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

IntPtr GetFunction(IntPtr hModule, string procedureName);

void 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 PlatformNotSupportedException($"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 dllToLoad)
{
var filename = $"lib{dllToLoad}.so";
ClearError();
var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (res == IntPtr.Zero)
{
var err = GetError();
throw new DllNotFoundException($"Could not load {filename} 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()
{
dlerror();
}

string GetError()
{
var res = 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 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)
{
var filename = $"lib{dllToLoad}.dylib";
ClearError();
var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (res == IntPtr.Zero)
{
var err = GetError();
throw new DllNotFoundException($"Could not load {filename} 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()
{
dlerror();
}

string GetError()
{
var res = 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
{
private const string NativeDll = "kernel32.dll";


public IntPtr Load(string dllToLoad)
{
var res = WindowsLoader.LoadLibrary(dllToLoad);
if (res == IntPtr.Zero)
throw new DllNotFoundException($"Could not load {dllToLoad}", new Win32Exception());
return res;
}

public IntPtr GetFunction(IntPtr hModule, string procedureName)
{
var res = WindowsLoader.GetProcAddress(hModule, procedureName);
if (res == IntPtr.Zero)
throw new MissingMethodException($"Failed to load symbol {procedureName}", new Win32Exception());
return res;
}

public void Free(IntPtr hModule) => WindowsLoader.FreeLibrary(hModule);

[DllImport(NativeDll, SetLastError = true)]
static extern IntPtr LoadLibrary(string dllToLoad);

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

[DllImport(NativeDll)]
static extern bool FreeLibrary(IntPtr 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
}
}
Loading