Skip to content

Commit 483f192

Browse files
committed
try libdl.so.2 on Linux before libdl.so
also refactored LibraryLoader to share code between Linux and Mac fixes #1422
1 parent 1e5338f commit 483f192

File tree

2 files changed

+121
-92
lines changed

2 files changed

+121
-92
lines changed

src/runtime/platform/LibDL.cs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#pragma warning disable IDE1006 // Naming Styles (interface for native functions)
2+
using System;
3+
using System.Runtime.InteropServices;
4+
5+
namespace Python.Runtime.Platform
6+
{
7+
interface ILibDL
8+
{
9+
IntPtr dlopen(string fileName, int flags);
10+
IntPtr dlsym(IntPtr handle, string symbol);
11+
int dlclose(IntPtr handle);
12+
IntPtr dlerror();
13+
14+
int RTLD_NOW { get; }
15+
int RTLD_GLOBAL { get; }
16+
IntPtr RTLD_DEFAULT { get; }
17+
}
18+
19+
class LinuxLibDL : ILibDL
20+
{
21+
private const string NativeDll = "libdl.so";
22+
23+
public int RTLD_NOW => 0x2;
24+
public int RTLD_GLOBAL => 0x100;
25+
public IntPtr RTLD_DEFAULT => IntPtr.Zero;
26+
27+
public static ILibDL GetInstance()
28+
{
29+
try
30+
{
31+
ILibDL libdl2 = new LinuxLibDL2();
32+
// call dlerror to ensure library is resolved
33+
libdl2.dlerror();
34+
return libdl2;
35+
} catch (DllNotFoundException)
36+
{
37+
return new LinuxLibDL();
38+
}
39+
}
40+
41+
IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags);
42+
IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol);
43+
int ILibDL.dlclose(IntPtr handle) => dlclose(handle);
44+
IntPtr ILibDL.dlerror() => dlerror();
45+
46+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
47+
private static extern IntPtr dlopen(string fileName, int flags);
48+
49+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
50+
private static extern IntPtr dlsym(IntPtr handle, string symbol);
51+
52+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
53+
private static extern int dlclose(IntPtr handle);
54+
55+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
56+
private static extern IntPtr dlerror();
57+
}
58+
59+
class LinuxLibDL2 : ILibDL
60+
{
61+
private const string NativeDll = "libdl.so.2";
62+
63+
public int RTLD_NOW => 0x2;
64+
public int RTLD_GLOBAL => 0x100;
65+
public IntPtr RTLD_DEFAULT => IntPtr.Zero;
66+
67+
IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags);
68+
IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol);
69+
int ILibDL.dlclose(IntPtr handle) => dlclose(handle);
70+
IntPtr ILibDL.dlerror() => dlerror();
71+
72+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
73+
private static extern IntPtr dlopen(string fileName, int flags);
74+
75+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
76+
private static extern IntPtr dlsym(IntPtr handle, string symbol);
77+
78+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
79+
private static extern int dlclose(IntPtr handle);
80+
81+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
82+
private static extern IntPtr dlerror();
83+
}
84+
85+
class MacLibDL : ILibDL
86+
{
87+
public int RTLD_NOW => 0x2;
88+
public int RTLD_GLOBAL => 0x8;
89+
const string NativeDll = "/usr/lib/libSystem.dylib";
90+
public IntPtr RTLD_DEFAULT => new(-2);
91+
92+
IntPtr ILibDL.dlopen(string fileName, int flags) => dlopen(fileName, flags);
93+
IntPtr ILibDL.dlsym(IntPtr handle, string symbol) => dlsym(handle, symbol);
94+
int ILibDL.dlclose(IntPtr handle) => dlclose(handle);
95+
IntPtr ILibDL.dlerror() => dlerror();
96+
97+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
98+
private static extern IntPtr dlopen(string fileName, int flags);
99+
100+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
101+
private static extern IntPtr dlsym(IntPtr handle, string symbol);
102+
103+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
104+
private static extern int dlclose(IntPtr handle);
105+
106+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
107+
private static extern IntPtr dlerror();
108+
}
109+
}

src/runtime/platform/LibraryLoader.cs

+12-92
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ public static ILibraryLoader Instance
2828
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
2929
_instance = new WindowsLoader();
3030
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
31-
_instance = new LinuxLoader();
31+
_instance = new PosixLoader(LinuxLibDL.GetInstance());
3232
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
33-
_instance = new DarwinLoader();
33+
_instance = new PosixLoader(new MacLibDL());
3434
else
3535
throw new PlatformNotSupportedException(
3636
"This operating system is not supported"
@@ -42,87 +42,19 @@ public static ILibraryLoader Instance
4242
}
4343
}
4444

