-
-
Notifications
You must be signed in to change notification settings - Fork 181
Expand file tree
/
Copy pathLibraryLoader.cs
More file actions
223 lines (186 loc) · 8.48 KB
/
LibraryLoader.cs
File metadata and controls
223 lines (186 loc) · 8.48 KB
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
/// @file
/// @brief File implementing the LlamaLib library loader
/// \cond HIDE
using System;
using System.Runtime.InteropServices;
namespace UndreamAI.LlamaLib
{
/// @ingroup utils
/// <summary>
/// Class implementing the LlamaLib library loader
/// Adapted from SkiaForUnity:
/// https://github.com/ammariqais/SkiaForUnity/blob/f43322218c736d1c41f3a3df9355b90db4259a07/SkiaUnity/Assets/SkiaSharp/SkiaSharp-Bindings/SkiaSharp.HarfBuzz.Shared/HarfBuzzSharp.Shared/LibraryLoader.cs
/// </summary>
static class LibraryLoader
{
/// <summary>
/// Allows to retrieve a function delegate for the library
/// </summary>
/// <typeparam name="T">type to cast the function</typeparam>
/// <param name="library">library handle</param>
/// <param name="name">function name</param>
/// <returns>function delegate</returns>
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);
}
/// <summary>
/// Loads the provided library in a cross-platform manner
/// </summary>
/// <param name="libraryPath">library path</param>
/// <returns>library handle</returns>
public static IntPtr LoadLibrary(string libraryPath)
{
if (string.IsNullOrEmpty(libraryPath))
throw new ArgumentNullException(nameof(libraryPath));
#if (ANDROID || IOS || VISIONOS) || (!UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS || UNITY_VISIONOS))
return Mobile.dlopen(libraryPath);
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Win32.LoadLibrary(libraryPath);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return Linux.dlopen(libraryPath);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return Mac.dlopen(libraryPath);
else throw new PlatformNotSupportedException($"Current platform is unknown, unable to load library '{libraryPath}'.");
#endif
}
/// <summary>
/// Retrieve a function delegate for the library in a cross-platform manner
/// </summary>
/// <param name="library">library handle</param>
/// <param name="symbolName">function name</param>
/// <returns>function handle</returns>
public static IntPtr GetSymbol(IntPtr library, string symbolName)
{
if (string.IsNullOrEmpty(symbolName))
throw new ArgumentNullException(nameof(symbolName));
#if (ANDROID || IOS || VISIONOS) || (!UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS || UNITY_VISIONOS))
return Mobile.dlsym(library, symbolName);
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Win32.GetProcAddress(library, symbolName);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return Linux.dlsym(library, symbolName);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return Mac.dlsym(library, symbolName);
else throw new PlatformNotSupportedException($"Current platform is unknown, unable to load symbol '{symbolName}' from library {library}.");
#endif
}
/// <summary>
/// Frees up the library
/// </summary>
/// <param name="library">library handle</param>
public static void FreeLibrary(IntPtr library)
{
if (library == IntPtr.Zero)
return;
#if (ANDROID || IOS || VISIONOS) || (!UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS || UNITY_VISIONOS))
Mobile.dlclose(library);
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
Win32.FreeLibrary(library);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
Linux.dlclose(library);
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
Mac.dlclose(library);
else throw new PlatformNotSupportedException($"Current platform is unknown, unable to close library '{library}'.");
#endif
}
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 static bool UseSystemLibrary2 = true;
public static IntPtr dlopen(string path, bool lazy = true)
{
try
{
return dlopen2(path, lazy ? RTLD_LAZY : RTLD_NOW);
}
catch (DllNotFoundException)
{
UseSystemLibrary2 = false;
return dlopen1(path, lazy ? RTLD_LAZY : RTLD_NOW);
}
}
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)]
public static extern bool FreeLibrary(IntPtr hModule);
}
private static class Mobile
{
public static IntPtr dlopen(string path) => dlopen(path, 1);
#if (ANDROID || IOS || VISIONOS) || (!UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS || UNITY_VISIONOS))
[DllImport("__Internal")]
public static extern IntPtr dlopen(string filename, int flags);
[DllImport("__Internal")]
public static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("__Internal")]
public static extern int dlclose(IntPtr handle);
#else
public static IntPtr dlopen(string filename, int flags)
{
return IntPtr.Zero;
}
public static IntPtr dlsym(IntPtr handle, string symbol)
{
return IntPtr.Zero;
}
public static int dlclose(IntPtr handle)
{
return 0;
}
#endif
}
}
}