1 module libloading.library; 2 3 private: 4 5 version (Windows) 6 { 7 import core.runtime; 8 } 9 else version (Posix) 10 { 11 import core.sys.posix.dlfcn; 12 } 13 else static assert(false, "Unsupported platform."); 14 15 import std.exception : enforce, errnoEnforce; 16 import std.traits : isFunctionPointer, isPointer; 17 18 version (Posix) 19 { 20 version (LDC) 21 { 22 import core.sys.posix.pthread; 23 import core.sys.posix.stdlib : abort; 24 import ldc.attributes; 25 extern (C) 26 { 27 __gshared @weak pthread_mutex_t libloading_dlerror_mutex = PTHREAD_MUTEX_INITIALIZER; 28 29 @weak void libloading_dlerror_mutex_lock() @nogc nothrow 30 { 31 if (pthread_mutex_lock(&libloading_dlerror_mutex) != 0) 32 abort(); 33 } 34 35 @weak void libloading_dlerror_mutex_unlock() @nogc nothrow 36 { 37 if (pthread_mutex_unlock(&libloading_dlerror_mutex) != 0) 38 abort(); 39 } 40 } 41 } 42 else 43 { 44 extern (C) 45 { 46 void libloading_dlerror_mutex_lock() @nogc nothrow; 47 void libloading_dlerror_mutex_unlock() @nogc nothrow; 48 } 49 } 50 } 51 52 /** 53 * Whole error handling scheme in libdl is done via setting and querying some 54 * global state. 55 */ 56 version(Posix) 57 bool withDlerror(bool delegate() @nogc nothrow del, string* message) 58 /+ @nogc +/ nothrow 59 { 60 /** 61 * We will guard all uses of libdl library with our own mutex for thread-safe. 62 */ 63 import core.stdc.string : strlen; 64 65 libloading_dlerror_mutex_lock(); 66 scope (exit) libloading_dlerror_mutex_unlock(); 67 immutable result = del(); 68 if (!result) 69 { 70 auto error = dlerror(); 71 if (error is null) 72 return false; 73 // copy the error string above, when we call dlerror again to let libdl 74 // know it may free its copy of the string. 75 *message = error[0 .. strlen(error)].idup; 76 } 77 return result; 78 } 79 80 public: 81 82 /// Library. 83 struct Library 84 { 85 const(void)* handle; 86 alias handle this; 87 } 88 89 /// Find and load a library. 90 Library loadLibrary(const(char)* filename = null, int flags = RTLD_NOW) 91 { 92 Library library; 93 94 version (Windows) 95 { 96 library = Runtime.loadLibrary(filename); 97 } 98 else version (Posix) 99 { 100 string errorMessage; 101 bool result = withDlerror(delegate() @nogc nothrow { 102 const result = dlopen(filename, flags); 103 if (result is null) 104 return false; 105 library = result; 106 return true; 107 }, &errorMessage); 108 109 if (!result) 110 { 111 if (errorMessage is null) 112 enforce(false, "Unknwon reason"); 113 errnoEnforce(false, errorMessage); 114 } 115 } 116 117 return library; 118 } 119 120 /// Ditto. 121 Library loadLibrary(string filename, int flags = RTLD_NOW) 122 { 123 import std.string : toStringz; 124 return loadLibrary(filename.toStringz, flags); 125 } 126 127 /// Dispose a loaded library. 128 void unload(ref Library library) nothrow 129 { 130 version (Windows) 131 { 132 Runtime.unloadLibrary(library); 133 } 134 version (Posix) 135 { 136 string errorMessage; 137 withDlerror(delegate() @nogc nothrow { 138 return dlclose(cast(void*) library) == 0; 139 }, &errorMessage); 140 } 141 } 142 143 /// Symbol from a library. 144 struct Symbol(T) 145 { 146 T pointer; 147 alias pointer this; 148 149 version(linux) 150 string toString() 151 { 152 import core.sys.linux.dlfcn : Dl_info, dladdr; 153 import std.format : format; 154 import std.string : fromStringz; 155 156 Dl_info info = void; 157 if (dladdr(this.pointer, &info) != 0) 158 { 159 if (info.dli_sname is null) 160 return format!"Unknown symbol from %s"(info.dli_fname.fromStringz); 161 else 162 return format!"Symbol %s from %s"( 163 info.dli_sname.fromStringz, 164 info.dli_fname.fromStringz); 165 } 166 else return "Unknown symbol"; 167 } 168 } 169 170 /// Get a pointer to function by symbol name. 171 Symbol!T getSymbol(T)(ref Library library, const(char)* symbolName) 172 if (isFunctionPointer!T || isPointer!T) 173 { 174 Symbol!T symbol; 175 176 version (Windows) 177 { 178 import core.sys.windows.windows : GetProcAddress; 179 import std.format : format; 180 181 const p = GetProcAddress(library, symbolName); 182 if (p is null) 183 enforce(false, format!"Could not load function function '%s'"(symbolName)); 184 symbol = cast(T) p; 185 } 186 else version (Posix) 187 { 188 string errorMessage; 189 bool result = withDlerror(delegate() @nogc nothrow { 190 // clear any existing error, please see `man dlsym`. 191 dlerror(); 192 const p = dlsym(cast(void*)library, symbolName); 193 if (p is null) 194 return false; 195 symbol = cast(T) p; 196 return true; 197 }, &errorMessage); 198 199 if (!result) 200 { 201 if (errorMessage is null) 202 enforce(false, "Unknwon reason"); 203 errnoEnforce(false, errorMessage); 204 } 205 } 206 207 return symbol; 208 } 209 210 /// Ditto. 211 Symbol!T getSymbol(T)(ref Library library, string symbolName) 212 if (isFunctionPointer!T || isPointer!T) 213 { 214 import std.string : toStringz; 215 return getSymbol!T(library, symbolName.toStringz); 216 }