xref: /AOO41X/main/sal/osl/w32/module.cxx (revision 03c97e340010506c11d4ffaab7f577e5f7050fe6)
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