132
|
1 /**
|
|
2 * The shared library module provides a basic layer around the native functions
|
|
3 * used to load symbols from shared libraries.
|
|
4 *
|
|
5 * Copyright: Copyright (C) 2007 Tomasz Stachowiak
|
|
6 * License: BSD style: $(LICENSE)
|
|
7 * Authors: Tomasz Stachowiak, Anders Bergh
|
|
8 */
|
|
9
|
|
10 module tango.sys.SharedLib;
|
|
11
|
|
12
|
|
13 private {
|
|
14 import tango.core.Exception : TracedException;
|
|
15 import tango.stdc.stringz : fromUtf8z;
|
|
16
|
|
17 version (Windows) {
|
|
18 import tango.sys.Common : SysError;
|
|
19
|
|
20 alias void* HINSTANCE, HMODULE;
|
|
21 alias int BOOL;
|
|
22
|
|
23 extern (Windows) {
|
|
24 void* GetProcAddress(HINSTANCE, char*);
|
|
25 HINSTANCE LoadLibraryA(char*);
|
|
26 BOOL FreeLibrary(HMODULE);
|
|
27 }
|
|
28 }
|
|
29 else version (Posix) {
|
|
30 import tango.stdc.posix.dlfcn;
|
|
31 }
|
|
32 else {
|
|
33 static assert (false, "No support for this platform");
|
|
34 }
|
|
35
|
|
36 version (SharedLibVerbose) import tango.util.log.Trace;
|
|
37 }
|
|
38
|
|
39
|
|
40 /**
|
|
41 SharedLib is an interface to system-specific shared libraries, such
|
|
42 as ".dll", ".so" or ".dylib" files. It provides a simple interface to obtain
|
|
43 symbol addresses (such as function pointers) from these libraries.
|
|
44
|
|
45 Example:
|
|
46 ----
|
|
47
|
|
48 void main() {
|
|
49 if (auto lib = SharedLib.load(`c:\windows\system32\opengl32.dll`)) {
|
|
50 Trace.formatln("Library successfully loaded");
|
|
51
|
|
52 void* ptr = lib.getSymbol("glClear");
|
|
53 if (ptr) {
|
|
54 Trace.formatln("Symbol glClear found. Address = 0x{:x}", ptr);
|
|
55 } else {
|
|
56 Trace.formatln("Symbol glClear not found");
|
|
57 }
|
|
58
|
|
59 lib.unload();
|
|
60 } else {
|
|
61 Trace.formatln("Could not load the library");
|
|
62 }
|
|
63
|
|
64 assert (0 == SharedLib.numLoadedLibs);
|
|
65 }
|
|
66
|
|
67 ----
|
|
68
|
|
69 This implementation uses reference counting, thus a library is not loaded
|
|
70 again if it has been loaded before and not unloaded by the user.
|
|
71 Unloading a SharedLib decreases its reference count. When it reaches 0,
|
|
72 the shared library associated with it is unloaded and the SharedLib instance
|
|
73 is deleted. Please do not delete SharedLib instances manually, unload() will
|
|
74 take care of it.
|
|
75
|
|
76 Note:
|
|
77 SharedLib is thread-safe.
|
|
78 */
|
|
79 final class SharedLib {
|
|
80 /// Mapped from RTLD_NOW, RTLD_LAZY, RTLD_GLOBAL and RTLD_LOCAL
|
|
81 enum LoadMode {
|
|
82 Now = 0b1,
|
|
83 Lazy = 0b10,
|
|
84 Global = 0b100,
|
|
85 Local = 0b1000
|
|
86 }
|
|
87
|
|
88
|
|
89 /**
|
|
90 Loads an OS-specific shared library.
|
|
91
|
|
92 Note:
|
|
93 Please use this function instead of the constructor, which is private.
|
|
94
|
|
95 Params:
|
|
96 path = The path to a shared library to be loaded
|
|
97 mode = Library loading mode. See LoadMode
|
|
98
|
|
99 Returns:
|
|
100 A SharedLib instance being a handle to the library, or null if it
|
|
101 could not be loaded and SharedLib.throwExceptions is set to false.
|
|
102 Otherwise, throws SharedLibException
|
|
103 */
|
|
104 static SharedLib load(char[] path, LoadMode mode = LoadMode.Now | LoadMode.Global) {
|
|
105 SharedLib res;
|
|
106
|
|
107 synchronized (mutex) {
|
|
108 auto lib = path in loadedLibs;
|
|
109 if (lib) {
|
|
110 version (SharedLibVerbose) Trace.formatln("SharedLib found in the hashmap");
|
|
111 res = *lib;
|
|
112 }
|
|
113 else {
|
|
114 version (SharedLibVerbose) Trace.formatln("Creating a new instance of SharedLib");
|
|
115 res = new SharedLib(path);
|
|
116 loadedLibs[path] = res;
|
|
117 }
|
|
118
|
|
119 ++res.refCnt;
|
|
120 }
|
|
121
|
|
122 bool delRes = false;
|
|
123 Exception exc;
|
|
124
|
|
125 synchronized (res) {
|
|
126 if (!res.loaded) {
|
|
127 version (SharedLibVerbose) Trace.formatln("Loading the SharedLib");
|
|
128 try {
|
|
129 res.load_(mode);
|
|
130 } catch (Exception e) {
|
|
131 exc = e;
|
|
132 }
|
|
133 }
|
|
134
|
|
135 if (res.loaded) {
|
|
136 version (SharedLibVerbose) Trace.formatln("SharedLib successfully loaded, returning");
|
|
137 return res;
|
|
138 } else {
|
|
139 synchronized (mutex) {
|
|
140 if (path in loadedLibs) {
|
|
141 version (SharedLibVerbose) Trace.formatln("Removing the SharedLib from the hashmap");
|
|
142 loadedLibs.remove(path);
|
|
143 }
|
|
144 }
|
|
145 }
|
|
146
|
|
147 // make sure that only one thread will delete the object
|
|
148 if (0 == --res.refCnt) {
|
|
149 delRes = true;
|
|
150 }
|
|
151 }
|
|
152
|
|
153 if (delRes) {
|
|
154 version (SharedLibVerbose) Trace.formatln("Deleting the SharedLib");
|
|
155 delete res;
|
|
156 }
|
|
157
|
|
158 if (exc !is null) {
|
|
159 throw exc;
|
|
160 }
|
|
161
|
|
162 version (SharedLibVerbose) Trace.formatln("SharedLib not loaded, returning null");
|
|
163 return null;
|
|
164 }
|
|
165
|
|
166
|
|
167 /**
|
|
168 Unloads the OS-specific shared library associated with this SharedLib instance.
|
|
169
|
|
170 Note:
|
|
171 It's invalid to use the object after unload() has been called, as unload()
|
|
172 will delete it if it's not referenced any more.
|
|
173
|
|
174 Throws SharedLibException on failure. In this case, the SharedLib object is not deleted.
|
|
175 */
|
|
176 void unload() {
|
|
177 bool deleteThis = false;
|
|
178
|
|
179 synchronized (this) {
|
|
180 assert (loaded);
|
|
181 assert (refCnt > 0);
|
|
182
|
|
183 synchronized (mutex) {
|
|
184 if (--refCnt <= 0) {
|
|
185 version (SharedLibVerbose) Trace.formatln("Unloading the SharedLib");
|
|
186 try {
|
|
187 unload_();
|
|
188 } catch (Exception e) {
|
|
189 ++refCnt;
|
|
190 throw e;
|
|
191 }
|
|
192
|
|
193 assert ((path in loadedLibs) !is null);
|
|
194 loadedLibs.remove(path);
|
|
195
|
|
196 deleteThis = true;
|
|
197 }
|
|
198 }
|
|
199 }
|
|
200 if (deleteThis) {
|
|
201 version (SharedLibVerbose) Trace.formatln("Deleting the SharedLib");
|
|
202 delete this;
|
|
203 }
|
|
204 }
|
|
205
|
|
206
|
|
207 /**
|
|
208 Returns the path to the OS-specific shared library associated with this object.
|
|
209 */
|
|
210 char[] path() {
|
|
211 return this.path_;
|
|
212 }
|
|
213
|
|
214
|
|
215 /**
|
|
216 Obtains the address of a symbol within the shared library
|
|
217
|
|
218 Params:
|
|
219 name = The name of the symbol; must be a null-terminated C string
|
|
220
|
|
221 Returns:
|
|
222 A pointer to the symbol or null if it's not present in the library
|
|
223 and SharedLib.throwExceptions is set to false. Otherwise, throws SharedLibException
|
|
224 */
|
|
225 void* getSymbol(char* name) {
|
|
226 assert (loaded);
|
|
227 return getSymbol_(name);
|
|
228 }
|
|
229
|
|
230
|
|
231
|
|
232 /**
|
|
233 Returns the total number of libraries currently loaded by SharedLib
|
|
234 */
|
|
235 static uint numLoadedLibs() {
|
|
236 return loadedLibs.keys.length;
|
|
237 }
|
|
238
|
|
239
|
|
240 private {
|
|
241 version (Windows) {
|
|
242 HMODULE handle;
|
|
243
|
|
244 void load_(LoadMode mode) {
|
|
245 handle = LoadLibraryA((this.path_ ~ \0).ptr);
|
|
246 if (handle is null && SharedLib.throwExceptions) {
|
|
247 throw new SharedLibException("Couldn't load shared library '" ~ this.path_ ~ "' : " ~ SysError.lastMsg);
|
|
248 }
|
|
249 }
|
|
250
|
|
251 void* getSymbol_(char* name) {
|
|
252 auto res = GetProcAddress(handle, name);
|
|
253 if (res is null && SharedLib.throwExceptions) {
|
|
254 throw new SharedLibException("Couldn't load symbol '" ~ fromUtf8z(name) ~ "' from shared library '" ~ this.path_ ~ "' : " ~ SysError.lastMsg);
|
|
255 } else {
|
|
256 return res;
|
|
257 }
|
|
258 }
|
|
259
|
|
260 void unload_() {
|
|
261 if (0 == FreeLibrary(handle) && SharedLib.throwExceptions) {
|
|
262 throw new SharedLibException("Couldn't unload shared library '" ~ this.path_ ~ "' : " ~ SysError.lastMsg);
|
|
263 }
|
|
264 }
|
|
265 }
|
|
266 else version (Posix) {
|
|
267 void* handle;
|
|
268
|
|
269 void load_(LoadMode mode) {
|
|
270 int mode_;
|
|
271 if (mode & LoadMode.Now) mode_ |= RTLD_NOW;
|
|
272 if (mode & LoadMode.Lazy) mode_ |= RTLD_LAZY;
|
|
273 if (mode & LoadMode.Global) mode_ |= RTLD_GLOBAL;
|
|
274 if (mode & LoadMode.Local) mode_ |= RTLD_LOCAL;
|
|
275
|
|
276 handle = dlopen((this.path_ ~ \0).ptr, mode_);
|
|
277 if (handle is null && SharedLib.throwExceptions) {
|
|
278 throw new SharedLibException("Couldn't load shared library: " ~ fromUtf8z(dlerror()));
|
|
279 }
|
|
280 }
|
|
281
|
|
282 void* getSymbol_(char* name) {
|
|
283 auto res = dlsym(handle, name);
|
|
284 if (res is null && SharedLib.throwExceptions) {
|
|
285 throw new SharedLibException("Couldn't load symbol: " ~ fromUtf8z(dlerror()));
|
|
286 } else {
|
|
287 return res;
|
|
288 }
|
|
289 }
|
|
290
|
|
291 void unload_() {
|
|
292 if (0 != dlclose(handle) && SharedLib.throwExceptions) {
|
|
293 throw new SharedLibException("Couldn't unload shared library: " ~ fromUtf8z(dlerror()));
|
|
294 }
|
|
295 }
|
|
296 }
|
|
297 else {
|
|
298 static assert (false, "No support for this platform");
|
|
299 }
|
|
300
|
|
301
|
|
302 char[] path_;
|
|
303 int refCnt = 0;
|
|
304
|
|
305
|
|
306 bool loaded() {
|
|
307 return handle !is null;
|
|
308 }
|
|
309
|
|
310
|
|
311 this(char[] path) {
|
|
312 this.path_ = path.dup;
|
|
313 }
|
|
314 }
|
|
315
|
|
316
|
|
317 private static {
|
|
318 SharedLib[char[]] loadedLibs;
|
|
319 Object mutex;
|
|
320 }
|
|
321
|
|
322
|
|
323 static this() {
|
|
324 mutex = new Object;
|
|
325 }
|
|
326
|
|
327
|
|
328 /// Set this to false if you don't want SharedLib to throw exceptions in symbol(), load() and unload()
|
|
329 static bool throwExceptions = true;
|
|
330 }
|
|
331
|
|
332
|
|
333 class SharedLibException : TracedException {
|
|
334 this (char[] msg) {
|
|
335 super(msg);
|
|
336 }
|
|
337 }
|