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 }