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