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