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

Lines changed: 109 additions & 0 deletions
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

Lines changed: 12 additions & 92 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy