1 module sharedlib; 2 3 import core.stdc.errno; 4 import core.stdc.string; 5 import core.atomic; 6 import core.sync.mutex; 7 import core.sys.posix.dlfcn; 8 import core.sys.posix.unistd; 9 import std.exception; 10 import std.string : toStringz; 11 12 13 version (Posix): 14 15 16 /** 17 See `man 3 dlopen`. 18 */ 19 enum RTLD_LOCAL = 0; 20 enum RTLD_LAZY = 1; 21 enum RTLD_NOW = 2; 22 enum RTLD_GLOBAL = 3; 23 24 25 /// To guard all uses libdl our own mutex. 26 /// This code is stallen from std.concurrency. 27 private @property shared(Mutex) initOnceLock() 28 { 29 static shared Mutex lock; 30 if (auto mtx = atomicLoad!(MemoryOrder.acq)(lock)) 31 return mtx; 32 auto mtx = new shared Mutex; 33 if (cas(&lock, cast(shared) null, mtx)) 34 return mtx; 35 return atomicLoad!(MemoryOrder.acq)(lock); 36 } 37 38 39 /// Whole error in libdl is shared at global state. 40 /// So need to guard all uses of libdl with mutex. 41 private void dlerrorWithFunc(bool delegate() del) 42 { 43 auto m = initOnceLock(); 44 m.lock(); 45 scope (exit) m.unlock(); 46 47 if (!del()) 48 { 49 const errorMsg = dlerror(); 50 if (errorMsg !is null) 51 errnoEnforce(false, cast(string) errorMsg[0 .. strlen(errorMsg)]); 52 errnoEnforce(false, "failed unknown reason."); 53 } 54 } 55 56 /** 57 This is a wrapper of UNIX-specified dynamic loading. 58 See `man 3 dlopen`. 59 */ 60 struct SharedLibrary 61 { 62 void* handle; 63 64 /// 65 this(string filename, int flags) 66 { 67 dlerrorWithFunc(() nothrow { 68 this.handle = dlopen(filename.toStringz, flags); 69 return this.handle !is null; 70 }); 71 } 72 73 ~this() nothrow @nogc 74 { 75 import core.internal.abort : abort; 76 /// destructor cannot raise exception, so only call dlclose(3). 77 if (this.handle !is null) 78 { 79 const ret = dlclose(this.handle); 80 if (ret != 0) 81 abort("Error: dlclose(3) failed."); 82 } 83 } 84 85 /// 86 void close() 87 { 88 dlerrorWithFunc(() nothrow { 89 return dlclose(this.handle) == 0; 90 }); 91 } 92 93 /// 94 auto get(string symbolName) 95 { 96 void* symbol; 97 dlerrorWithFunc(() nothrow { 98 symbol = dlsym(this.handle, symbolName.toStringz); 99 return symbol !is null; 100 }); 101 return symbol; 102 } 103 104 // utility for getting the adress of library loaded. 105 void* getLoadedAddr() nothrow 106 { 107 return cast(void*) *cast(const size_t*) this.handle; 108 } 109 } 110 111 112 @system unittest 113 { 114 version (linux) 115 { 116 // Using libm.so gots invalid ELF Header. 117 string libm = "libm.so.6"; 118 } 119 else version (OSX) 120 { 121 string libm = "libm.dylib"; 122 } 123 124 { 125 auto lib = new SharedLibrary(libm, RTLD_LAZY); 126 auto ceil = cast(double function(double)) lib.get("ceil"); 127 assert(ceil(0.45) == 1); 128 } 129 130 { 131 auto lib = new SharedLibrary(libm, RTLD_LAZY); 132 const addr = lib.getLoadedAddr(); 133 assert(addr !is null); 134 } 135 }