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.sharedlib;
29 
30 import std..string;
31 
32 import derelict.util.exception,
33        derelict.util.system;
34 
35 alias void* SharedLibHandle;
36 
37 static if(Derelict_OS_Posix) {
38     import core.sys.posix.dlfcn;
39 
40     enum LDFlags
41     {
42         rtldLocal = RTLD_LOCAL,
43         rtldLazy = RTLD_LAZY,
44         rtldNow = RTLD_NOW,
45         rtldGlobal = RTLD_GLOBAL,
46     }
47 
48     void derelictLDFlags(LDFlags flags) { ldFlags = flags; }
49 
50     private {
51         LDFlags ldFlags = LDFlags.rtldNow;
52 
53         SharedLibHandle LoadSharedLib(string libName)
54         {
55             return dlopen(libName.toStringz(), ldFlags);
56         }
57 
58         void UnloadSharedLib(SharedLibHandle hlib)
59         {
60             dlclose(hlib);
61         }
62 
63         void* GetSymbol(SharedLibHandle hlib, string symbolName)
64         {
65             return dlsym(hlib, symbolName.toStringz());
66         }
67 
68         string GetErrorStr()
69         {
70             import std.conv : to;
71 
72             auto err = dlerror();
73             if(err is null)
74                 return "Uknown Error";
75 
76             return to!string(err);
77         }
78     }
79 } else static if(Derelict_OS_Windows) {
80     import core.sys.windows.windows;
81 
82     private {
83         SharedLibHandle LoadSharedLib(string libName)
84         {
85             return LoadLibraryA(libName.toStringz());
86         }
87 
88         void UnloadSharedLib(SharedLibHandle hlib)
89         {
90             FreeLibrary(hlib);
91         }
92 
93         void* GetSymbol(SharedLibHandle hlib, string symbolName)
94         {
95             return GetProcAddress(hlib, symbolName.toStringz());
96         }
97 
98         string GetErrorStr()
99         {
100             import std.windows.syserror;
101             return sysErrorString(GetLastError());
102         }
103     }
104 } else {
105     static assert(0, "Derelict does not support this platform.");
106 }
107 
108 /++
109  Low-level wrapper of the even lower-level operating-specific shared library
110  loading interface.
111 
112  While this interface can be used directly in applications, it is recommended
113  to use the interface specified by derelict.util.loader.SharedLibLoader
114  to implement bindings. SharedLib is designed to be the base of a higher-level
115  loader, but can be used in a program if only a handful of functions need to
116  be loaded from a given shared library.
117 +/
118 struct SharedLib
119 {
120     /++
121      Finds and loads a shared library, using names to find the library
122      on the file system.
123 
124      If multiple library names are specified in names, a SharedLibLoadException
125      will only be thrown if all of the libraries fail to load. It will be the head
126      of an exceptin chain containing one instance of the exception for each library
127      that failed.
128 
129 
130      Params:
131         names = An array containing one or more shared library names,
132                 with one name per index.
133      Throws:    SharedLibLoadException if the shared library or one of its
134                 dependencies cannot be found on the file system.
135                 SymbolLoadException if an expected symbol is missing from the
136                 library.
137     +/
138     void load(string[] names)
139     {
140         if(isLoaded)
141             return;
142 
143         string[] failedLibs;
144         string[] reasons;
145 
146         foreach(n; names) {
147             _hlib = LoadSharedLib(n);
148             if(_hlib !is null) {
149                 _name = n;
150                 break;
151             }
152 
153             failedLibs ~= n;
154             reasons ~= GetErrorStr();
155         }
156 
157         if(!isLoaded) {
158             SharedLibLoadException.throwNew(failedLibs, reasons);
159         }
160     }
161 
162     /++
163      Loads the symbol specified by symbolName from a shared library.
164 
165      Params:
166         symbolName =        The name of the symbol to load.
167         doThrow =   If true, a SymbolLoadException will be thrown if the symbol
168                     is missing. If false, no exception will be thrown and the
169                     ptr parameter will be set to null.
170      Throws:        SymbolLoadException if doThrow is true and a the symbol
171                     specified by funcName is missing from the shared library.
172     +/
173     void* loadSymbol(string symbolName, bool doThrow = true)
174     {
175         void* sym = GetSymbol(_hlib, symbolName);
176         if(doThrow && !sym) {
177             auto result = ShouldThrow.Yes;
178             if(_onMissingSym !is null)
179                 result = _onMissingSym(symbolName);
180             if(result == ShouldThrow.Yes)
181                 throw new SymbolLoadException(_name, symbolName);
182         }
183 
184         return sym;
185     }
186 
187     /++
188      Unloads the shared library from memory, invalidating all function pointers
189      which were assigned a symbol by one of the load methods.
190     +/
191     void unload()
192     {
193         if(isLoaded) {
194             UnloadSharedLib(_hlib);
195             _hlib = null;
196         }
197     }
198 
199 
200     /// Returns the name of the shared library.
201     @property @nogc nothrow
202     string name() { return _name; }
203 
204     /// Returns true if the shared library is currently loaded, false otherwise.
205     @property @nogc nothrow
206     bool isLoaded() { return (_hlib !is null); }
207 
208     /++
209      Sets the callback that will be called when an expected symbol is
210      missing from the shared library.
211 
212      Params:
213         callback =      A delegate that returns a value of type
214                         derelict.util.exception.ShouldThrow and accepts
215                         a string as the sole parameter.
216     +/
217     @property @nogc nothrow
218     void missingSymbolCallback(MissingSymbolCallbackDg callback)
219     {
220         _onMissingSym = callback;
221     }
222 
223     /++
224      Sets the callback that will be called when an expected symbol is
225      missing from the shared library.
226 
227      Params:
228         callback =      A pointer to a function that returns a value of type
229                         derelict.util.exception.ShouldThrow and accepts
230                         a string as the sole parameter.
231     +/
232     @property @nogc nothrow
233     void missingSymbolCallback(MissingSymbolCallbackFunc callback)
234     {
235         import std.functional : toDelegate;
236         _onMissingSym = toDelegate(callback);
237     }
238 
239     /++
240      Returns the currently active missing symbol callback.
241 
242      This exists primarily as a means to save the current callback before
243      setting a new one. It's useful, for example, if the new callback needs
244      to delegate to the old one.
245     +/
246     @property @nogc nothrow
247     MissingSymbolCallback missingSymbolCallback() { return _onMissingSym; }
248 
249 private:
250     string _name;
251     SharedLibHandle _hlib;
252     private MissingSymbolCallbackDg _onMissingSym;
253 }