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 /**
29     Loading and unloading shared libraries.
30 */
31 module dplug.core.sharedlib;
32 
33 import dplug.core.nogc;
34 import dplug.core.vec;
35 
36 //version = debugSharedLibs;
37 
38 version(debugSharedLibs)
39 {
40     import core.stdc.stdio;
41 }
42 
43 /// Shared library ressource
44 struct SharedLib
45 {
46 nothrow:
47 @nogc:
48 
49     void load(string name)
50     {
51         version(debugSharedLibs)
52         {
53             auto lib = CString(name);
54             printf("loading dynlib '%s'\n", lib.storage);
55         }
56 
57         if(isLoaded)
58             return;
59         _name = name;
60         _hlib = LoadSharedLib(name);
61         if(_hlib is null)
62             assert(false, "Couldn't open the shared library.");
63     }
64 
65     bool hasSymbol(string symbolName)
66     {
67         assert(isLoaded());
68         void* sym = GetSymbol(_hlib, symbolName);
69         return sym != null;
70     }
71 
72     void* loadSymbol(string symbolName)
73     {
74         assert(isLoaded());
75 
76         version(debugSharedLibs)
77         {
78             auto sb = CString(symbolName);
79             printf("  loading symbol '%s'\n", sb.storage);
80         }
81 
82         void* sym = GetSymbol(_hlib, symbolName);
83         if(!sym)
84             assert(false, "Couldn't get symbol.");
85         return sym;
86     }
87 
88     void unload()
89     {
90         if(isLoaded())
91         {
92             UnloadSharedLib(_hlib);
93             _hlib = null;
94 
95             version(debugSharedLibs)
96             {
97                 auto lib = CString(_name);
98                 printf("unloaded dynlib '%s'\n", lib.storage);
99             }
100         }
101     }
102 
103     /// Returns true if the shared library is currently loaded, false otherwise.
104     bool isLoaded()
105     {
106         return (_hlib !is null);
107     }
108 
109 private:
110     string _name;
111     SharedLibHandle _hlib;
112 }
113 
114 /// Loader. In debug mode, this fills functions pointers with null.
115 abstract class SharedLibLoader
116 {
117 nothrow:
118 @nogc:
119 
120     this(string libName)
121     {
122         _libName = libName;
123         version(debugSharedLibs)
124         {
125             _funcPointers = makeAlignedBuffer!(void**)();
126         }
127     }
128 
129     /// Binds a function pointer to a symbol in this loader's shared library.
130     final void bindFunc(void** ptr, string funcName)
131     {
132         void* func = _lib.loadSymbol(funcName);
133         version(debugSharedLibs)
134         {
135             _funcPointers.pushBack(ptr);
136         }
137         *ptr = func;
138     }
139 
140     final void load()
141     {
142         _lib.load(_libName);
143         loadSymbols();
144     }
145 
146     // Unload the library, and sets all functions pointer to null.
147     final void unload()
148     {
149         _lib.unload();
150 
151         version(debugSharedLibs)
152         {
153             // Sets all registered functions pointers to null
154             // so that they can't be reused
155             foreach(ptr; _funcPointers[])
156                 *ptr = null;
157 
158             _funcPointers.clearContents();
159         }
160     }
161 
162 protected:
163 
164     /// Implemented by subclasses to load all symbols with `bindFunc`.
165     abstract void loadSymbols();
166 
167 private:
168     string _libName;
169     SharedLib _lib;
170     version(debugSharedLibs)
171         Vec!(void**) _funcPointers;
172 }
173 
174 
175 private:
176 
177 alias void* SharedLibHandle;
178 
179 version(Posix)
180 {
181     import core.sys.posix.dlfcn;
182 
183     private {
184 
185         SharedLibHandle LoadSharedLib(string libName) nothrow @nogc
186         {
187             return dlopen(CString(libName), RTLD_NOW);
188         }
189 
190         void UnloadSharedLib(SharedLibHandle hlib) nothrow @nogc
191         {
192             dlclose(hlib);
193         }
194 
195         void* GetSymbol(SharedLibHandle hlib, string symbolName) nothrow @nogc
196         {
197             return dlsym(hlib, CString(symbolName));
198         }
199 
200         string GetErrorStr()
201         {
202             import std.conv : to;
203 
204             auto err = dlerror();
205             if(err is null)
206                 return "Unknown Error";
207 
208             return to!string(err);
209         }
210     }
211 }
212 else version(Windows)
213 {
214     import core.sys.windows.windows;
215 
216     private {
217         nothrow @nogc
218         SharedLibHandle LoadSharedLib(string libName)
219         {
220             return LoadLibraryA(CString(libName));
221         }
222 
223         nothrow @nogc
224         void UnloadSharedLib(SharedLibHandle hlib)
225         {
226             FreeLibrary(hlib);
227         }
228 
229         nothrow @nogc
230         void* GetSymbol(SharedLibHandle hlib, string symbolName)
231         {
232             return GetProcAddress(hlib, CString(symbolName));
233         }
234 
235         nothrow @nogc
236         string GetErrorStr()
237         {
238             import std.windows.syserror;
239             DWORD err = GetLastError();
240             return assumeNothrowNoGC(
241                     (DWORD err)
242                     {
243                         return sysErrorString(err);
244                     }
245                 )(err);
246         }
247     }
248 } else {
249     static assert(0, "Derelict does not support this platform.");
250 }