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