aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPali Rohár <pali.rohar@gmail.com>2019-01-29 22:57:04 +0100
committerPali Rohár <pali.rohar@gmail.com>2019-02-14 09:25:21 +0100
commit63d7bda42322bb0916e30bc547123a8330a4dcc2 (patch)
tree71d0ea65a3c604982b20445384392fbdbce6d136
parent29c46a54ffa31513be1a2cdcd9e8a55938d6e549 (diff)
downloaddlfcn-win32-63d7bda42322bb0916e30bc547123a8330a4dcc2.tar.gz
dlfcn-win32-63d7bda42322bb0916e30bc547123a8330a4dcc2.tar.bz2
dlfcn-win32-63d7bda42322bb0916e30bc547123a8330a4dcc2.zip
Implement support for dlsym() with RTLD_DEFAULT and RTLD_NEXT
dlsym() with RTLD_DEFAULT handle behaves in same way like with global handle returned by dlopen() with NULL file name. dlsym() with RTLD_NEXT handle search for next loaded module which provides specified symbol. "Next" means module which in EnumProcessModules() result after the module which called dlsym(). To get caller function of dlsym() use _ReturnAddress() intrinsic. To get module where is caller function use the fact that HMODULE is the same value as the module's base address. When compiling under gcc, defines _ReturnAddress() macro via gcc's builtin as it does not provide MSC's specific _ReturnAddress() intrinsic. Added tests demonstrate that both RTLD_DEFAULT and RTLD_NEXT are working as expected.
-rw-r--r--CMakeLists.txt3
-rw-r--r--Makefile7
-rw-r--r--cmake-test/CMakeLists.txt3
-rw-r--r--dlfcn.c65
-rw-r--r--dlfcn.h4
-rw-r--r--test.c60
-rw-r--r--testdll.c6
-rw-r--r--testdll2.c55
8 files changed, 193 insertions, 10 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a13cf99..07addbb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,6 +63,9 @@ if (BUILD_TESTS)
63 enable_testing() 63 enable_testing()
64 add_library(testdll SHARED testdll.c) 64 add_library(testdll SHARED testdll.c)
65 set_target_properties(testdll PROPERTIES PREFIX "") 65 set_target_properties(testdll PROPERTIES PREFIX "")
66 add_library(testdll2 SHARED testdll2.c)
67 set_target_properties(testdll2 PROPERTIES PREFIX "")
68 target_link_libraries(testdll2 dl)
66 add_library(testdll3 SHARED testdll3.c) 69 add_library(testdll3 SHARED testdll3.c)
67 set_target_properties(testdll3 PROPERTIES PREFIX "") 70 set_target_properties(testdll3 PROPERTIES PREFIX "")
68 add_executable(t_dlfcn test.c) 71 add_executable(t_dlfcn test.c)
diff --git a/Makefile b/Makefile
index a0f5cb9..efac5af 100644
--- a/Makefile
+++ b/Makefile
@@ -66,10 +66,13 @@ test.exe: test.o $(TARGETS)
66testdll.dll: testdll.c 66testdll.dll: testdll.c
67 $(CC) -shared -o $@ $^ 67 $(CC) -shared -o $@ $^
68 68
69testdll2.dll: testdll2.c $(TARGETS)
70 $(CC) -shared -o $@ $< -L. -ldl $(LIBS)
71
69testdll3.dll: testdll3.c 72testdll3.dll: testdll3.c
70 $(CC) -shared -o $@ $^ 73 $(CC) -shared -o $@ $^
71 74
72test: $(TARGETS) test.exe testdll.dll testdll3.dll 75test: $(TARGETS) test.exe testdll.dll testdll2.dll testdll3.dll
73 $(WINE) test.exe 76 $(WINE) test.exe
74 77
75clean:: 78clean::
@@ -77,7 +80,7 @@ clean::
77 dlfcn.o \ 80 dlfcn.o \
78 libdl.dll libdl.a libdl.def libdl.dll.a libdl.lib libdl.exp \ 81 libdl.dll libdl.a libdl.def libdl.dll.a libdl.lib libdl.exp \
79 tmptest.c tmptest.dll \ 82 tmptest.c tmptest.dll \
80 test.exe testdll.dll testdll3.dll 83 test.exe testdll.dll testdll2.dll testdll3.dll
81 84
82distclean: clean 85distclean: clean
83 rm -f config.mak 86 rm -f config.mak
diff --git a/cmake-test/CMakeLists.txt b/cmake-test/CMakeLists.txt
index b853920..659a79a 100644
--- a/cmake-test/CMakeLists.txt
+++ b/cmake-test/CMakeLists.txt
@@ -8,6 +8,9 @@ find_package(dlfcn-win32 REQUIRED)
8 8
9add_library(testdll SHARED ../testdll.c) 9add_library(testdll SHARED ../testdll.c)
10set_target_properties(testdll PROPERTIES PREFIX "") 10set_target_properties(testdll PROPERTIES PREFIX "")
11add_library(testdll2 SHARED ../testdll2.c)
12set_target_properties(testdll2 PROPERTIES PREFIX "")
13target_link_libraries(testdll2 dlfcn-win32::dl)
11add_library(testdll3 SHARED ../testdll3.c) 14add_library(testdll3 SHARED ../testdll3.c)
12set_target_properties(testdll3 PROPERTIES PREFIX "") 15set_target_properties(testdll3 PROPERTIES PREFIX "")
13add_executable(t_dlfcn ../test.c) 16add_executable(t_dlfcn ../test.c)
diff --git a/dlfcn.c b/dlfcn.c
index 1e706f0..8d3c793 100644
--- a/dlfcn.c
+++ b/dlfcn.c
@@ -30,6 +30,17 @@
30#include <stdio.h> 30#include <stdio.h>
31#include <stdlib.h> 31#include <stdlib.h>
32 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
33#ifdef SHARED 44#ifdef SHARED
34#define DLFCN_WIN32_EXPORTS 45#define DLFCN_WIN32_EXPORTS
35#endif 46#endif
@@ -312,9 +323,11 @@ int dlclose( void *handle )
312 return (int) ret; 323 return (int) ret;
313} 324}
314 325
326__declspec(noinline) /* Needed for _ReturnAddress() */
315void *dlsym( void *handle, const char *name ) 327void *dlsym( void *handle, const char *name )
316{ 328{
317 FARPROC symbol; 329 FARPROC symbol;
330 HMODULE hCaller;
318 HMODULE hModule; 331 HMODULE hModule;
319 HANDLE hCurrentProc; 332 HANDLE hCurrentProc;
320 333
@@ -324,20 +337,53 @@ void *dlsym( void *handle, const char *name )
324#endif 337#endif
325 338
326 current_error = NULL; 339 current_error = NULL;
340 symbol = NULL;
341 hCaller = NULL;
342 hModule = GetModuleHandle( NULL );
327 hCurrentProc = GetCurrentProcess( ); 343 hCurrentProc = GetCurrentProcess( );
328 344
329 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 }
373
374 if( handle != RTLD_NEXT )
375 {
376 symbol = GetProcAddress( (HMODULE) handle, name );
330 377
331 if( symbol != NULL ) 378 if( symbol != NULL )
332 goto end; 379 goto end;
380 }
333 381
334 /* 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
335 * in all globally loaded objects. 383 * in all globally loaded objects.
336 */ 384 */
337 385
338 hModule = GetModuleHandle( NULL ); 386 if( hModule == handle || handle == RTLD_NEXT )
339
340 if( hModule == handle )
341 { 387 {
342 HMODULE *modules; 388 HMODULE *modules;
343 DWORD cbNeeded; 389 DWORD cbNeeded;
@@ -357,6 +403,13 @@ void *dlsym( void *handle, const char *name )
357 { 403 {
358 for( i = 0; i < dwSize / sizeof( HMODULE ); i++ ) 404 for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
359 { 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 }
360 if( local_search( modules[i] ) ) 413 if( local_search( modules[i] ) )
361 continue; 414 continue;
362 symbol = GetProcAddress( modules[i], name ); 415 symbol = GetProcAddress( modules[i], name );
diff --git a/dlfcn.h b/dlfcn.h
index 711e431..c0d7777 100644
--- a/dlfcn.h
+++ b/dlfcn.h
@@ -44,8 +44,8 @@ extern "C" {
44 * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. 44 * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.
45 */ 45 */
46 46
47#define RTLD_DEFAULT 0 47#define RTLD_DEFAULT ((void *)0)
48#define RTLD_NEXT 0 48#define RTLD_NEXT ((void *)-1)
49 49
50DLFCN_EXPORT void *dlopen ( const char *file, int mode ); 50DLFCN_EXPORT void *dlopen ( const char *file, int mode );
51DLFCN_EXPORT int dlclose(void *handle); 51DLFCN_EXPORT int dlclose(void *handle);
diff --git a/test.c b/test.c
index 10c655e..814aca1 100644
--- a/test.c
+++ b/test.c
@@ -73,10 +73,13 @@
73int main() 73int main()
74{ 74{
75 void *global; 75 void *global;
76 void *library2;
76 void *library; 77 void *library;
77 char *error; 78 char *error;
78 int (*function)( void ); 79 int (*function)( void );
80 int (*function2_from_library2)( void );
79 size_t (*fwrite_local) ( const void *, size_t, size_t, FILE * ); 81 size_t (*fwrite_local) ( const void *, size_t, size_t, FILE * );
82 size_t (*fputs_default) ( const char *, FILE * );
80 int (*nonexistentfunction)( void ); 83 int (*nonexistentfunction)( void );
81 int ret; 84 int ret;
82 HMODULE library3; 85 HMODULE library3;
@@ -90,6 +93,16 @@ int main()
90 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT); 93 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
91#endif 94#endif
92 95
96 library2 = dlopen( "testdll2.dll", RTLD_GLOBAL );
97 if( !library2 )
98 {
99 error = dlerror( );
100 printf( "ERROR\tCould not open library2 globally: %s\n", error ? error : "" );
101 RETURN_ERROR;
102 }
103 else
104 printf( "SUCCESS\tOpened library2 globally: %p\n", library2 );
105
93 library = dlopen( "testdll.dll", RTLD_GLOBAL ); 106 library = dlopen( "testdll.dll", RTLD_GLOBAL );
94 if( !library ) 107 if( !library )
95 { 108 {
@@ -127,6 +140,22 @@ int main()
127 fwrite_local(hello_world,sizeof(char),strlen(hello_world),stderr); 140 fwrite_local(hello_world,sizeof(char),strlen(hello_world),stderr);
128 fflush(stderr); 141 fflush(stderr);
129 142
143 fputs_default = dlsym(RTLD_DEFAULT, "fputs");
144 if (!fputs_default)
145 {
146 error = dlerror();
147 printf("ERROR\tCould not get symbol from default handle: %s\n",
148 error ? error : "");
149 CLOSE_LIB;
150 CLOSE_GLOBAL;
151 RETURN_ERROR;
152 }
153 else
154 printf("SUCCESS\tGot symbol from default handle: %p\n", fputs_default);
155 char * hello_world_fputs = "Hello world from default fputs!\n";
156 fputs_default(hello_world_fputs, stderr);
157 fflush(stderr);
158
130 function = dlsym( library, "function" ); 159 function = dlsym( library, "function" );
131 if( !function ) 160 if( !function )
132 { 161 {
@@ -142,6 +171,27 @@ int main()
142 171
143 RUNFUNC; 172 RUNFUNC;
144 173
174 function2_from_library2 = dlsym( library2, "function2" );
175 if( !function2_from_library2 )
176 {
177 error = dlerror( );
178 printf( "ERROR\tCould not get symbol from library2 handle: %s\n",
179 error ? error : "" );
180 CLOSE_LIB;
181 CLOSE_GLOBAL;
182 RETURN_ERROR;
183 }
184 else
185 printf( "SUCCESS\tGot symbol from library2 handle: %p\n", function2_from_library2 );
186
187 ret = function2_from_library2 ();
188 if( ret != 2 )
189 {
190 CLOSE_LIB;
191 CLOSE_GLOBAL;
192 RETURN_ERROR;
193 }
194
145 nonexistentfunction = dlsym( library, "nonexistentfunction" ); 195 nonexistentfunction = dlsym( library, "nonexistentfunction" );
146 if( nonexistentfunction ) 196 if( nonexistentfunction )
147 { 197 {
@@ -197,6 +247,16 @@ int main()
197 else 247 else
198 printf( "SUCCESS\tClosed library.\n" ); 248 printf( "SUCCESS\tClosed library.\n" );
199 249
250 ret = dlclose( library2 );
251 if( ret )
252 {
253 error = dlerror( );
254 printf( "ERROR\tCould not close library2: %s\n", error ? error : "" );
255 RETURN_ERROR;
256 }
257 else
258 printf( "SUCCESS\tClosed library2.\n" );
259
200 library = dlopen( "testdll.dll", RTLD_LOCAL ); 260 library = dlopen( "testdll.dll", RTLD_LOCAL );
201 if( !library ) 261 if( !library )
202 { 262 {
diff --git a/testdll.c b/testdll.c
index ff99f87..3df2234 100644
--- a/testdll.c
+++ b/testdll.c
@@ -30,6 +30,12 @@
30#define EXPORT 30#define EXPORT
31#endif 31#endif
32 32
33EXPORT int function2( void )
34{
35 printf( "Hello, world! from original library\n" );
36 return 0;
37}
38
33EXPORT int function( void ) 39EXPORT int function( void )
34{ 40{
35 printf( "Hello, world!\n" ); 41 printf( "Hello, world!\n" );
diff --git a/testdll2.c b/testdll2.c
new file mode 100644
index 0000000..910c820
--- /dev/null
+++ b/testdll2.c
@@ -0,0 +1,55 @@
1/*
2 * dlfcn-win32
3 * Copyright (c) 2007 Ramiro Polla
4 * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
5 *
6 * dlfcn-win32 is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * dlfcn-win32 is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with dlfcn-win32; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifdef _DEBUG
22#define _CRTDBG_MAP_ALLOC
23#include <stdlib.h>
24#include <crtdbg.h>
25#endif
26#include <stdio.h>
27
28#include "dlfcn.h"
29
30#if defined(_WIN32)
31#define EXPORT __declspec(dllexport)
32#else
33#define EXPORT
34#endif
35
36EXPORT int function2( void )
37{
38 char *error;
39 int (*function2_orig)(void);
40 printf( "Hello, world! from wrapper library\n" );
41 function2_orig = dlsym(RTLD_NEXT, "function2");
42 if (!function2_orig)
43 {
44 error = dlerror( );
45 printf( "ERROR\tCould not get symbol from RTLD_NEXT handle: %s\n",
46 error ? error : "" );
47 return 1;
48 }
49 if (function2_orig() != 0)
50 {
51 printf( "ERROR\tOriginal function from RTLD_NEXT handle did not return correct value\n" );
52 return 1;
53 }
54 return 2;
55}