forked from mono/SkiaSharp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLibraryLoader.cs
273 lines (223 loc) · 7.77 KB
/
LibraryLoader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
#nullable disable
using System;
using System.IO;
using System.Runtime.InteropServices;
#if HARFBUZZ
using HarfBuzzSharp.Internals;
#else
using SkiaSharp.Internals;
#endif
#if HARFBUZZ
namespace HarfBuzzSharp
#else
namespace SkiaSharp
#endif
{
#if USE_DELEGATES || USE_LIBRARY_LOADER
internal static class LibraryLoader
{
static LibraryLoader ()
{
if (PlatformConfiguration.IsWindows)
Extension = ".dll";
else if (PlatformConfiguration.IsMac)
Extension = ".dylib";
else
Extension = ".so";
}
public static string Extension { get; }
public static IntPtr LoadLocalLibrary<T> (string libraryName)
{
var libraryPath = GetLibraryPath (libraryName);
var handle = LoadLibrary (libraryPath);
if (handle == IntPtr.Zero)
throw new DllNotFoundException ($"Unable to load library '{libraryName}'.");
return handle;
static string GetLibraryPath (string libraryName)
{
#if NET
var arch = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();
#else
var arch = PlatformConfiguration.Is64Bit
? PlatformConfiguration.IsArm ? "arm64" : "x64"
: PlatformConfiguration.IsArm ? "arm" : "x86";
#endif
var libWithExt = libraryName;
if (!libraryName.EndsWith (Extension, StringComparison.OrdinalIgnoreCase))
libWithExt += Extension;
// 1. try alongside managed assembly
var path = typeof (T).Assembly.Location;
if (!string.IsNullOrEmpty (path)) {
path = Path.GetDirectoryName (path);
if (CheckLibraryPath (path, arch, libWithExt, out var localLib))
return localLib;
}
// 2. try current directory
if (CheckLibraryPath (Directory.GetCurrentDirectory (), arch, libWithExt, out var lib))
return lib;
// 3. try app domain
try {
if (AppDomain.CurrentDomain is AppDomain domain) {
// 3.1 RelativeSearchPath
if (CheckLibraryPath (domain.RelativeSearchPath, arch, libWithExt, out lib))
return lib;
// 3.2 BaseDirectory
if (CheckLibraryPath (domain.BaseDirectory, arch, libWithExt, out lib))
return lib;
}
} catch {
// no-op as there may not be any domain or path
}
// 4. use PATH or default loading mechanism
return libWithExt;
}
static bool CheckLibraryPath(string root, string arch, string libWithExt, out string foundPath)
{
if (!string.IsNullOrEmpty (root)) {
// a. in specific platform sub dir
if (!string.IsNullOrEmpty (PlatformConfiguration.LinuxFlavor)) {
var muslLib = Path.Combine (root, PlatformConfiguration.LinuxFlavor + "-" + arch, libWithExt);
if (File.Exists (muslLib)) {
foundPath = muslLib;
return true;
}
}
// b. in generic platform sub dir
var searchLib = Path.Combine (root, arch, libWithExt);
if (File.Exists (searchLib)) {
foundPath = searchLib;
return true;
}
// c. in root
searchLib = Path.Combine (root, libWithExt);
if (File.Exists (searchLib)) {
foundPath = searchLib;
return true;
}
}
// d. nothing
foundPath = null;
return false;
}
}
public static T GetSymbolDelegate<T> (IntPtr library, string name)
where T : Delegate
{
var symbol = GetSymbol (library, name);
if (symbol == IntPtr.Zero)
throw new EntryPointNotFoundException ($"Unable to load symbol '{name}'.");
return Marshal.GetDelegateForFunctionPointer<T> (symbol);
}
public static IntPtr LoadLibrary (string libraryName)
{
if (string.IsNullOrEmpty (libraryName))
throw new ArgumentNullException (nameof (libraryName));
IntPtr handle;
if (PlatformConfiguration.IsWindows)
handle = Win32.LoadLibrary (libraryName);
else if (PlatformConfiguration.IsLinux)
handle = Linux.dlopen (libraryName);
else if (PlatformConfiguration.IsMac)
handle = Mac.dlopen (libraryName);
else
throw new PlatformNotSupportedException ($"Current platform is unknown, unable to load library '{libraryName}'.");
return handle;
}
public static IntPtr GetSymbol (IntPtr library, string symbolName)
{
if (string.IsNullOrEmpty (symbolName))
throw new ArgumentNullException (nameof (symbolName));
IntPtr handle;
if (PlatformConfiguration.IsWindows)
handle = Win32.GetProcAddress (library, symbolName);
else if (PlatformConfiguration.IsLinux)
handle = Linux.dlsym (library, symbolName);
else if (PlatformConfiguration.IsMac)
handle = Mac.dlsym (library, symbolName);
else
throw new PlatformNotSupportedException ($"Current platform is unknown, unable to load symbol '{symbolName}' from library {library}.");
return handle;
}
public static void FreeLibrary (IntPtr library)
{
if (library == IntPtr.Zero)
return;
if (PlatformConfiguration.IsWindows)
Win32.FreeLibrary (library);
else if (PlatformConfiguration.IsLinux)
Linux.dlclose (library);
else if (PlatformConfiguration.IsMac)
Mac.dlclose (library);
else
throw new PlatformNotSupportedException ($"Current platform is unknown, unable to close library '{library}'.");
}
#pragma warning disable IDE1006 // Naming Styles
private static class Mac
{
private const string SystemLibrary = "/usr/lib/libSystem.dylib";
private const int RTLD_LAZY = 1;
private const int RTLD_NOW = 2;
public static IntPtr dlopen (string path, bool lazy = true) =>
dlopen (path, lazy ? RTLD_LAZY : RTLD_NOW);
[DllImport (SystemLibrary)]
public static extern IntPtr dlopen (string path, int mode);
[DllImport (SystemLibrary)]
public static extern IntPtr dlsym (IntPtr handle, string symbol);
[DllImport (SystemLibrary)]
public static extern void dlclose (IntPtr handle);
}
private static class Linux
{
private const string SystemLibrary = "libdl.so";
private const string SystemLibrary2 = "libdl.so.2"; // newer Linux distros use this
private const int RTLD_LAZY = 1;
private const int RTLD_NOW = 2;
private const int RTLD_DEEPBIND = 8;
private static bool UseSystemLibrary2 = true;
public static IntPtr dlopen (string path, bool lazy = true)
{
try {
return dlopen2 (path, (lazy ? RTLD_LAZY : RTLD_NOW) | RTLD_DEEPBIND);
} catch (DllNotFoundException) {
UseSystemLibrary2 = false;
return dlopen1 (path, (lazy ? RTLD_LAZY : RTLD_NOW) | RTLD_DEEPBIND);
}
}
public static IntPtr dlsym (IntPtr handle, string symbol)
{
return UseSystemLibrary2 ? dlsym2 (handle, symbol) : dlsym1 (handle, symbol);
}
public static void dlclose (IntPtr handle)
{
if (UseSystemLibrary2)
dlclose2 (handle);
else
dlclose1 (handle);
}
[DllImport (SystemLibrary, EntryPoint="dlopen")]
private static extern IntPtr dlopen1 (string path, int mode);
[DllImport (SystemLibrary, EntryPoint="dlsym")]
private static extern IntPtr dlsym1 (IntPtr handle, string symbol);
[DllImport (SystemLibrary, EntryPoint="dlclose")]
private static extern void dlclose1 (IntPtr handle);
[DllImport (SystemLibrary2, EntryPoint="dlopen")]
private static extern IntPtr dlopen2 (string path, int mode);
[DllImport (SystemLibrary2, EntryPoint="dlsym")]
private static extern IntPtr dlsym2 (IntPtr handle, string symbol);
[DllImport (SystemLibrary2, EntryPoint="dlclose")]
private static extern void dlclose2 (IntPtr handle);
}
private static class Win32
{
private const string SystemLibrary = "Kernel32.dll";
[DllImport (SystemLibrary, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr LoadLibrary (string lpFileName);
[DllImport (SystemLibrary, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress (IntPtr hModule, string lpProcName);
[DllImport (SystemLibrary, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern void FreeLibrary (IntPtr hModule);
}
#pragma warning restore IDE1006 // Naming Styles
}
#endif
}