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 }