1 /*
2 
3 Boost Software License - Version 1.0 - August 17th, 2003
4 
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 
27 */
28 module derelict.util.loader;
29 
30 import std.array,
31        std..string;
32 
33 import derelict.util.exception,
34        derelict.util.sharedlib,
35        derelict.util.system;
36 
37 struct SharedLibVersion
38 {
39     int major;
40     int minor;
41     int patch;
42 }
43 
44 abstract class SharedLibLoader
45 {
46     /++
47      Constructs a new instance of shared lib loader with a string of one
48      or more shared library names to use as default.
49 
50      Params:
51         libNames =      A string containing one or more comma-separated shared
52                         library names.
53     +/
54     this(string libNames) { _libNames = libNames; }
55 
56     /++
57      Binds a function pointer to a symbol in this loader's shared library.
58 
59      Params:
60         ptr =       Pointer to a function pointer that will be used as the bind
61                     point.
62         funcName =  The name of the symbol to be bound.
63         doThrow =   If true, a SymbolLoadException will be thrown if the symbol
64                     is missing. If false, no exception will be thrown and the
65                     ptr parameter will be set to null.
66      Throws:        SymbolLoadException if doThrow is true and a the symbol
67                     specified by funcName is missing from the shared library.
68     +/
69     final void bindFunc(void** ptr, string funcName, bool doThrow = true)
70     {
71         void* func = loadSymbol(funcName, doThrow);
72         *ptr = func;
73     }
74 
75     /++
76      Binds a function pointer to a stdcall symbol in this loader's shared library.
77 
78      On builds for anything other than 32-bit Windows, this simply delegates to bindFunc.
79 
80      Params:
81         ptr =       Pointer to a function pointer that will be used as the bind
82                     point.
83         funcName =  The name of the symbol to be bound.
84         doThrow =   If true, a SymbolLoadException will be thrown if the symbol
85                     is missing. If false, no exception will be thrown and the
86                     ptr parameter will be set to null.
87      Throws:        SymbolLoadException if doThrow is true and a the symbol
88                     specified by funcName is missing from the shared library.
89     +/
90     final void bindFunc_stdcall(Func)(ref Func f, string unmangledName)
91     {
92         static if(Derelict_OS_Windows && !Derelict_Arch_64) {
93             import std.format : format;
94             import std.traits : ParameterTypeTuple;
95 
96             // get type-tuple of parameters
97             ParameterTypeTuple!f params;
98 
99             size_t sizeOfParametersOnStack(A...)(A args)
100             {
101                 size_t sum = 0;
102                 foreach (arg; args) {
103                     sum += arg.sizeof;
104 
105                     // align on 32-bit stack
106                     if (sum % 4 != 0)
107                         sum += 4 - (sum % 4);
108                 }
109                 return sum;
110             }
111             unmangledName = format("_%s@%s", unmangledName, sizeOfParametersOnStack(params));
112         }
113         bindFunc(cast(void**)&f, unmangledName);
114     }
115 
116     /++
117      Finds and loads a shared library, using this loader's default shared library
118      names and default supported shared library version.
119 
120      If multiple library names are specified as default, a SharedLibLoadException
121      will only be thrown if all of the libraries fail to load. It will be the head
122      of an exceptin chain containing one instance of the exception for each library
123      that failed.
124 
125      Examples:  If this loader supports versions 2.0 and 2.1 of a shared libary,
126                 this method will attempt to load 2.1 and will fail if only 2.0
127                 is present on the system.
128 
129      Throws:    SharedLibLoadException if the shared library or one of its
130                 dependencies cannot be found on the file system.
131                 SymbolLoadException if an expected symbol is missing from the
132                 library.
133     +/
134     final void load() { load(_libNames); }
135 
136     /++
137      Finds and loads any version of a shared library greater than or equal to
138      the required mimimum version, using this loader's default shared library
139      names.
140 
141      If multiple library names are specified as default, a SharedLibLoadException
142      will only be thrown if all of the libraries fail to load. It will be the head
143      of an exceptin chain containing one instance of the exception for each library
144      that failed.
145 
146      Examples:  If this loader supports versions 2.0 and 2.1 of a shared library,
147                 passing a SharedLibVersion with the major field set to 2 and the
148                 minor field set to 0 will cause the loader to load version 2.0
149                 if version 2.1 is not available on the system.
150 
151      Params:
152         minRequiredVersion = the minimum version of the library that is acceptable.
153                              Subclasses are free to ignore this.
154 
155      Throws:    SharedLibLoadException if the shared library or one of its
156                 dependencies cannot be found on the file system.
157                 SymbolLoadException if an expected symbol is missing from the
158                 library.
159     +/
160     final void load(SharedLibVersion minRequiredVersion)
161     {
162         configureMinimumVersion(minRequiredVersion);
163         load();
164     }
165 
166     /++
167      Finds and loads a shared library, using libNames to find the library
168      on the file system.
169 
170      If multiple library names are specified in libNames, a SharedLibLoadException
171      will only be thrown if all of the libraries fail to load. It will be the head
172      of an exceptin chain containing one instance of the exception for each library
173      that failed.
174 
175      Examples:  If this loader supports versions 2.0 and 2.1 of a shared libary,
176                 this method will attempt to load 2.1 and will fail if only 2.0
177                 is present on the system.
178 
179      Params:
180         libNames =      A string containing one or more comma-separated shared
181                         library names.
182      Throws:    SharedLibLoadException if the shared library or one of its
183                 dependencies cannot be found on the file system.
184                 SymbolLoadException if an expected symbol is missing from the
185                 library.
186     +/
187     final void load(string libNames)
188     {
189         if(libNames == null)
190             libNames = _libNames;
191 
192         auto lnames = libNames.split(",");
193         foreach(ref string l; lnames)
194             l = l.strip();
195 
196         load(lnames);
197     }
198 
199     /++
200      Finds and loads any version of a shared library greater than or equal to
201      the required mimimum version, using libNames to find the library
202      on the file system.
203 
204      If multiple library names are specified as default, a SharedLibLoadException
205      will only be thrown if all of the libraries fail to load. It will be the head
206      of an exceptin chain containing one instance of the exception for each library
207      that failed.
208 
209      Examples:  If this loader supports versions 2.0 and 2.1 of a shared library,
210                 passing a SharedLibVersion with the major field set to 2 and the
211                 minor field set to 0 will cause the loader to load version 2.0
212                 if version 2.1 is not available on the system.
213 
214      Params:
215         libNames =      A string containing one or more comma-separated shared
216                         library names.
217         minRequiredVersion = The minimum version of the library that is acceptable.
218                              Subclasses are free to ignore this.
219 
220      Throws:    SharedLibLoadException if the shared library or one of its
221                 dependencies cannot be found on the file system.
222                 SymbolLoadException if an expected symbol is missing from the
223                 library.
224     +/
225     final void load(string libNames, SharedLibVersion minRequiredVersion)
226     {
227         configureMinimumVersion(minRequiredVersion);
228         load(libNames);
229     }
230 
231     /++
232      Finds and loads a shared library, using libNames to find the library
233      on the file system.
234 
235      If multiple library names are specified in libNames, a SharedLibLoadException
236      will only be thrown if all of the libraries fail to load. It will be the head
237      of an exception chain containing one instance of the exception for each library
238      that failed.
239 
240 
241      Params:
242         libNames =      An array containing one or more shared library names,
243                         with one name per index.
244 
245      Throws:    SharedLibLoadException if the shared library or one of its
246                 dependencies cannot be found on the file system.
247                 SymbolLoadException if an expected symbol is missing from the
248                 library.
249     +/
250     final void load(string[] libNames)
251     {
252         _lib.load(libNames);
253         loadSymbols();
254     }
255 
256     /++
257      Finds and loads any version of a shared library greater than or equal to
258      the required mimimum version, , using libNames to find the library
259      on the file system.
260 
261      If multiple library names are specified in libNames, a SharedLibLoadException
262      will only be thrown if all of the libraries fail to load. It will be the head
263      of an exception chain containing one instance of the exception for each library
264      that failed.
265 
266      Examples:  If this loader supports versions 2.0 and 2.1 of a shared library,
267                 passing a SharedLibVersion with the major field set to 2 and the
268                 minor field set to 0 will cause the loader to load version 2.0
269                 if version 2.1 is not available on the system.
270 
271 
272      Params:
273         libNames =      An array containing one or more shared library names,
274                         with one name per index.
275         minRequiredVersion = The minimum version of the library that is acceptable.
276                              Subclasses are free to ignore this.
277 
278      Throws:    SharedLibLoadException if the shared library or one of its
279                 dependencies cannot be found on the file system.
280                 SymbolLoadException if an expected symbol is missing from the
281                 library.
282     +/
283     final void load(string[] libNames, SharedLibVersion minRequiredVersion)
284     {
285         configureMinimumVersion(minRequiredVersion);
286         load(libNames);
287     }
288 
289     /++
290      Unloads the shared library from memory, invalidating all function pointers
291      which were assigned a symbol by one of the load methods.
292     +/
293     final void unload() { _lib.unload(); }
294 
295 
296     /// Returns: true if the shared library is loaded, false otherwise.
297     @property @nogc nothrow
298     final bool isLoaded() { return _lib.isLoaded; }
299 
300     /++
301      Sets the callback that will be called when an expected symbol is
302      missing from the shared library.
303 
304      Params:
305         callback =      A delegate that returns a value of type
306                         derelict.util.exception.ShouldThrow and accepts
307                         a string as the sole parameter.
308     +/
309     @property @nogc nothrow
310     final void missingSymbolCallback(MissingSymbolCallbackDg callback)
311     {
312         _lib.missingSymbolCallback = callback;
313     }
314 
315     /++
316      Sets the callback that will be called when an expected symbol is
317      missing from the shared library.
318 
319      Params:
320         callback =      A pointer to a function that returns a value of type
321                         derelict.util.exception.ShouldThrow and accepts
322                         a string as the sole parameter.
323     +/
324     @property @nogc nothrow
325     final void missingSymbolCallback(MissingSymbolCallbackFunc callback)
326     {
327         _lib.missingSymbolCallback = callback;
328     }
329 
330     /++
331      Returns the currently active missing symbol callback.
332 
333      This exists primarily as a means to save the current callback before
334      setting a new one. It's useful, for example, if the new callback needs
335      to delegate to the old one.
336     +/
337     @property @nogc nothrow
338     final MissingSymbolCallback missingSymbolCallback()
339     {
340         return _lib.missingSymbolCallback;
341     }
342 
343 protected:
344     /++
345      Must be implemented by subclasses to load all of the symbols from a
346      shared library.
347 
348      This method is called by the load methods.
349     +/
350     abstract void loadSymbols();
351 
352     /++
353      Allows a subclass to install an exception handler for specific versions
354      of a library before loadSymbols is called.
355 
356      This method is optional. If the subclass does not implement it, calls to
357      any of the overloads of the load method that take a SharedLibVersion will
358      cause a compile time assert to fire.
359     +/
360     void configureMinimumVersion(SharedLibVersion minVersion)
361     {
362         assert(0, "SharedLibVersion is not supported by this loader.");
363     }
364 
365     /++
366      Subclasses can use this as an alternative to bindFunc, but must bind
367      the returned symbol manually.
368 
369      bindFunc calls this internally, so it can be overloaded to get behavior
370      different from the default.
371 
372      Params:
373         name =      The name of the symbol to load.doThrow =   If true, a SymbolLoadException will be thrown if the symbol
374                     is missing. If false, no exception will be thrown and the
375                     ptr parameter will be set to null.
376      Throws:        SymbolLoadException if doThrow is true and a the symbol
377                     specified by funcName is missing from the shared library.
378      Returns:       The symbol matching the name parameter.
379     +/
380     void* loadSymbol(string name, bool doThrow = true)
381     {
382         return _lib.loadSymbol(name, doThrow);
383     }
384 
385     /// Returns a reference to the shared library wrapped by this loader.
386     @property @nogc nothrow
387     final ref SharedLib lib(){ return _lib; }
388 
389 
390 private:
391     string _libNames;
392     SharedLib _lib;
393 }