xref: /trunk/main/sal/osl/w32/module.cxx (revision 87d2adbc)
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, rtl::Allocator<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_getModuleHandle */
105 /*****************************************************************************/
106 
107 sal_Bool SAL_CALL
108 osl_getModuleHandle(rtl_uString *pModuleName, oslModule *pResult)
109 {
110     HINSTANCE hInstance = GetModuleHandleW(reinterpret_cast<LPCWSTR>(pModuleName->buffer));
111     if( hInstance )
112     {
113         *pResult = (oslModule) hInstance;
114         return sal_True;
115     }
116 
117     return sal_False;
118 }
119 
120 /*****************************************************************************/
121 /* osl_unloadModule */
122 /*****************************************************************************/
123 void SAL_CALL osl_unloadModule(oslModule Module)
124 {
125 	FreeLibrary((HINSTANCE)Module);
126 }
127 
128 /*****************************************************************************/
129 /* osl_getSymbol */
130 /*****************************************************************************/
131 void* SAL_CALL osl_getSymbol(oslModule Module, rtl_uString *strSymbolName)
132 {
133     /* casting from a function pointer to a data pointer is invalid
134        be in this case unavoidable because the API has to stay
135        compitable we need to keep this function which returns a
136        void* by definition */
137 #ifdef _MSC_VER
138 #pragma warning(push)
139 #pragma warning(disable:4054)
140 #endif
141     return (void*)(osl_getFunctionSymbol(Module, strSymbolName));
142 #ifdef _MSC_VER
143 #pragma warning(pop)
144 #endif
145 }
146 
147 /*****************************************************************************/
148 /* osl_getFunctionSymbol */
149 /*****************************************************************************/
150 oslGenericFunction SAL_CALL osl_getFunctionSymbol( oslModule Module, rtl_uString *strSymbolName )
151 {
152     rtl_String *symbolName = NULL;
153 	oslGenericFunction address;
154 
155 	OSL_ASSERT(Module);
156 	OSL_ASSERT(strSymbolName);
157 
158 	rtl_uString2String(
159 		&symbolName,
160 		strSymbolName->buffer,
161 		strSymbolName->length,
162 		RTL_TEXTENCODING_UTF8,
163 		OUSTRING_TO_OSTRING_CVTFLAGS
164 	);
165 
166 	address=osl_getAsciiFunctionSymbol(Module, rtl_string_getStr(symbolName));
167 	rtl_string_release(symbolName);
168 
169     return address;
170 }
171 
172 /*****************************************************************************/
173 /* osl_getAsciiFunctionSymbol */
174 /*****************************************************************************/
175 oslGenericFunction SAL_CALL
176 osl_getAsciiFunctionSymbol( oslModule Module, const sal_Char *pSymbol )
177 {
178 	oslGenericFunction fncAddr = NULL;
179 
180     if( pSymbol )
181         fncAddr=(oslGenericFunction)GetProcAddress((HINSTANCE) Module, pSymbol);
182 
183     return fncAddr;
184 }
185 
186 
187 
188 /*****************************************************************************/
189 /* osl_addressGetModuleURL */
190 /*****************************************************************************/
191 
192 /*****************************************************************************/
193 /* Implementation for Windows 95, 98 and Me */
194 /*****************************************************************************/
195 
196 /* Undefine because there is no explicit "A" definition */
197 
198 #ifdef MODULEENTRY32
199 #undef MODULEENTRY32
200 #endif
201 
202 #ifdef LPMODULEENTRY32
203 #undef LPMODULEENTRY32
204 #endif
205 
206 typedef HANDLE (WINAPI *CreateToolhelp32Snapshot_PROC)( DWORD dwFlags, DWORD th32ProcessID );
207 typedef BOOL (WINAPI *Module32First_PROC)( HANDLE	hSnapshot, LPMODULEENTRY32 lpme32 );
208 typedef BOOL (WINAPI *Module32Next_PROC)( HANDLE	hSnapshot, LPMODULEENTRY32 lpme32 );
209 
210 static sal_Bool SAL_CALL _osl_addressGetModuleURL_Windows( void *pv, rtl_uString **pustrURL )
211 {
212 	sal_Bool	bSuccess		= sal_False;	/* Assume failure */
213 	HMODULE		hModKernel32	= GetModuleHandleA( "KERNEL32.DLL" );
214 
215 	if ( hModKernel32 )
216 	{
217 		CreateToolhelp32Snapshot_PROC	lpfnCreateToolhelp32Snapshot = (CreateToolhelp32Snapshot_PROC)GetProcAddress( hModKernel32, "CreateToolhelp32Snapshot" );
218 		Module32First_PROC				lpfnModule32First = (Module32First_PROC)GetProcAddress( hModKernel32, "Module32First" );
219 		Module32Next_PROC				lpfnModule32Next = (Module32Next_PROC)GetProcAddress( hModKernel32, "Module32Next" );
220 
221 		if ( lpfnCreateToolhelp32Snapshot && lpfnModule32First && lpfnModule32Next )
222 		{
223 			HANDLE	hModuleSnap	= NULL;
224 			DWORD	dwProcessId = GetCurrentProcessId();
225 
226 			// Take a snapshot of all modules in the specified process.
227 
228 			hModuleSnap = lpfnCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId );
229 
230 			if ( INVALID_HANDLE_VALUE != hModuleSnap )
231 			{
232 				MODULEENTRY32	me32	= {0};
233 
234 				// Fill the size of the structure before using it.
235 
236 				me32.dwSize = sizeof(MODULEENTRY32);
237 
238 				// Walk the module list of the process, and find the module of
239 				// interest. Then copy the information to the buffer pointed
240 				// to by lpMe32 so that it can be returned to the caller.
241 
242 				if ( lpfnModule32First(hModuleSnap, &me32) )
243 				{
244 					do
245 					{
246 						if ( (BYTE *)pv >= (BYTE *)me32.hModule && (BYTE *)pv < (BYTE *)me32.hModule + me32.modBaseSize )
247 						{
248 							rtl_uString	*ustrSysPath = NULL;
249 
250 							rtl_string2UString( &ustrSysPath, me32.szExePath, strlen(me32.szExePath), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
251                             OSL_ASSERT(ustrSysPath != NULL);
252 							osl_getFileURLFromSystemPath( ustrSysPath, pustrURL );
253 							rtl_uString_release( ustrSysPath );
254 
255 							bSuccess = sal_True;
256 						}
257 
258 					} while ( !bSuccess && lpfnModule32Next( hModuleSnap, &me32 ) );
259 				}
260 
261 
262 				// Do not forget to clean up the snapshot object.
263 
264 				CloseHandle (hModuleSnap);
265 			}
266 
267 		}
268 	}
269 
270 	return	bSuccess;
271 }
272 
273 /***************************************************************************************/
274 /* Implementation for Windows NT, 2K and XP (2K and XP could use the above method too) */
275 /***************************************************************************************/
276 
277 #ifdef _MSC_VER
278 #pragma warning(push,1) /* disable warnings within system headers */
279 #endif
280 #include <imagehlp.h>
281 #ifdef _MSC_VER
282 #pragma warning(pop)
283 #endif
284 
285 typedef BOOL (WINAPI *SymInitialize_PROC)(
286     HANDLE   hProcess,
287     LPSTR    UserSearchPath,
288     BOOL     fInvadeProcess
289     );
290 
291 typedef BOOL (WINAPI *SymCleanup_PROC)(
292     HANDLE hProcess
293 	);
294 
295 typedef BOOL (WINAPI *SymGetModuleInfo_PROC)(
296     HANDLE              hProcess,
297     DWORD               dwAddr,
298     PIMAGEHLP_MODULE  ModuleInfo
299     );
300 
301 /* 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
302 	it's O.K on NT 4 ???!!!
303 	BTW: We are using ANSI function because not all version of IMAGEHLP.DLL contain Unicode support
304 */
305 
306 static sal_Bool SAL_CALL _osl_addressGetModuleURL_NT4( void *pv, rtl_uString **pustrURL )
307 {
308 	sal_Bool	bSuccess	= sal_False;	/* Assume failure */
309 
310 	/*	IMAGEHELP.DLL has a bug that it recursivly scans subdirectories of
311 		the root when calling SymInitialize(), so we preferr DBGHELP.DLL
312 		which exports the same symbols and is shipped with OOo */
313 
314 	HMODULE		hModImageHelp = LoadLibrary( "DBGHELP.DLL" );
315 
316 	if ( !hModImageHelp )
317 		hModImageHelp = LoadLibrary( "IMAGEHLP.DLL" );
318 
319 	if ( hModImageHelp )
320 	{
321 		SymGetModuleInfo_PROC	lpfnSymGetModuleInfo;
322 		SymInitialize_PROC		lpfnSymInitialize;
323 		SymCleanup_PROC			lpfnSymCleanup;
324 
325 
326 		lpfnSymInitialize = (SymInitialize_PROC)GetProcAddress( hModImageHelp, "SymInitialize" );
327 		lpfnSymCleanup = (SymCleanup_PROC)GetProcAddress( hModImageHelp, "SymCleanup" );
328 		lpfnSymGetModuleInfo = (SymGetModuleInfo_PROC)GetProcAddress( hModImageHelp, "SymGetModuleInfo" );
329 
330 
331 		if ( lpfnSymInitialize && lpfnSymCleanup && lpfnSymGetModuleInfo )
332 		{
333 			IMAGEHLP_MODULE	ModuleInfo;
334 			::osl::LongPathBuffer< sal_Char > aModuleFileName( MAX_LONG_PATH );
335 			LPSTR	lpSearchPath = NULL;
336 
337 			if ( GetModuleFileNameA( NULL, aModuleFileName, aModuleFileName.getBufSizeInSymbols() ) )
338 			{
339 				char *pLastBkSlash = strrchr( aModuleFileName, '\\' );
340 
341 				if (
342 					pLastBkSlash &&
343 					pLastBkSlash > (sal_Char*)aModuleFileName
344 					&& *(pLastBkSlash - 1) != ':'
345 					&& *(pLastBkSlash - 1) != '\\'
346 					)
347 				{
348 					*pLastBkSlash = 0;
349 					lpSearchPath = aModuleFileName;
350 				}
351 			}
352 
353 			lpfnSymInitialize( GetCurrentProcess(), lpSearchPath, TRUE );
354 
355 			ZeroMemory( &ModuleInfo, sizeof(ModuleInfo) );
356 			ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
357 
358 			bSuccess = (sal_Bool)(!!lpfnSymGetModuleInfo( GetCurrentProcess(), (DWORD)pv, &ModuleInfo ));
359 
360 			if ( bSuccess )
361 			{
362 				/*	#99182 On localized (non-english) NT4 and XP (!!!) for some libraries the LoadedImageName member of ModuleInfo isn't filled. Because
363 					other members ModuleName and ImageName do not contain the full path we can cast the Member
364 					BaseOfImage to a HMODULE (on NT it's the same) and use GetModuleFileName to retrieve the full
365 					path of the loaded image */
366 
367 				if ( ModuleInfo.LoadedImageName[0] || GetModuleFileNameA( (HMODULE)ModuleInfo.BaseOfImage, ModuleInfo.LoadedImageName, sizeof(ModuleInfo.LoadedImageName) ) )
368 				{
369 					rtl_uString	*ustrSysPath = NULL;
370 
371 					rtl_string2UString( &ustrSysPath, ModuleInfo.LoadedImageName, strlen(ModuleInfo.LoadedImageName), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
372                     OSL_ASSERT(ustrSysPath != NULL);
373 					osl_getFileURLFromSystemPath( ustrSysPath, pustrURL );
374 					rtl_uString_release( ustrSysPath );
375 				}
376 				else
377 					bSuccess = sal_False;
378 			}
379 
380 			lpfnSymCleanup( GetCurrentProcess() );
381 		}
382 
383 		FreeLibrary( hModImageHelp );
384 	}
385 
386 	return bSuccess;
387 }
388 
389 
390 typedef struct _MODULEINFO {
391     LPVOID lpBaseOfDll;
392     DWORD SizeOfImage;
393     LPVOID EntryPoint;
394 } MODULEINFO, *LPMODULEINFO;
395 
396 typedef BOOL (WINAPI *EnumProcessModules_PROC)(
397   HANDLE hProcess,      // handle to the process
398   HMODULE * lphModule,  // array to receive the module handles
399   DWORD cb,             // size of the array
400   LPDWORD lpcbNeeded    // receives the number of bytes returned
401 );
402 
403 typedef BOOL (WINAPI *GetModuleInformation_PROC)(
404   HANDLE hProcess,         // handle to the process
405   HMODULE hModule,         // handle to the module
406   LPMODULEINFO lpmodinfo,  // structure that receives information
407   DWORD cb                 // size of the structure
408 );
409 
410 #define bufsizeof(buffer) (sizeof(buffer) / sizeof((buffer)[0]))
411 
412 /* This version can fail because PSAPI.DLL is not always part of NT 4 despite MSDN Libary 6.0a say so */
413 
414 static sal_Bool SAL_CALL _osl_addressGetModuleURL_NT( void *pv, rtl_uString **pustrURL )
415 {
416 	sal_Bool	bSuccess	= sal_False;	/* Assume failure */
417 	static HMODULE		hModPsapi = NULL;
418 
419 	if ( !hModPsapi )
420 		hModPsapi = LoadLibrary( "PSAPI.DLL" );
421 
422 	if ( hModPsapi )
423 	{
424 		EnumProcessModules_PROC		lpfnEnumProcessModules		= (EnumProcessModules_PROC)GetProcAddress( hModPsapi, "EnumProcessModules" );
425 		GetModuleInformation_PROC	lpfnGetModuleInformation	= (GetModuleInformation_PROC)GetProcAddress( hModPsapi, "GetModuleInformation" );
426 
427 		if ( lpfnEnumProcessModules && lpfnGetModuleInformation )
428 		{
429 			DWORD		cbNeeded = 0;
430 			HMODULE		*lpModules = NULL;
431 			DWORD		nModules = 0;
432 			UINT		iModule = 0;
433 			MODULEINFO	modinfo;
434 
435 			lpfnEnumProcessModules( GetCurrentProcess(), NULL, 0, &cbNeeded );
436 
437 			lpModules = (HMODULE *)_alloca( cbNeeded );
438 			lpfnEnumProcessModules( GetCurrentProcess(), lpModules, cbNeeded, &cbNeeded );
439 
440 			nModules = cbNeeded / sizeof(HMODULE);
441 
442 			for ( iModule = 0; !bSuccess && iModule < nModules; iModule++ )
443 			{
444 				lpfnGetModuleInformation( GetCurrentProcess(), lpModules[iModule], &modinfo, sizeof(modinfo) );
445 
446 				if ( (BYTE *)pv >= (BYTE *)modinfo.lpBaseOfDll && (BYTE *)pv < (BYTE *)modinfo.lpBaseOfDll + modinfo.SizeOfImage )
447 				{
448                     ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
449 					rtl_uString	*ustrSysPath = NULL;
450 
451 					GetModuleFileNameW( lpModules[iModule], ::osl::mingw_reinterpret_cast<LPWSTR>(aBuffer), aBuffer.getBufSizeInSymbols() );
452 
453 					rtl_uString_newFromStr( &ustrSysPath, aBuffer );
454 					osl_getFileURLFromSystemPath( ustrSysPath, pustrURL );
455 					rtl_uString_release( ustrSysPath );
456 
457 					bSuccess = sal_True;
458 				}
459 			}
460 		}
461 
462 	}
463 
464 	return bSuccess;
465 }
466 
467 /*****************************************************************************/
468 /* Dispatcher for osl_osl_addressGetModuleURL */
469 /*****************************************************************************/
470 
471 sal_Bool SAL_CALL osl_getModuleURLFromAddress( void *pv, rtl_uString **pustrURL )
472 {
473 	/* Use ..._NT first because ..._NT4 is much slower */
474 	if ( IS_NT )
475 		return _osl_addressGetModuleURL_NT( pv, pustrURL ) || _osl_addressGetModuleURL_NT4( pv, pustrURL );
476 	else
477 		return _osl_addressGetModuleURL_Windows( pv, pustrURL );
478 }
479 
480 /*****************************************************************************/
481 /* osl_getModuleURLFromFunctionAddress */
482 /*****************************************************************************/
483 sal_Bool SAL_CALL osl_getModuleURLFromFunctionAddress( oslGenericFunction addr, rtl_uString ** ppLibraryUrl )
484 {
485     /* casting a function pointer to a data pointer (void*) is
486        not allowed according to the C/C++ standards. In this case
487        it is unavoidable because we have to stay compatible we
488        cannot remove any function. */
489 #ifdef _MSC_VER
490 #pragma warning(push)
491 #pragma warning(disable:4054)
492 #endif
493     return osl_getModuleURLFromAddress((void*)addr, ppLibraryUrl);
494 #ifdef _MSC_VER
495 #pragma warning(pop)
496 #endif
497 }
498 
499 
500