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