1 /*
2  * This file is part of gtkD.
3  *
4  * gtkD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (at your option) any later version, with
8  * some exceptions, please read the COPYING file.
9  *
10  * gtkD is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with gtkD; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
18  */
19 
20 module gtkc.Loader;
21 
22 import std.stdio;
23 import std.string;
24 
25 import gtkc.paths;
26 
27 public struct Linker
28 {
29 	private static void*[string]    loadedLibraries;
30 	private static string[][string] loadFailures;
31 
32 	extern(C) static void unsupportedSymbol()
33 	{
34 		throw new Error("The function you are calling is not pressent in your version of GTK+.");
35 	}
36 
37 	/*
38 	 * Links the provided symbol
39 	 * Params:
40 	 *     funct     = The function we are linking
41 	 *     symbol    = The name of the symbol to link
42 	 *     libraries = One or more libraries to search for the symbol
43 	 */
44 	public static void link(T)(ref T funct, string symbol, LIBRARY[] libraries ...)
45 	{
46 		funct = cast(T)getSymbol(symbol, libraries);
47 	}
48 
49 	/*
50 	 * Links the provided symbol
51 	 * Params:
52 	 *     funct     = The function we are linking
53 	 *     symbol    = The name of the symbol to link
54 	 *     libraries = One or more libraries to search for the symbol
55 	 */
56 	public static void link(T)(ref T funct, string symbol, string[] libraries ...)
57 	{
58 		funct = cast(T)getSymbol(symbol, libraries);
59 	}
60 
61 	/*
62 	 * Gets a simbol from one of the provided libraries
63 	 * Params:
64 	 *     symbol    = The name of the symbol to link
65 	 *     libraries = One or more libraries to search for the symbol
66 	 */
67 	public static void* getSymbol(string symbol, LIBRARY[] libraries ...)
68 	{
69 		string[] libStr = new string[libraries.length];
70 
71 		foreach (i, library; libraries )
72 		{
73 			libStr[i] = importLibs[library];
74 		}
75 
76 		return getSymbol(symbol, libStr);
77 	}
78 
79 	/*
80 	 * Gets a simbol from one of the provided libraries
81 	 * Params:
82 	 *     symbol    = The name of the symbol to link
83 	 *     libraries = One or more libraries to search for the symbol
84 	 */
85 	public static void* getSymbol(string symbol, string[] libraries ...)
86 	{
87 		void* handle;
88 
89 		foreach ( library; libraries )
90 		{
91 			if( !(library in loadedLibraries) )
92 				loadLibrary(library);
93 
94 			handle = pGetSymbol(loadedLibraries[library], symbol);
95 
96 			if ( handle !is null )
97 				break;
98 		}
99 
100 		if ( handle is null )
101 		{
102 			foreach ( library; libraries )
103 				loadFailures[library] ~= symbol;
104 
105 			handle = &unsupportedSymbol;
106 		}
107 
108 		return handle;
109 	}
110 
111 	/*
112 	 * Loads a library
113 	 */
114 	public static void loadLibrary(string library)
115 	{
116 		void* handle = pLoadLibrary(library);
117 
118 		//TODO: A more general way to try more than one version.
119 		if ( handle is null && library == importLibs[LIBRARY.GSV] )
120 			handle = pLoadLibrary(importLibs[LIBRARY.GSV1]);
121 
122 		if ( handle is null )
123 			throw new Exception("Library load failed: " ~ library);
124 
125 		loadedLibraries[library] = handle;
126 	}
127 
128 	/*
129 	 * Unload a library
130 	 */
131 	public static void unloadLibrary(LIBRARY library)
132 	{
133 		unloadLibrary( importLibs[library] );
134 	}
135 
136 	/*
137 	 * Unload a library
138 	 */
139 	public static void unloadLibrary(string library)
140 	{
141 		pUnloadLibrary(loadedLibraries[library]);
142 
143 		loadedLibraries.remove(library);
144 	}
145 
146 	/**
147 	 * Checks if any symbol failed to load
148 	 * Returns: true if ALL symbols are loaded
149 	 */
150 	public static bool isPerfectLoad()
151 	{
152 		return loadFailures.keys.length == 0;
153 	}
154 
155 	/**
156 	 * Gets all libraries loaded.
157 	 * returns: An array with the loaded libraries
158 	 */
159 	public static string[] getLoadLibraries()
160 	{
161 		return loadedLibraries.keys;
162 	}
163 
164 	/**
165 	 * Print all libraries loaded.
166 	 */
167 	public static void dumpLoadLibraries()
168 	{
169 		foreach ( lib; getLoadLibraries() )
170 		{
171 			writefln("Loaded lib = %s", lib);
172 		}
173 	}
174 
175 	/**
176 	 * Checks if a library is loaded.
177 	 * Returns: true is the library was loaded sucsessfully.
178 	 */
179 	public static bool isLoaded(LIBRARY library)
180 	{
181 		return isLoaded(importLibs[library]);
182 	}
183 
184 	/**
185 	 * Checks if a library is loaded.
186 	 * Returns: true is the library was loaded sucsessfully.
187 	 */
188 	public static bool isLoaded(string library)
189 	{
190 		if ( library in loadedLibraries )
191 			return true;
192 		else
193 			return false;
194 	}
195 
196 	/**
197 	 * Gets all the failed loads for a specific library.
198 	 * returns: An array of the names hat failed to load for a specific library
199 	 *          or null if none was found
200 	 */
201 	public static string[] getLoadFailures(LIBRARY library)
202 	{
203 		return getLoadFailures(importLibs[library]);
204 	}
205 
206 	/**
207 	 * Gets all the failed loads for a specific library.
208 	 * returns: An array of the names hat failed to load for a specific library
209 	 *          or null if none was found
210 	 */
211 	public static string[] getLoadFailures(string library)
212 	{
213 		if ( library in loadFailures )
214 			return loadFailures[library];
215 		else
216 			return null;
217 	}
218 
219 	/**
220 	 * Print all symbols that failed to load
221 	 */
222 	public static void dumpFailedLoads()
223 	{
224 		foreach ( library; loadedLibraries.keys )
225 		{
226 			foreach ( symbol; getLoadFailures(library) )
227 			{
228 				writefln("failed (%s) %s", library, symbol);
229 			}
230 		}
231 	}
232 
233 	static ~this()
234 	{
235 		foreach ( library; loadedLibraries.keys )
236 			unloadLibrary(library);
237 	}
238 }
239 
240 // Platform specific implementation below.
241 
242 version(Windows)
243 {
244 	extern(Windows)
245 	{
246 		void* LoadLibraryA(char*);
247 		void* GetProcAddress(void*, char*);
248 		void FreeLibrary(void*);
249 
250 		int SetDllDirectoryA(const(char)* path);
251 
252 		enum MachineType : ushort
253 		{
254 			UNKNOWN = 0x0,
255 			AM33 = 0x1d3,
256 			AMD64 = 0x8664,
257 			ARM = 0x1c0,
258 			EBC = 0xebc,
259 			I386 = 0x14c,
260 			IA64 = 0x200,
261 			M32R = 0x9041,
262 			MIPS16 = 0x266,
263 			MIPSFPU = 0x366,
264 			MIPSFPU16 = 0x466,
265 			POWERPC = 0x1f0,
266 			POWERPCFP = 0x1f1,
267 			R4000 = 0x166,
268 			SH3 = 0x1a2,
269 			SH3DSP = 0x1a3,
270 			SH4 = 0x1a6,
271 			SH5 = 0x1a8,
272 			THUMB = 0x1c2,
273 			WCEMIPSV2 = 0x169,
274 		}
275 	}
276 
277 	private void* pLoadLibrary(string libraryName)
278 	{
279 		setDllPath();
280 
281 		return LoadLibraryA(cast(char*)toStringz(libraryName));
282 	}
283 
284 	private void* pGetSymbol(void* handle, string symbol)
285 	{
286 		return GetProcAddress(handle, cast(char*)toStringz(symbol));
287 	}
288 
289 	private alias FreeLibrary pUnloadLibrary;
290 
291 	private void setDllPath()
292 	{
293 		static bool isSet;
294 
295 		if ( isSet )
296 			return;
297 
298 		string gtkPath = getGtkPath();
299 
300 		if ( gtkPath.length > 0 )
301 			SetDllDirectoryA((gtkPath~'\0').ptr);
302 
303 		isSet = true;
304 	}
305 
306 	private string getGtkPath()
307 	{
308 		import std.algorithm;
309 		import std.path;
310 		import std.process;
311 		import std.file;
312 
313 		foreach (path; splitter(environment.get("PATH"), ';'))
314 		{
315 			string dllPath = buildNormalizedPath(path, "libgtk-3-0.dll");
316 
317 			if ( !exists(dllPath) )
318 				continue;
319 
320 			if ( checkArchitecture(dllPath) )
321 				return path;
322 		}
323 
324 		return null;
325 	}
326 
327 	private bool checkArchitecture(string dllPath)
328 	{
329 		import std.stdio;
330 
331 		File dll = File(dllPath);
332 
333 		dll.seek(0x3c);
334 		int offset = dll.rawRead(new int[1])[0];
335 
336 		dll.seek(offset);
337 		uint peHead = dll.rawRead(new uint[1])[0];
338 
339 		//Not a PE Header.
340 		if( peHead != 0x00004550)
341 			return false;
342 
343 		MachineType type = dll.rawRead(new MachineType[1])[0];
344 
345 		version(Win32)
346 		{
347 			if ( type == MachineType.I386 )
348 				return true;
349 		}
350 		else version(Win64)
351 		{
352 			if ( type == MachineType.AMD64 )
353 				return true;
354 		}
355 
356 		return false;
357 	}
358 }
359 else
360 {
361 	extern(C)
362 	{
363 		void* dlopen(char*, int);
364 		char* dlerror();
365 		void* dlsym(void*,char*);
366 		int   dlclose(void*);
367 	}
368 
369 	enum RTLD
370 	{
371 		LAZY     = 0x00001,  // Lazy function call binding
372 		NOW      = 0x00002,  // Immediate function call binding
373 		NOLOAD   = 0x00004,  // No object load
374 		DEEPBIND = 0x00008,  //
375 		GLOBAL   = 0x00100   // Make object available to whole program
376 	}
377 
378 	private void* pLoadLibrary(string libraryName, RTLD flag = RTLD.NOW)
379 	{
380 		void* handle = dlopen(cast(char*)toStringz(libraryName), flag);
381 
382 		// clear the error buffer
383 		dlerror();
384 
385 		return handle;
386 	}
387 
388 	private void* pGetSymbol(void* libraryHandle, string symbol)
389 	{
390 		void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol));
391 
392 		// clear the error buffer
393 		dlerror();
394 
395 		return symbolHandle;
396 	}
397 
398 	private int pUnloadLibrary(void* libraryHandle)
399 	{
400 		return dlclose(libraryHandle);
401 	}
402 }