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 unsuportedSymbol()
33 	{
34 		throw new Error("The funcion your 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 = &unsuportedSymbol;
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 		string exePath = buildNormalizedPath(thisExePath().dirName(), "gtk", "bin");
314 		string dllPath = buildNormalizedPath(exePath, "libgtk-3-0.dll");
315 		
316 		if ( exists(dllPath) )
317 		{
318 			if ( checkArchitecture(dllPath) )
319 				return exePath;
320 		}
321 
322 		foreach (path; splitter(environment.get("PATH"), ';'))
323 		{
324 			dllPath = buildNormalizedPath(path, "libgtk-3-0.dll");
325 
326 			if ( !exists(dllPath) )
327 				continue;
328 
329 			if ( checkArchitecture(dllPath) )
330 				return path;
331 		}
332 
333 		return null;
334 	}
335 
336 	private bool checkArchitecture(string dllPath)
337 	{
338 		import std.stdio;
339 
340 		File dll = File(dllPath);
341 
342 		dll.seek(0x3c);
343 		int offset = dll.rawRead(new int[1])[0];
344 
345 		dll.seek(offset);
346 		uint peHead = dll.rawRead(new uint[1])[0];
347 
348 		//Not a PE Header.
349 		if( peHead != 0x00004550)
350 			return false;
351 
352 		MachineType type = dll.rawRead(new MachineType[1])[0];
353 
354 		version(Win32)
355 		{
356 			if ( type == MachineType.I386 )
357 				return true;
358 		}
359 		else version(Win64)
360 		{
361 			if ( type == MachineType.AMD64 )
362 				return true;
363 		}
364 
365 		return false;		
366 	}
367 }
368 else
369 {
370 	extern(C)
371 	{
372 		void* dlopen(char*, int);
373 		char* dlerror();
374 		void* dlsym(void*,char*);
375 		int   dlclose(void*);
376 	}
377 
378 	enum RTLD
379 	{
380 		LAZY     = 0x00001,  // Lazy function call binding
381 		NOW      = 0x00002,  // Immediate function call binding
382 		NOLOAD   = 0x00004,  // No object load
383 		DEEPBIND = 0x00008,  //
384 		GLOBAL   = 0x00100   // Make object available to whole program
385 	}
386 
387 	private void* pLoadLibrary(string libraryName, RTLD flag = RTLD.NOW)
388 	{
389 		void* handle = dlopen(cast(char*)toStringz(libraryName), flag);
390 
391 		// clear the error buffer
392 		dlerror();
393 
394 		return handle;
395 	}
396 
397 	private void* pGetSymbol(void* libraryHandle, string symbol)
398 	{
399 		void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol));
400 
401 		// clear the error buffer
402 		dlerror();
403 
404 		return symbolHandle;
405 	}
406 
407 	private int pUnloadLibrary(void* libraryHandle)
408 	{
409 		return dlclose(libraryHandle);
410 	}
411 }