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