aboutsummaryrefslogtreecommitdiff
path: root/dlfcn.c
diff options
context:
space:
mode:
Diffstat (limited to 'dlfcn.c')
-rw-r--r--dlfcn.c245
1 files changed, 129 insertions, 116 deletions
diff --git a/dlfcn.c b/dlfcn.c
index b356558..8d3c793 100644
--- a/dlfcn.c
+++ b/dlfcn.c
@@ -2,6 +2,7 @@
2 * dlfcn-win32 2 * dlfcn-win32
3 * Copyright (c) 2007 Ramiro Polla 3 * Copyright (c) 2007 Ramiro Polla
4 * Copyright (c) 2015 Tiancheng "Timothy" Gu 4 * Copyright (c) 2015 Tiancheng "Timothy" Gu
5 * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
5 * 6 *
6 * dlfcn-win32 is free software; you can redistribute it and/or 7 * dlfcn-win32 is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
@@ -29,6 +30,17 @@
29#include <stdio.h> 30#include <stdio.h>
30#include <stdlib.h> 31#include <stdlib.h>
31 32
33#ifdef _MSC_VER
34/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
35#include <intrin.h>
36#pragma intrinsic(_ReturnAddress)
37#else
38/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
39#ifndef _ReturnAddress
40#define _ReturnAddress() (__builtin_extract_return_addr(__builtin_return_address(0)))
41#endif
42#endif
43
32#ifdef SHARED 44#ifdef SHARED
33#define DLFCN_WIN32_EXPORTS 45#define DLFCN_WIN32_EXPORTS
34#endif 46#endif
@@ -52,57 +64,48 @@
52 * any kind of thread safety. 64 * any kind of thread safety.
53 */ 65 */
54 66
55typedef struct global_object { 67typedef struct local_object {
56 HMODULE hModule; 68 HMODULE hModule;
57 struct global_object *previous; 69 struct local_object *previous;
58 struct global_object *next; 70 struct local_object *next;
59} global_object; 71} local_object;
60 72
61static global_object first_object; 73static local_object first_object;
62static global_object first_automatic_object;
63static int auto_ref_count = 0;
64 74
65/* These functions implement a double linked list for the global objects. */ 75/* These functions implement a double linked list for the local objects. */
66static global_object *global_search( global_object *start, HMODULE hModule ) 76static local_object *local_search( HMODULE hModule )
67{ 77{
68 global_object *pobject; 78 local_object *pobject;
69 79
70 if( hModule == NULL ) 80 if( hModule == NULL )
71 return NULL; 81 return NULL;
72 82
73 for( pobject = start; pobject; pobject = pobject->next ) 83 for( pobject = &first_object; pobject; pobject = pobject->next )
74 if( pobject->hModule == hModule ) 84 if( pobject->hModule == hModule )
75 return pobject; 85 return pobject;
76 86
77 return NULL; 87 return NULL;
78} 88}
79 89
80static void global_add( global_object *start, HMODULE hModule ) 90static void local_add( HMODULE hModule )
81{ 91{
82 global_object *pobject; 92 local_object *pobject;
83 global_object *nobject; 93 local_object *nobject;
84 94
85 if( hModule == NULL ) 95 if( hModule == NULL )
86 return; 96 return;
87 97
88 pobject = global_search( start, hModule ); 98 pobject = local_search( hModule );
89 99
90 /* Do not add object again if it's already on the list */ 100 /* Do not add object again if it's already on the list */
91 if( pobject ) 101 if( pobject )
92 return; 102 return;
93 103
94 if( start == &first_automatic_object ) 104 for( pobject = &first_object; pobject->next; pobject = pobject->next );
95 {
96 pobject = global_search( &first_object, hModule );
97 if( pobject )
98 return;
99 }
100
101 for( pobject = start; pobject->next; pobject = pobject->next );
102 105
103 nobject = (global_object*) malloc( sizeof( global_object ) ); 106 nobject = (local_object*) malloc( sizeof( local_object ) );
104 107
105 /* Should this be enough to fail global_add, and therefore also fail 108 /* Should this be enough to fail local_add, and therefore also fail
106 * dlopen? 109 * dlopen?
107 */ 110 */
108 if( !nobject ) 111 if( !nobject )
@@ -114,14 +117,14 @@ static void global_add( global_object *start, HMODULE hModule )
114 nobject->hModule = hModule; 117 nobject->hModule = hModule;
115} 118}
116 119
117static void global_rem( global_object *start, HMODULE hModule ) 120static void local_rem( HMODULE hModule )
118{ 121{
119 global_object *pobject; 122 local_object *pobject;
120 123
121 if( hModule == NULL ) 124 if( hModule == NULL )
122 return; 125 return;
123 126
124 pobject = global_search( start, hModule ); 127 pobject = local_search( hModule );
125 128
126 if( !pobject ) 129 if( !pobject )
127 return; 130 return;
@@ -224,10 +227,6 @@ void *dlopen( const char *file, int mode )
224 227
225 if( file == 0 ) 228 if( file == 0 )
226 { 229 {
227 HMODULE hAddtnlMods[1024]; // Already loaded modules
228 HANDLE hCurrentProc = GetCurrentProcess( );
229 DWORD cbNeeded;
230
231 /* POSIX says that if the value of file is 0, a handle on a global 230 /* POSIX says that if the value of file is 0, a handle on a global
232 * symbol object must be provided. That object must be able to access 231 * symbol object must be provided. That object must be able to access
233 * all symbols from the original program file, and any objects loaded 232 * all symbols from the original program file, and any objects loaded
@@ -242,27 +241,13 @@ void *dlopen( const char *file, int mode )
242 241
243 if( !hModule ) 242 if( !hModule )
244 save_err_ptr_str( file ); 243 save_err_ptr_str( file );
245
246
247 /* GetModuleHandle( NULL ) only returns the current program file. So
248 * if we want to get ALL loaded module including those in linked DLLs,
249 * we have to use EnumProcessModules( ).
250 */
251 if( EnumProcessModules( hCurrentProc, hAddtnlMods,
252 sizeof( hAddtnlMods ), &cbNeeded ) != 0 )
253 {
254 DWORD i;
255 for( i = 0; i < cbNeeded / sizeof( HMODULE ); i++ )
256 {
257 global_add( &first_automatic_object, hAddtnlMods[i] );
258 }
259 }
260 auto_ref_count++;
261 } 244 }
262 else 245 else
263 { 246 {
247 HANDLE hCurrentProc;
248 DWORD dwProcModsBefore, dwProcModsAfter;
264 CHAR lpFileName[MAX_PATH]; 249 CHAR lpFileName[MAX_PATH];
265 int i; 250 size_t i;
266 251
267 /* MSDN says backslashes *must* be used instead of forward slashes. */ 252 /* MSDN says backslashes *must* be used instead of forward slashes. */
268 for( i = 0 ; i < sizeof(lpFileName) - 1 ; i ++ ) 253 for( i = 0 ; i < sizeof(lpFileName) - 1 ; i ++ )
@@ -276,6 +261,11 @@ void *dlopen( const char *file, int mode )
276 } 261 }
277 lpFileName[i] = '\0'; 262 lpFileName[i] = '\0';
278 263
264 hCurrentProc = GetCurrentProcess( );
265
266 if( EnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 )
267 dwProcModsBefore = 0;
268
279 /* POSIX says the search path is implementation-defined. 269 /* POSIX says the search path is implementation-defined.
280 * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely 270 * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
281 * to UNIX's search paths (start with system folders instead of current 271 * to UNIX's search paths (start with system folders instead of current
@@ -284,17 +274,24 @@ void *dlopen( const char *file, int mode )
284 hModule = LoadLibraryEx(lpFileName, NULL, 274 hModule = LoadLibraryEx(lpFileName, NULL,
285 LOAD_WITH_ALTERED_SEARCH_PATH ); 275 LOAD_WITH_ALTERED_SEARCH_PATH );
286 276
287 /* If the object was loaded with RTLD_GLOBAL, add it to list of global 277 if( EnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
288 * objects, so that its symbols may be retrieved even if the handle for 278 dwProcModsAfter = 0;
279
280 /* If the object was loaded with RTLD_LOCAL, add it to list of local
281 * objects, so that its symbols cannot be retrieved even if the handle for
289 * the original program file is passed. POSIX says that if the same 282 * the original program file is passed. POSIX says that if the same
290 * file is specified in multiple invocations, and any of them are 283 * file is specified in multiple invocations, and any of them are
291 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the 284 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
292 * symbols will remain global. 285 * symbols will remain global. If number of loaded modules was not
286 * changed after calling LoadLibraryEx(), it means that library was
287 * already loaded.
293 */ 288 */
294 if( !hModule ) 289 if( !hModule )
295 save_err_str( lpFileName ); 290 save_err_str( lpFileName );
296 else if( (mode & RTLD_GLOBAL) ) 291 else if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter )
297 global_add( &first_object, hModule ); 292 local_add( hModule );
293 else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
294 local_rem( hModule );
298 } 295 }
299 296
300 /* Return to previous state of the error-mode bit flags. */ 297 /* Return to previous state of the error-mode bit flags. */
@@ -303,21 +300,6 @@ void *dlopen( const char *file, int mode )
303 return (void *) hModule; 300 return (void *) hModule;
304} 301}
305 302
306static void free_auto( )
307{
308 global_object *pobject = first_automatic_object.next;
309 if( pobject )
310 {
311 global_object *next;
312 for ( ; pobject; pobject = next )
313 {
314 next = pobject->next;
315 free( pobject );
316 }
317 first_automatic_object.next = NULL;
318 }
319}
320
321int dlclose( void *handle ) 303int dlclose( void *handle )
322{ 304{
323 HMODULE hModule = (HMODULE) handle; 305 HMODULE hModule = (HMODULE) handle;
@@ -327,22 +309,11 @@ int dlclose( void *handle )
327 309
328 ret = FreeLibrary( hModule ); 310 ret = FreeLibrary( hModule );
329 311
330 /* If the object was loaded with RTLD_GLOBAL, remove it from list of global 312 /* If the object was loaded with RTLD_LOCAL, remove it from list of local
331 * objects. 313 * objects.
332 */ 314 */
333 if( ret ) 315 if( ret )
334 { 316 local_rem( hModule );
335 HMODULE cur = GetModuleHandle( NULL );
336 global_rem( &first_object, hModule );
337 if( hModule == cur )
338 {
339 auto_ref_count--;
340 if( auto_ref_count < 0 )
341 auto_ref_count = 0;
342 if( !auto_ref_count )
343 free_auto( );
344 }
345 }
346 else 317 else
347 save_err_ptr_str( handle ); 318 save_err_ptr_str( handle );
348 319
@@ -352,10 +323,13 @@ int dlclose( void *handle )
352 return (int) ret; 323 return (int) ret;
353} 324}
354 325
326__declspec(noinline) /* Needed for _ReturnAddress() */
355void *dlsym( void *handle, const char *name ) 327void *dlsym( void *handle, const char *name )
356{ 328{
357 FARPROC symbol; 329 FARPROC symbol;
330 HMODULE hCaller;
358 HMODULE hModule; 331 HMODULE hModule;
332 HANDLE hCurrentProc;
359 333
360#ifdef UNICODE 334#ifdef UNICODE
361 wchar_t namew[MAX_PATH]; 335 wchar_t namew[MAX_PATH];
@@ -363,39 +337,88 @@ void *dlsym( void *handle, const char *name )
363#endif 337#endif
364 338
365 current_error = NULL; 339 current_error = NULL;
340 symbol = NULL;
341 hCaller = NULL;
342 hModule = GetModuleHandle( NULL );
343 hCurrentProc = GetCurrentProcess( );
366 344
367 symbol = GetProcAddress( (HMODULE) handle, name ); 345 if( handle == RTLD_DEFAULT )
346 {
347 /* The symbol lookup happens in the normal global scope; that is,
348 * a search for a symbol using this handle would find the same
349 * definition as a direct use of this symbol in the program code.
350 * So use same lookup procedure as when filename is NULL.
351 */
352 handle = hModule;
353 }
354 else if( handle == RTLD_NEXT )
355 {
356 /* Specifies the next object after this one that defines name.
357 * This one refers to the object containing the invocation of dlsym().
358 * The next object is the one found upon the application of a load
359 * order symbol resolution algorithm. To get caller function of dlsym()
360 * use _ReturnAddress() intrinsic. To get HMODULE of caller function
361 * use undocumented hack from https://stackoverflow.com/a/2396380
362 * The HMODULE of a DLL is the same value as the module's base address.
363 */
364 MEMORY_BASIC_INFORMATION info;
365 size_t sLen;
366 sLen = VirtualQueryEx( hCurrentProc, _ReturnAddress(), &info, sizeof( info ) );
367 if( sLen != sizeof( info ) )
368 goto end;
369 hCaller = (HMODULE) info.AllocationBase;
370 if(!hCaller)
371 goto end;
372 }
368 373
369 if( symbol != NULL ) 374 if( handle != RTLD_NEXT )
370 goto end; 375 {
376 symbol = GetProcAddress( (HMODULE) handle, name );
377
378 if( symbol != NULL )
379 goto end;
380 }
371 381
372 /* If the handle for the original program file is passed, also search 382 /* If the handle for the original program file is passed, also search
373 * in all globally loaded objects. 383 * in all globally loaded objects.
374 */ 384 */
375 385
376 hModule = GetModuleHandle( NULL ); 386 if( hModule == handle || handle == RTLD_NEXT )
377
378 if( hModule == handle )
379 { 387 {
380 global_object *pobject; 388 HMODULE *modules;
381 389 DWORD cbNeeded;
382 for( pobject = &first_object; pobject; pobject = pobject->next ) 390 DWORD dwSize;
383 { 391 size_t i;
384 if( pobject->hModule )
385 {
386 symbol = GetProcAddress( pobject->hModule, name );
387 if( symbol != NULL )
388 goto end;
389 }
390 }
391 392
392 for( pobject = &first_automatic_object; pobject; pobject = pobject->next ) 393 /* GetModuleHandle( NULL ) only returns the current program file. So
394 * if we want to get ALL loaded module including those in linked DLLs,
395 * we have to use EnumProcessModules( ).
396 */
397 if( EnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 )
393 { 398 {
394 if( pobject->hModule ) 399 modules = malloc( dwSize );
400 if( modules )
395 { 401 {
396 symbol = GetProcAddress( pobject->hModule, name ); 402 if( EnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded )
397 if( symbol != NULL ) 403 {
398 goto end; 404 for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
405 {
406 if( handle == RTLD_NEXT && hCaller )
407 {
408 /* Next modules can be used for RTLD_NEXT */
409 if( hCaller == modules[i] )
410 hCaller = NULL;
411 continue;
412 }
413 if( local_search( modules[i] ) )
414 continue;
415 symbol = GetProcAddress( modules[i], name );
416 if( symbol != NULL )
417 goto end;
418 }
419
420 }
421 free( modules );
399 } 422 }
400 } 423 }
401 } 424 }
@@ -472,18 +495,8 @@ char *dlerror( void )
472BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) 495BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
473{ 496{
474 (void) hinstDLL; 497 (void) hinstDLL;
475 /* 498 (void) fdwReason;
476 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx 499 (void) lpvReserved;
477 *
478 * When handling DLL_PROCESS_DETACH, a DLL should free resources such as heap
479 * memory only if the DLL is being unloaded dynamically (the lpReserved
480 * parameter is NULL).
481 */
482 if( fdwReason == DLL_PROCESS_DETACH && !lpvReserved )
483 {
484 auto_ref_count = 0;
485 free_auto( );
486 }
487 return TRUE; 500 return TRUE;
488} 501}
489#endif 502#endif