xref: /aoo41x/main/sal/osl/w32/module.cxx (revision cdf0e10c)
1 /*************************************************************************
2 *
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
6 *
7 * OpenOffice.org - a multi-platform office productivity suite
8 *
9 * This file is part of OpenOffice.org.
10 *
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
14 *
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org.  If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
25 *
26 ************************************************************************/
27 
28 #include "system.h"
29 #include <tlhelp32.h>
30 
31 #include "file_url.h"
32 #include "path_helper.hxx"
33 
34 #include <osl/module.h>
35 #include <osl/diagnose.h>
36 #include <osl/thread.h>
37 #include <osl/file.h>
38 #include <rtl/logfile.h>
39 #include <vector>
40 
41 /*
42 	under WIN32, we use the void* oslModule
43 	as a WIN32 HANDLE (which is also a 32-bit value)
44 */
45 
46 /*****************************************************************************/
47 /* osl_loadModule */
48 /*****************************************************************************/
49 oslModule SAL_CALL osl_loadModule(rtl_uString *strModuleName, sal_Int32 nRtldMode )
50 {
51     HINSTANCE hInstance;
52 	UINT errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
53 	rtl_uString* Module = NULL;
54 	oslModule ret = 0;
55 	oslFileError	nError;
56 
57     RTL_LOGFILE_TRACE1( "{ osl_loadModule start: %S", (LPTSTR)&strModuleName->buffer );
58 
59 	OSL_ASSERT(strModuleName);
60 
61     nRtldMode = nRtldMode; /* avoid warnings */
62 
63 	nError = osl_getSystemPathFromFileURL(strModuleName, &Module);
64 
65 	if ( osl_File_E_None != nError )
66 		rtl_uString_assign(&Module, strModuleName);
67 
68 	hInstance = LoadLibraryW(reinterpret_cast<LPCWSTR>(Module->buffer));
69 
70     if (hInstance == NULL)
71         hInstance = LoadLibraryExW(reinterpret_cast<LPCWSTR>(Module->buffer), NULL,
72                                   LOAD_WITH_ALTERED_SEARCH_PATH);
73 
74 	//In case of long path names (\\?\c:\...) try to shorten the filename.
75 	//LoadLibrary cannot handle file names which exceed 260 letters.
76     //In case the path is to long, the function will fail. However, the error
77     //code can be different. For example, it returned  ERROR_FILENAME_EXCED_RANGE
78     //on Windows XP and ERROR_INSUFFICIENT_BUFFER on Windows 7 (64bit)
79 	if (hInstance == NULL && Module->length > 260)
80 	{
81         std::vector<WCHAR, rtl::Allocator<WCHAR> > vec(Module->length + 1);
82 		DWORD len = GetShortPathNameW(reinterpret_cast<LPCWSTR>(Module->buffer),
83                                       &vec[0], Module->length + 1);
84 		if (len )
85 		{
86 			hInstance = LoadLibraryW(&vec[0]);
87 
88 			if (hInstance == NULL)
89 				hInstance = LoadLibraryExW(&vec[0], NULL,
90                                   LOAD_WITH_ALTERED_SEARCH_PATH);
91 		}
92 	}
93 
94 
95 	if (hInstance <= (HINSTANCE)HINSTANCE_ERROR)
96 		hInstance = 0;
97 
98 	ret = (oslModule) hInstance;
99 	rtl_uString_release(Module);
100 	SetErrorMode(errorMode);
101 
102     RTL_LOGFILE_TRACE1( "} osl_loadModule end: %S", (LPTSTR)&strModuleName->buffer );
103 
104 	return ret;
105 }
106 
107 /*****************************************************************************/
108 /* osl_getModuleHandle */
109 /*****************************************************************************/
110 
111 sal_Bool SAL_CALL
112 osl_getModuleHandle(rtl_uString *pModuleName, oslModule *pResult)
113 {
114     HINSTANCE hInstance = GetModuleHandleW(reinterpret_cast<LPCWSTR>(pModuleName->buffer));
115     if( hInstance )
116     {
117         *pResult = (oslModule) hInstance;
118         return sal_True;
119     }
120 
121     return sal_False;
122 }
123 
124 /*****************************************************************************/
125 /* osl_unloadModule */
126 /*****************************************************************************/
127 void SAL_CALL osl_unloadModule(oslModule Module)
128 {
129 	FreeLibrary((HINSTANCE)Module);
130 }
131 
132 /*****************************************************************************/
133 /* osl_getSymbol */
134 /*****************************************************************************/
135 void* SAL_CALL osl_getSymbol(oslModule Module, rtl_uString *strSymbolName)
136 {
137     /* casting from a function pointer to a data pointer is invalid
138        be in this case unavoidable because the API has to stay
139        compitable we need to keep this function which returns a
140        void* by definition */
141 #ifdef _MSC_VER
142 #pragma warning(push)
143 #pragma warning(disable:4054)
144 #endif
145     return (void*)(osl_getFunctionSymbol(Module, strSymbolName));
146 #ifdef _MSC_VER
147 #pragma warning(pop)
148 #endif
149 }
150 
151 /*****************************************************************************/
152 /* osl_getFunctionSymbol */
153 /*****************************************************************************/
154 oslGenericFunction SAL_CALL osl_getFunctionSymbol( oslModule Module, rtl_uString *strSymbolName )
155 {
156     rtl_String *symbolName = NULL;
157 	oslGenericFunction address;
158 
159 	OSL_ASSERT(Module);
160 	OSL_ASSERT(strSymbolName);
161 
162 	rtl_uString2String(
163 		&symbolName,
164 		strSymbolName->buffer,
165 		strSymbolName->length,
166 		RTL_TEXTENCODING_UTF8,
167 		OUSTRING_TO_OSTRING_CVTFLAGS
168 	);
169 
170 	address=osl_getAsciiFunctionSymbol(Module, rtl_string_getStr(symbolName));
171 	rtl_string_release(symbolName);
172 
173     return address;
174 }
175 
176 /*****************************************************************************/
177 /* osl_getAsciiFunctionSymbol */
178 /*****************************************************************************/
179 oslGenericFunction SAL_CALL
180 osl_getAsciiFunctionSymbol( oslModule Module, const sal_Char *pSymbol )
181 {
182 	oslGenericFunction fncAddr = NULL;
183 
184     if( pSymbol )
185         fncAddr=(oslGenericFunction)GetProcAddress((HINSTANCE) Module, pSymbol);
186 
187     return fncAddr;
188 }
189 
190 
191 
192 /*****************************************************************************/
193 /* osl_addressGetModuleURL */
194 /*****************************************************************************/
195 
196 /*****************************************************************************/
197 /* Implementation for Windows 95, 98 and Me */
198 /*****************************************************************************/
199 
200 /* Undefine because there is no explicit "A" definition */
201 
202 #ifdef MODULEENTRY32
203 #undef MODULEENTRY32
204 #endif
205 
206 #ifdef LPMODULEENTRY32
207 #undef LPMODULEENTRY32
208 #endif
209 
210 typedef HANDLE (WINAPI *CreateToolhelp32Snapshot_PROC)( DWORD dwFlags, DWORD th32ProcessID );
211 typedef BOOL (WINAPI *Module32First_PROC)( HANDLE	hSnapshot, LPMODULEENTRY32 lpme32 );
212 typedef BOOL (WINAPI *Module32Next_PROC)( HANDLE	hSnapshot, LPMODULEENTRY32 lpme32 );
213 
214 static sal_Bool SAL_CALL _osl_addressGetModuleURL_Windows( void *pv, rtl_uString **pustrURL )
215 {
216 	sal_Bool	bSuccess		= sal_False;	/* Assume failure */
217 	HMODULE		hModKernel32	= GetModuleHandleA( "KERNEL32.DLL" );
218 
219 	if ( hModKernel32 )
220 	{
221 		CreateToolhelp32Snapshot_PROC	lpfnCreateToolhelp32Snapshot = (CreateToolhelp32Snapshot_PROC)GetProcAddress( hModKernel32, "CreateToolhelp32Snapshot" );
222 		Module32First_PROC				lpfnModule32First = (Module32First_PROC)GetProcAddress( hModKernel32, "Module32First" );
223 		Module32Next_PROC				lpfnModule32Next = (Module32Next_PROC)GetProcAddress( hModKernel32, "Module32Next" );
224 
225 		if ( lpfnCreateToolhelp32Snapshot && lpfnModule32First && lpfnModule32Next )
226 		{
227 			HANDLE	hModuleSnap	= NULL;
228 			DWORD	dwProcessId = GetCurrentProcessId();
229 
230 			// Take a snapshot of all modules in the specified process.
231 
232 			hModuleSnap = lpfnCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId );
233 
234 			if ( INVALID_HANDLE_VALUE != hModuleSnap )
235 			{
236 				MODULEENTRY32	me32	= {0};
237 
238 				// Fill the size of the structure before using it.
239 
240 				me32.dwSize = sizeof(MODULEENTRY32);
241 
242 				// Walk the module list of the process, and find the module of
243 				// interest. Then copy the information to the buffer pointed
244 				// to by lpMe32 so that it can be returned to the caller.
245 
246 				if ( lpfnModule32First(hModuleSnap, &me32) )
247 				{
248 					do
249 					{
250 						if ( (BYTE *)pv >= (BYTE *)me32.hModule && (BYTE *)pv < (BYTE *)me32.hModule + me32.modBaseSize )
251 						{
252 							rtl_uString	*ustrSysPath = NULL;
253 
254 							rtl_string2UString( &ustrSysPath, me32.szExePath, strlen(me32.szExePath), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
255                             OSL_ASSERT(ustrSysPath != NULL);
256 							osl_getFileURLFromSystemPath( ustrSysPath, pustrURL );
257 							rtl_uString_release( ustrSysPath );
258 
259 							bSuccess = sal_True;
260 						}
261 
262 					} while ( !bSuccess && lpfnModule32Next( hModuleSnap, &me32 ) );
263 				}
264 
265 
266 				// Do not forget to clean up the snapshot object.
267 
268 				CloseHandle (hModuleSnap);
269 			}
270 
271 		}
272 	}
273 
274 	return	bSuccess;
275 }
276 
277 /***************************************************************************************/
278 /* Implementation for Windows NT, 2K and XP (2K and XP could use the above method too) */
279 /***************************************************************************************/
280 
281 #ifdef _MSC_VER
282 #pragma warning(push,1) /* disable warnings within system headers */
283 #endif
284 #include <imagehlp.h>
285 #ifdef _MSC_VER
286 #pragma warning(pop)
287 #endif
288 
289 typedef BOOL (WINAPI *SymInitialize_PROC)(
290     HANDLE   hProcess,
291     LPSTR    UserSearchPath,
292     BOOL     fInvadeProcess
293     );
294 
295 typedef BOOL (WINAPI *SymCleanup_PROC)(
296     HANDLE hProcess
297 	);
298 
299 typedef BOOL (WINAPI *SymGetModuleInfo_PROC)(
300     HANDLE              hProcess,
301     DWORD               dwAddr,
302     PIMAGEHLP_MODULE  ModuleInfo
303     );
304 
305 /* Seems that IMAGEHLP.DLL is always availiable on NT 4. But MSDN from Platform SDK says Win 2K is required. MSDN from VS 6.0a says
306 	it's O.K on NT 4 ???!!!
307 	BTW: We are using ANSI function because not all version of IMAGEHLP.DLL contain Unicode support
308 */
309 
310 static sal_Bool SAL_CALL _osl_addressGetModuleURL_NT4( void *pv, rtl_uString **pustrURL )
311 {
312 	sal_Bool	bSuccess	= sal_False;	/* Assume failure */
313 
314 	/*	IMAGEHELP.DLL has a bug that it recursivly scans subdirectories of
315 		the root when calling SymInitialize(), so we preferr DBGHELP.DLL
316 		which exports the same symbols and is shipped with OOo */
317 
318 	HMODULE		hModImageHelp = LoadLibrary( "DBGHELP.DLL" );
319 
320 	if ( !hModImageHelp )
321 		hModImageHelp = LoadLibrary( "IMAGEHLP.DLL" );
322 
323 	if ( hModImageHelp )
324 	{
325 		SymGetModuleInfo_PROC	lpfnSymGetModuleInfo;
326 		SymInitialize_PROC		lpfnSymInitialize;
327 		SymCleanup_PROC			lpfnSymCleanup;
328 
329 
330 		lpfnSymInitialize = (SymInitialize_PROC)GetProcAddress( hModImageHelp, "SymInitialize" );
331 		lpfnSymCleanup = (SymCleanup_PROC)GetProcAddress( hModImageHelp, "SymCleanup" );
332 		lpfnSymGetModuleInfo = (SymGetModuleInfo_PROC)GetProcAddress( hModImageHelp, "SymGetModuleInfo" );
333 
334 
335 		if ( lpfnSymInitialize && lpfnSymCleanup && lpfnSymGetModuleInfo )
336 		{
337 			IMAGEHLP_MODULE	ModuleInfo;
338 			::osl::LongPathBuffer< sal_Char > aModuleFileName( MAX_LONG_PATH );
339 			LPSTR	lpSearchPath = NULL;
340 
341 			if ( GetModuleFileNameA( NULL, aModuleFileName, aModuleFileName.getBufSizeInSymbols() ) )
342 			{
343 				char *pLastBkSlash = strrchr( aModuleFileName, '\\' );
344 
345 				if (
346 					pLastBkSlash &&
347 					pLastBkSlash > (sal_Char*)aModuleFileName
348 					&& *(pLastBkSlash - 1) != ':'
349 					&& *(pLastBkSlash - 1) != '\\'
350 					)
351 				{
352 					*pLastBkSlash = 0;
353 					lpSearchPath = aModuleFileName;
354 				}
355 			}
356 
357 			lpfnSymInitialize( GetCurrentProcess(), lpSearchPath, TRUE );
358 
359 			ZeroMemory( &ModuleInfo, sizeof(ModuleInfo) );
360 			ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
361 
362 			bSuccess = (sal_Bool)(!!lpfnSymGetModuleInfo( GetCurrentProcess(), (DWORD)pv, &ModuleInfo ));
363 
364 			if ( bSuccess )
365 			{
366 				/*	#99182 On localized (non-english) NT4 and XP (!!!) for some libraries the LoadedImageName member of ModuleInfo isn't filled. Because
367 					other members ModuleName and ImageName do not contain the full path we can cast the Member
368 					BaseOfImage to a HMODULE (on NT it's the same) and use GetModuleFileName to retrieve the full
369 					path of the loaded image */
370 
371 				if ( ModuleInfo.LoadedImageName[0] || GetModuleFileNameA( (HMODULE)ModuleInfo.BaseOfImage, ModuleInfo.LoadedImageName, sizeof(ModuleInfo.LoadedImageName) ) )
372 				{
373 					rtl_uString	*ustrSysPath = NULL;
374 
375 					rtl_string2UString( &ustrSysPath, ModuleInfo.LoadedImageName, strlen(ModuleInfo.LoadedImageName), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
376                     OSL_ASSERT(ustrSysPath != NULL);
377 					osl_getFileURLFromSystemPath( ustrSysPath, pustrURL );
378 					rtl_uString_release( ustrSysPath );
379 				}
380 				else
381 					bSuccess = sal_False;
382 			}
383 
384 			lpfnSymCleanup( GetCurrentProcess() );
385 		}
386 
387 		FreeLibrary( hModImageHelp );
388 	}
389 
390 	return bSuccess;
391 }
392 
393 
394 typedef struct _MODULEINFO {
395     LPVOID lpBaseOfDll;
396     DWORD SizeOfImage;
397     LPVOID EntryPoint;
398 } MODULEINFO, *LPMODULEINFO;
399 
400 typedef BOOL (WINAPI *EnumProcessModules_PROC)(
401   HANDLE hProcess,      // handle to the process
402   HMODULE * lphModule,  // array to receive the module handles
403   DWORD cb,             // size of the array
404   LPDWORD lpcbNeeded    // receives the number of bytes returned
405 );
406 
407 typedef BOOL (WINAPI *GetModuleInformation_PROC)(
408   HANDLE hProcess,         // handle to the process
409   HMODULE hModule,         // handle to the module
410   LPMODULEINFO lpmodinfo,  // structure that receives information
411   DWORD cb                 // size of the structure
412 );
413 
414 #define bufsizeof(buffer) (sizeof(buffer) / sizeof((buffer)[0]))
415 
416 /* This version can fail because PSAPI.DLL is not always part of NT 4 despite MSDN Libary 6.0a say so */
417 
418 static sal_Bool SAL_CALL _osl_addressGetModuleURL_NT( void *pv, rtl_uString **pustrURL )
419 {
420 	sal_Bool	bSuccess	= sal_False;	/* Assume failure */
421 	static HMODULE		hModPsapi = NULL;
422 
423 	if ( !hModPsapi )
424 		hModPsapi = LoadLibrary( "PSAPI.DLL" );
425 
426 	if ( hModPsapi )
427 	{
428 		EnumProcessModules_PROC		lpfnEnumProcessModules		= (EnumProcessModules_PROC)GetProcAddress( hModPsapi, "EnumProcessModules" );
429 		GetModuleInformation_PROC	lpfnGetModuleInformation	= (GetModuleInformation_PROC)GetProcAddress( hModPsapi, "GetModuleInformation" );
430 
431 		if ( lpfnEnumProcessModules && lpfnGetModuleInformation )
432 		{
433 			DWORD		cbNeeded = 0;
434 			HMODULE		*lpModules = NULL;
435 			DWORD		nModules = 0;
436 			UINT		iModule = 0;
437 			MODULEINFO	modinfo;
438 
439 			lpfnEnumProcessModules( GetCurrentProcess(), NULL, 0, &cbNeeded );
440 
441 			lpModules = (HMODULE *)_alloca( cbNeeded );
442 			lpfnEnumProcessModules( GetCurrentProcess(), lpModules, cbNeeded, &cbNeeded );
443 
444 			nModules = cbNeeded / sizeof(HMODULE);
445 
446 			for ( iModule = 0; !bSuccess && iModule < nModules; iModule++ )
447 			{
448 				lpfnGetModuleInformation( GetCurrentProcess(), lpModules[iModule], &modinfo, sizeof(modinfo) );
449 
450 				if ( (BYTE *)pv >= (BYTE *)modinfo.lpBaseOfDll && (BYTE *)pv < (BYTE *)modinfo.lpBaseOfDll + modinfo.SizeOfImage )
451 				{
452                     ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
453 					rtl_uString	*ustrSysPath = NULL;
454 
455 					GetModuleFileNameW( lpModules[iModule], ::osl::mingw_reinterpret_cast<LPWSTR>(aBuffer), aBuffer.getBufSizeInSymbols() );
456 
457 					rtl_uString_newFromStr( &ustrSysPath, aBuffer );
458 					osl_getFileURLFromSystemPath( ustrSysPath, pustrURL );
459 					rtl_uString_release( ustrSysPath );
460 
461 					bSuccess = sal_True;
462 				}
463 			}
464 		}
465 
466 	}
467 
468 	return bSuccess;
469 }
470 
471 /*****************************************************************************/
472 /* Dispatcher for osl_osl_addressGetModuleURL */
473 /*****************************************************************************/
474 
475 sal_Bool SAL_CALL osl_getModuleURLFromAddress( void *pv, rtl_uString **pustrURL )
476 {
477 	/* Use ..._NT first because ..._NT4 is much slower */
478 	if ( IS_NT )
479 		return _osl_addressGetModuleURL_NT( pv, pustrURL ) || _osl_addressGetModuleURL_NT4( pv, pustrURL );
480 	else
481 		return _osl_addressGetModuleURL_Windows( pv, pustrURL );
482 }
483 
484 /*****************************************************************************/
485 /* osl_getModuleURLFromFunctionAddress */
486 /*****************************************************************************/
487 sal_Bool SAL_CALL osl_getModuleURLFromFunctionAddress( oslGenericFunction addr, rtl_uString ** ppLibraryUrl )
488 {
489     /* casting a function pointer to a data pointer (void*) is
490        not allowed according to the C/C++ standards. In this case
491        it is unavoidable because we have to stay compatible we
492        cannot remove any function. */
493 #ifdef _MSC_VER
494 #pragma warning(push)
495 #pragma warning(disable:4054)
496 #endif
497     return osl_getModuleURLFromAddress((void*)addr, ppLibraryUrl);
498 #ifdef _MSC_VER
499 #pragma warning(pop)
500 #endif
501 }
502 
503 
504