1 /* 2 __ 3 / _| 4 __ _ _ _ _ __ ___ _ __ __ _ | |_ ___ ___ ___ 5 / _` | | | | '__/ _ \| '__/ _` | | _/ _ \/ __/ __| 6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \ 7 \__,_|\__,_|_| \___/|_| \__,_| |_| \___/|___/___/ 8 9 Copyright © 2013-2016, Mike Parker. 10 Copyright © 2016, 渡世白玉. 11 Copyright © 2018, Michael D. Parker 12 Copyright © 2018-2019, Aurora Free Open Source Software. 13 Copyright © 2018-2019, Luís Ferreira <luis@aurorafoss.org> 14 15 This file is part of the Aurora Free Open Source Software. This 16 organization promote free and open source software that you can 17 redistribute and/or modify under the terms of the GNU Lesser General 18 Public License Version 3 as published by the Free Software Foundation or 19 (at your option) any later version approved by the Aurora Free Open Source 20 Software Organization. The license is available in the package root path 21 as 'LICENSE' file. Please review the following information to ensure the 22 GNU Lesser General Public License version 3 requirements will be met: 23 https://www.gnu.org/licenses/lgpl.html . 24 25 Alternatively, this file may be used under the terms of the GNU General 26 Public License version 3 or later as published by the Free Software 27 Foundation. Please review the following information to ensure the GNU 28 General Public License requirements will be met: 29 https://www.gnu.org/licenses/gpl-3.0.html. 30 31 NOTE: All products, services or anything associated to trademarks and 32 service marks used or referenced on this file are the property of their 33 respective companies/owners or its subsidiaries. Other names and brands 34 may be claimed as the property of others. 35 36 For more info about intellectual property visit: aurorafoss.org or 37 directly send an email to: contact (at) aurorafoss.org . 38 39 This file is an improvement of an existing code, part of DerelictUtil 40 from DerelictOrg. Check it out at derelictorg.github.io . 41 42 This file is an improvement of an existing code, developed by 渡世白玉 43 and available on github at https://github.com/huntlabs/DerelictUtil . 44 45 This file is an improvement of an existing code, part of bindbc-loader 46 from BindBC. Check it out at github.com/BindBC/bindbc-loader . 47 */ 48 49 module riverd.loader; 50 51 /** Dynamic library loader 52 * @file riverd/loader.d 53 * @author Luís Ferreira <luis@aurorafoss.org> 54 * @author Aurora Free Open Source Software 55 * @author 渡世白玉 56 * @author Michael D. Parker 57 * @date 2013-2019 58 */ 59 60 public import riverd.builder; 61 62 version(Posix) import core.sys.posix.dlfcn; 63 else version(Windows) import core.sys.windows.windows; 64 65 import std.conv : to; 66 import std.traits; 67 68 @nogc nothrow { 69 /** Load a dynamic library 70 * @param name complete library string name 71 */ 72 void* dylib_load(const(char)* name) 73 { 74 version(Posix) void* handle = dlopen(name, RTLD_NOW); 75 else version(Windows) void* handle = LoadLibraryA(name); 76 if(handle) return handle; 77 else return null; 78 } 79 80 /** Bind a library symbol 81 * This function bind a specific symbol from the dynamic library. 82 * @param handle library handler 83 * @param ptr symbol pointer 84 * @param name symbol name 85 */ 86 void dylib_bindSymbol(void* handle, void** ptr, const(char)* name) 87 { 88 assert(handle); 89 version(Posix) const void* sym = dlsym(handle, name); 90 else version(Windows) const void* sym = GetProcAddress(handle, name); 91 92 *ptr = cast(void*)sym; 93 } 94 95 /** Reports dynamic library errors 96 * @param buf char buffer 97 * @param len buffer length 98 */ 99 void dylib_sysError(char* buf, size_t len) 100 { 101 import core.stdc.string : strncpy; 102 version(Windows) { 103 char* msgBuf; 104 enum uint langID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); 105 106 FormatMessageA( 107 FORMAT_MESSAGE_ALLOCATE_BUFFER | 108 FORMAT_MESSAGE_FROM_SYSTEM | 109 FORMAT_MESSAGE_IGNORE_INSERTS, 110 null, 111 GetLastError(), 112 langID, 113 cast(char*)&msgBuf, 114 0, 115 null 116 ); 117 118 if(msgBuf) { 119 strncpy(buf, msgBuf, len); 120 buf[len - 1] = 0; 121 LocalFree(msgBuf); 122 } 123 else strncpy(buf, "Unknown Error\0", len); 124 } 125 else version(Posix) { 126 char* msg = dlerror(); 127 strncpy(buf, msg != null ? msg : "Unknown Error", len); 128 buf[len - 1] = 0; 129 } 130 } 131 } 132 version(D_BetterC) { 133 /** Unload a dynamic library 134 * @param handle dynamic library handler 135 */ 136 @nogc nothrow void dylib_unload(void* handle) 137 { 138 if(handle) { 139 version(Posix) dlclose(handle); 140 else version(Windows) FreeLibrary(handle); 141 handle = null; 142 } 143 } 144 } 145 else { 146 import std.array; 147 import std.string; 148 149 /** Dynamic Library Version struct */ 150 struct DylibVersion 151 { 152 uint major; /** major version number */ 153 uint minor; /** minor version number */ 154 uint patch; /** patch version number */ 155 } 156 157 /** Dynamic Library Loader Exception 158 * This exception is thrown when the library can't be loaded 159 */ 160 class DylibLoadException : Exception 161 { 162 163 /** Dynamic Library Loader Exception constructor 164 * @param msg exception message 165 * @param line line number in code 166 * @param file code file 167 */ 168 this(string msg, size_t line = __LINE__, string file = __FILE__) 169 { 170 super(msg, file, line, null); 171 } 172 173 /** Dynamic Library Loader Exception constructor 174 * @param names libraries name 175 * @param reasons reasons why it can't load 176 * @param line line number in code 177 * @param file code file 178 */ 179 this(string[] names, string[] reasons, size_t line = __LINE__, string file = __FILE__) 180 { 181 string msg = "Failed to load one or more shared libraries:"; 182 foreach(i, name; names) { 183 msg ~= "\n\t" ~ name ~ " - "; 184 if(i < reasons.length) 185 msg ~= reasons[i]; 186 else 187 msg ~= "Unknown"; 188 } 189 this(msg, line, file); 190 } 191 192 /** Dynamic Library Loader Exception constructor 193 * @param msg exception message 194 * @param name library name 195 * @param line line number in code 196 * @param file code file 197 */ 198 this(string msg, string name = "", size_t line = __LINE__, string file = __FILE__) 199 { 200 super(msg, file, line, null); 201 _name = name; 202 } 203 204 /** Get the library name */ 205 pure nothrow @nogc 206 @property string name() 207 { 208 return _name; 209 } 210 211 private string _name; /** library name */ 212 } 213 214 215 class DylibSymbolLoadException : Exception 216 { 217 218 this(string msg, size_t line = __LINE__, string file = __FILE__) { 219 super(msg, file, line, null); 220 } 221 222 this(string lib, string symbol, size_t line = __LINE__, string file = __FILE__) 223 { 224 _lib = lib; 225 _symbol = symbol; 226 this("Failed to load symbol " ~ symbol ~ " from shared library " ~ lib, line, file); 227 } 228 229 @property string lib() 230 { 231 return _lib; 232 } 233 234 @property string symbol() 235 { 236 return _symbol; 237 } 238 239 private: 240 string _lib; 241 string _symbol; 242 } 243 244 pure struct Dylib 245 { 246 void load(string[] names) 247 { 248 if(isLoaded) 249 return; 250 251 string[] fnames; 252 string[] freasons; 253 254 foreach(name; names) 255 { 256 import std.stdio; 257 import std.conv; 258 259 version(Posix) _handle = dlopen(name.toStringz(), RTLD_NOW); 260 else version(Windows) _handle = LoadLibraryA(name.toStringz()); 261 if(isLoaded) { 262 _name = name; 263 break; 264 } 265 266 fnames ~= name; 267 268 import std.conv : to; 269 270 version(Posix) { 271 string err = to!string(dlerror()); 272 if(err is null) 273 err = "Unknown error"; 274 } 275 else version(Windows) 276 { 277 import std.windows.syserror; 278 string err = sysErrorString(GetLastError()); 279 } 280 281 freasons ~= err; 282 } 283 if(!isLoaded) 284 throw new DylibLoadException(fnames, freasons); 285 } 286 287 void* loadSymbol(string name, bool required = true) 288 { 289 version(Posix) void* sym = dlsym(_handle, name.toStringz()); 290 else version(Windows) void* sym = GetProcAddress(_handle, name.toStringz()); 291 292 if(required && !sym) 293 { 294 if(_callback !is null) 295 required = _callback(name); 296 if(required) 297 throw new DylibSymbolLoadException(_name, name); 298 } 299 300 return sym; 301 } 302 303 void unload() 304 { 305 if(isLoaded) 306 { 307 version(Posix) dlclose(_handle); 308 else version(Windows) FreeLibrary(_handle); 309 _handle = null; 310 } 311 } 312 313 @property bool isLoaded() 314 { 315 return _handle !is null; 316 } 317 318 @property bool delegate(string) missingSymbolCallback() 319 { 320 return _callback; 321 } 322 323 @property void missingSymbolCallback(bool delegate(string) callback) 324 { 325 _callback = callback; 326 } 327 328 @property void missingSymbolCallback(bool function(string) callback) 329 { 330 import std.functional : toDelegate; 331 _callback = toDelegate(callback); 332 } 333 334 private: 335 string _name; 336 void* _handle; 337 bool delegate(string) _callback; 338 } 339 340 abstract class DylibLoader 341 { 342 this(string libs) 343 { 344 string[] libs_ = libs.split(","); 345 foreach(ref string l; libs_) 346 l = l.strip(); 347 this(libs_); 348 } 349 350 this(string[] libs) 351 { 352 _libs = libs; 353 dylib.load(_libs); 354 loadSymbols(); 355 } 356 357 final this(string[] libs, DylibVersion ver) 358 { 359 configureMinimumVersion(ver); 360 this(libs); 361 } 362 363 final this(string libs, DylibVersion ver) 364 { 365 configureMinimumVersion(ver); 366 this(libs); 367 } 368 369 protected void loadSymbols() {} 370 371 protected void configureMinimumVersion(DylibVersion minVersion) 372 { 373 assert(0, "DylibVersion is not supported by this loader."); 374 } 375 376 protected final void bindFunc(void** ptr, string name, bool required = true) 377 { 378 void* func = dylib.loadSymbol(name, required); 379 *ptr = func; 380 } 381 382 protected final void bindFunc(TFUN)(ref TFUN fun, string name, bool required = true) 383 if(isFunctionPointer!(TFUN)) 384 { 385 void* func = dylib.loadSymbol(name, required); 386 fun = cast(TFUN)func; 387 } 388 389 protected final void bindFunc_stdcall(Func)(ref Func f, string unmangledName) 390 { 391 version(Win32) { 392 import std.format : format; 393 import std.traits : ParameterTypeTuple; 394 395 // get type-tuple of parameters 396 ParameterTypeTuple!f params; 397 398 size_t sizeOfParametersOnStack(A...)(A args) 399 { 400 size_t sum = 0; 401 foreach (arg; args) { 402 sum += arg.sizeof; 403 404 // align on 32-bit stack 405 if (sum % 4 != 0) 406 sum += 4 - (sum % 4); 407 } 408 return sum; 409 } 410 unmangledName = format("_%s@%s", unmangledName, sizeOfParametersOnStack(params)); 411 } 412 bindFunc(cast(void**)&f, unmangledName); 413 } 414 415 @property final string[] libs() 416 { 417 return _libs; 418 } 419 420 Dylib dylib; 421 private string[] _libs; 422 } 423 424 pragma(inline, true) void dylib_unload(void* handle) 425 { 426 (cast(DylibLoader)handle).dylib.unload(); 427 } 428 } 429 430 pragma(inline, true) bool dylib_is_loaded(void* handle) 431 { 432 version(D_BetterC) return !(handle is null); 433 else return (cast(DylibLoader)handle).dylib.isLoaded(); 434 }