45-
class LinuxLoader : ILibraryLoader
45+
class PosixLoader : ILibraryLoader
4646
{
47-
private static int RTLD_NOW = 0x2;
48-
private static int RTLD_GLOBAL = 0x100;
49-
private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
50-
private const string NativeDll = "libdl.so";
47+
private readonly ILibDL libDL;
5148

52-
public IntPtr Load(string dllToLoad)
53-
{
54-
ClearError();
55-
var res = dlopen(dllToLoad, RTLD_NOW | RTLD_GLOBAL);
56-
if (res == IntPtr.Zero)
57-
{
58-
var err = GetError();
59-
throw new DllNotFoundException($"Could not load {dllToLoad} with flags RTLD_NOW | RTLD_GLOBAL: {err}");
60-
}
61-
62-
return res;
63-
}
64-
65-
public void Free(IntPtr handle)
66-
{
67-
dlclose(handle);
68-
}
69-
70-
public IntPtr GetFunction(IntPtr dllHandle, string name)
71-
{
72-
// look in the exe if dllHandle is NULL
73-
if (dllHandle == IntPtr.Zero)
74-
{
75-
dllHandle = RTLD_DEFAULT;
76-
}
77-
78-
ClearError();
79-
IntPtr res = dlsym(dllHandle, name);
80-
if (res == IntPtr.Zero)
81-
{
82-
var err = GetError();
83-
throw new MissingMethodException($"Failed to load symbol {name}: {err}");
84-
}
85-
return res;
86-
}
87-
88-
void ClearError()
49+
public PosixLoader(ILibDL libDL)
8950
{
90-
dlerror();
91-
}
92-
93-
string GetError()
94-
{
95-
var res = dlerror();
96-
if (res != IntPtr.Zero)
97-
return Marshal.PtrToStringAnsi(res);
98-
else
99-
return null;
51+
this.libDL = libDL ?? throw new ArgumentNullException(nameof(libDL));
10052
}
10153

102-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
103-
public static extern IntPtr dlopen(string fileName, int flags);
104-
105-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
106-
private static extern IntPtr dlsym(IntPtr handle, string symbol);
107-
108-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
109-
private static extern int dlclose(IntPtr handle);
110-
111-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
112-
private static extern IntPtr dlerror();
113-
}
114-
115-
class DarwinLoader : ILibraryLoader
116-
{
117-
private static int RTLD_NOW = 0x2;
118-
private static int RTLD_GLOBAL = 0x8;
119-
private const string NativeDll = "/usr/lib/libSystem.dylib";
120-
private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
121-
12254
public IntPtr Load(string dllToLoad)
12355
{
12456
ClearError();
125-
var res = dlopen(dllToLoad, RTLD_NOW | RTLD_GLOBAL);
57+
var res = libDL.dlopen(dllToLoad, libDL.RTLD_NOW | libDL.RTLD_GLOBAL);
12658
if (res == IntPtr.Zero)
12759
{
12860
var err = GetError();
@@ -134,19 +66,19 @@ public IntPtr Load(string dllToLoad)
13466

13567
public void Free(IntPtr handle)
13668
{
137-
dlclose(handle);
69+
libDL.dlclose(handle);
13870
}
13971

14072
public IntPtr GetFunction(IntPtr dllHandle, string name)
14173
{
14274
// look in the exe if dllHandle is NULL
14375
if (dllHandle == IntPtr.Zero)
14476
{
145-
dllHandle = RTLD_DEFAULT;
77+
dllHandle = libDL.RTLD_DEFAULT;
14678
}
14779

14880
ClearError();
149-
IntPtr res = dlsym(dllHandle, name);
81+
IntPtr res = libDL.dlsym(dllHandle, name);
15082
if (res == IntPtr.Zero)
15183
{
15284
var err = GetError();
@@ -157,29 +89,17 @@ public IntPtr GetFunction(IntPtr dllHandle, string name)
15789

15890
void ClearError()
15991
{
160-
dlerror();
92+
libDL.dlerror();
16193
}
16294

16395
string GetError()
16496
{
165-
var res = dlerror();
97+
var res = libDL.dlerror();
16698
if (res != IntPtr.Zero)
16799
return Marshal.PtrToStringAnsi(res);
168100
else
169101
return null;
170102
}
171-
172-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
173-
public static extern IntPtr dlopen(String fileName, int flags);
174-
175-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
176-
private static extern IntPtr dlsym(IntPtr handle, String symbol);
177-
178-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
179-
private static extern int dlclose(IntPtr handle);
180-
181-
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
182-
private static extern IntPtr dlerror();
183103
}
184104

185105
class WindowsLoader : ILibraryLoader

0 commit comments

Comments
 (0)