xref: /trunk/main/sal/osl/w32/module.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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