aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSilvio Traversaro <silvio@traversaro.it>2021-01-25 21:44:50 +0100
committerGitHub <noreply@github.com>2021-01-25 21:44:50 +0100
commita5af061ce1d1bb3f58e74c48c0698dad59d79937 (patch)
treedd9b74eaa2e8a21ebe8a99132f18f1bd1fb1828d
parent06ffb62a31f8986d1cda864bb8c3a8967d5a65fa (diff)
parent3a016513e42c082c6b7b509bf93f63066a1b65f8 (diff)
downloaddlfcn-win32-a5af061ce1d1bb3f58e74c48c0698dad59d79937.tar.gz
dlfcn-win32-a5af061ce1d1bb3f58e74c48c0698dad59d79937.tar.bz2
dlfcn-win32-a5af061ce1d1bb3f58e74c48c0698dad59d79937.zip
Merge pull request #72 from rhabacker/master
Add function dladdr()
-rw-r--r--.travis.yml12
-rw-r--r--CMakeLists.txt4
-rw-r--r--Makefile15
-rw-r--r--src/CMakeLists.txt11
-rw-r--r--src/dlfcn.c214
-rw-r--r--src/dlfcn.h16
-rw-r--r--tests/CMakeLists.txt38
-rw-r--r--tests/test-dladdr.c184
-rwxr-xr-xtools/ci-build.sh81
9 files changed, 531 insertions, 44 deletions
diff --git a/.travis.yml b/.travis.yml
index 3584048..ba1a8f6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,18 +1,21 @@
1language: c 1language: c
2 2
3compiler: 3compiler:
4 - x86_64-linux-gnu-gcc
4 - i686-w64-mingw32-gcc 5 - i686-w64-mingw32-gcc
5 - x86_64-w64-mingw32-gcc 6 - x86_64-w64-mingw32-gcc
6 7
7addons: 8addons:
8 apt: 9 apt:
9 packages: 10 packages:
11 - gcc
10 - gcc-mingw-w64 12 - gcc-mingw-w64
11 - wine 13 - wine
12 - cmake 14 - cmake
13 15
14env: 16env:
15 - ci_buildsys=cmake 17 - ci_buildsys=cmake ci_variant=static
18 - ci_buildsys=cmake ci_variant=shared
16 - ci_buildsys=Makefile 19 - ci_buildsys=Makefile
17 20
18jobs: 21jobs:
@@ -39,5 +42,12 @@ jobs:
39 # Check that we have a real i586-mingw32msvc-gcc compiler as sometimes it is just a symlink to i686-w64-mingw32-gcc 42 # Check that we have a real i586-mingw32msvc-gcc compiler as sometimes it is just a symlink to i686-w64-mingw32-gcc
40 before_script: "i586-mingw32msvc-gcc -v 2>&1 | grep -q -x 'Target: i586-mingw32msvc'" 43 before_script: "i586-mingw32msvc-gcc -v 2>&1 | grep -q -x 'Target: i586-mingw32msvc'"
41 44
45 exclude:
46 # unsupported
47 - compiler: i686-linux-gnu-gcc
48 env: ci_buildsys=Makefile
49 - compiler: x86_64-linux-gnu-gcc
50 env: ci_buildsys=Makefile
51
42script: 52script:
43 - ci_target=${CC%-*} ./tools/ci-build.sh 53 - ci_target=${CC%-*} ./tools/ci-build.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2202c93..df53be1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,7 +39,9 @@ else()
39 add_compile_options(-Wall) 39 add_compile_options(-Wall)
40endif() 40endif()
41 41
42add_subdirectory(src) 42if(WIN32)
43 add_subdirectory(src)
44endif()
43 45
44if (BUILD_TESTS) 46if (BUILD_TESTS)
45 add_subdirectory(tests) 47 add_subdirectory(tests)
diff --git a/Makefile b/Makefile
index ab7279b..944358f 100644
--- a/Makefile
+++ b/Makefile
@@ -6,14 +6,14 @@ CFLAGS = -Wall -O3 -fomit-frame-pointer -Isrc
6 6
7ifeq ($(BUILD_SHARED),yes) 7ifeq ($(BUILD_SHARED),yes)
8 TARGETS += libdl.dll 8 TARGETS += libdl.dll
9 SHFLAGS += -Wl,--out-implib,libdl.dll.a -DSHARED 9 SHFLAGS += -Wl,--out-implib,libdl.dll.a
10 INSTALL += shared-install 10 INSTALL += shared-install
11 TESTS += test.exe 11 TESTS += test.exe test-dladdr.exe
12endif 12endif
13ifeq ($(BUILD_STATIC),yes) 13ifeq ($(BUILD_STATIC),yes)
14 TARGETS += libdl.a 14 TARGETS += libdl.a
15 INSTALL += static-install 15 INSTALL += static-install
16 TESTS += test-static.exe 16 TESTS += test-static.exe test-dladdr-static.exe
17endif 17endif
18ifeq ($(BUILD_MSVC),yes) 18ifeq ($(BUILD_MSVC),yes)
19 TARGETS += libdl.lib 19 TARGETS += libdl.lib
@@ -32,7 +32,7 @@ libdl.a: $(SOURCES)
32 $(RANLIB) $@ 32 $(RANLIB) $@
33 33
34libdl.dll: $(SOURCES) 34libdl.dll: $(SOURCES)
35 $(CC) $(CFLAGS) $(SHFLAGS) -shared -o $@ $^ 35 $(CC) $(CFLAGS) $(SHFLAGS) -DDLFCN_WIN32_SHARED -shared -o $@ $^
36 36
37libdl.lib: libdl.dll 37libdl.lib: libdl.dll
38 $(LIBCMD) /machine:i386 /def:libdl.def 38 $(LIBCMD) /machine:i386 /def:libdl.def
@@ -64,6 +64,12 @@ test.exe: tests/test.c $(TARGETS)
64test-static.exe: tests/test.c $(TARGETS) 64test-static.exe: tests/test.c $(TARGETS)
65 $(CC) $(CFLAGS) -o $@ $< libdl.a 65 $(CC) $(CFLAGS) -o $@ $< libdl.a
66 66
67test-dladdr.exe: tests/test-dladdr.c $(TARGETS)
68 $(CC) $(CFLAGS) -DDLFCN_WIN32_SHARED -o $@ $< libdl.dll.a
69
70test-dladdr-static.exe: tests/test-dladdr.c $(TARGETS)
71 $(CC) $(CFLAGS) -o $@ $< libdl.a
72
67testdll.dll: tests/testdll.c 73testdll.dll: tests/testdll.c
68 $(CC) $(CFLAGS) -shared -o $@ $^ 74 $(CC) $(CFLAGS) -shared -o $@ $^
69 75
@@ -81,6 +87,7 @@ clean::
81 src/dlfcn.o \ 87 src/dlfcn.o \
82 libdl.dll libdl.a libdl.def libdl.dll.a libdl.lib libdl.exp \ 88 libdl.dll libdl.a libdl.def libdl.dll.a libdl.lib libdl.exp \
83 tmptest.c tmptest.dll \ 89 tmptest.c tmptest.dll \
90 test-dladdr.exe test-dladdr-static.exe \
84 test.exe test-static.exe testdll.dll testdll2.dll testdll3.dll 91 test.exe test-static.exe testdll.dll testdll2.dll testdll3.dll
85 92
86distclean: clean 93distclean: clean
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d5296b1..27f9030 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,15 +1,20 @@
1set(headers dlfcn.h) 1set(headers dlfcn.h)
2set(sources dlfcn.c) 2set(sources dlfcn.c)
3 3
4if (BUILD_SHARED_LIBS)
5 add_definitions(-DSHARED)
6endif (BUILD_SHARED_LIBS)
7 4
8add_library(dl ${sources}) 5add_library(dl ${sources})
9 6
10# Correctly export the location of installed includes in the target 7# Correctly export the location of installed includes in the target
11target_include_directories(dl INTERFACE $<INSTALL_INTERFACE:include>) 8target_include_directories(dl INTERFACE $<INSTALL_INTERFACE:include>)
12 9
10# dot not add -D<target>_EXPORTS
11set_target_properties(dl PROPERTIES DEFINE_SYMBOL "")
12
13# set shared mode for compiling library and propagate mode to cmake clients
14if (BUILD_SHARED_LIBS)
15 target_compile_definitions(dl PUBLIC DLFCN_WIN32_SHARED)
16endif (BUILD_SHARED_LIBS)
17
13install (TARGETS dl EXPORT dlfcn-win32-targets 18install (TARGETS dl EXPORT dlfcn-win32-targets
14 RUNTIME DESTINATION bin 19 RUNTIME DESTINATION bin
15 LIBRARY DESTINATION lib${LIB_SUFFIX} 20 LIBRARY DESTINATION lib${LIB_SUFFIX}
diff --git a/src/dlfcn.c b/src/dlfcn.c
index 3251661..5dd1ccf 100644
--- a/src/dlfcn.c
+++ b/src/dlfcn.c
@@ -3,6 +3,7 @@
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 * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
6 * Copyright (c) 2020 Ralf Habacker <ralf.habacker@freenet.de>
6 * 7 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal 9 * of this software and associated documentation files (the "Software"), to deal
@@ -45,7 +46,7 @@
45#endif 46#endif
46#endif 47#endif
47 48
48#ifdef SHARED 49#ifdef DLFCN_WIN32_SHARED
49#define DLFCN_WIN32_EXPORTS 50#define DLFCN_WIN32_EXPORTS
50#endif 51#endif
51#include "dlfcn.h" 52#include "dlfcn.h"
@@ -222,6 +223,7 @@ static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb,
222 return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded ); 223 return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
223} 224}
224 225
226DLFCN_EXPORT
225void *dlopen( const char *file, int mode ) 227void *dlopen( const char *file, int mode )
226{ 228{
227 HMODULE hModule; 229 HMODULE hModule;
@@ -329,6 +331,7 @@ void *dlopen( const char *file, int mode )
329 return (void *) hModule; 331 return (void *) hModule;
330} 332}
331 333
334DLFCN_EXPORT
332int dlclose( void *handle ) 335int dlclose( void *handle )
333{ 336{
334 HMODULE hModule = (HMODULE) handle; 337 HMODULE hModule = (HMODULE) handle;
@@ -353,6 +356,7 @@ int dlclose( void *handle )
353} 356}
354 357
355__declspec(noinline) /* Needed for _ReturnAddress() */ 358__declspec(noinline) /* Needed for _ReturnAddress() */
359DLFCN_EXPORT
356void *dlsym( void *handle, const char *name ) 360void *dlsym( void *handle, const char *name )
357{ 361{
358 FARPROC symbol; 362 FARPROC symbol;
@@ -460,6 +464,7 @@ end:
460 return *(void **) (&symbol); 464 return *(void **) (&symbol);
461} 465}
462 466
467DLFCN_EXPORT
463char *dlerror( void ) 468char *dlerror( void )
464{ 469{
465 /* If this is the second consecutive call to dlerror, return NULL */ 470 /* If this is the second consecutive call to dlerror, return NULL */
@@ -474,7 +479,212 @@ char *dlerror( void )
474 return error_buffer; 479 return error_buffer;
475} 480}
476 481
477#ifdef SHARED 482/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2
483 * for details */
484
485/* Get specific image section */
486static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size )
487{
488 IMAGE_DOS_HEADER *dosHeader;
489 IMAGE_OPTIONAL_HEADER *optionalHeader;
490
491 dosHeader = (IMAGE_DOS_HEADER *) module;
492
493 if( dosHeader->e_magic != 0x5A4D )
494 return FALSE;
495
496 optionalHeader = (IMAGE_OPTIONAL_HEADER *) ( (BYTE *) module + dosHeader->e_lfanew + 24 );
497
498 if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
499 return FALSE;
500
501 if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
502 return FALSE;
503
504 if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 )
505 return FALSE;
506
507 if( size != NULL )
508 *size = optionalHeader->DataDirectory[index].Size;
509
510 *ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress );
511
512 return TRUE;
513}
514
515/* Return symbol name for a given address */
516static const char *get_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address )
517{
518 int i;
519 void *candidateAddr = NULL;
520 const char *candidateName = NULL;
521 BYTE *base = (BYTE *) module; /* Required to have correct calculations */
522
523 for( i = 0; iid[i].Characteristics != 0 && iid[i].FirstThunk != 0; i++ )
524 {
525 IMAGE_THUNK_DATA *thunkILT = (IMAGE_THUNK_DATA *)( base + iid[i].Characteristics );
526 IMAGE_THUNK_DATA *thunkIAT = (IMAGE_THUNK_DATA *)( base + iid[i].FirstThunk );
527
528 for( ; thunkILT->u1.AddressOfData != 0; thunkILT++, thunkIAT++ )
529 {
530 IMAGE_IMPORT_BY_NAME *nameData;
531
532 if( IMAGE_SNAP_BY_ORDINAL( thunkILT->u1.Ordinal ) )
533 continue;
534
535 if( (void *) thunkIAT->u1.Function > addr || candidateAddr >= (void *) thunkIAT->u1.Function )
536 continue;
537
538 candidateAddr = (void *) thunkIAT->u1.Function;
539 nameData = (IMAGE_IMPORT_BY_NAME *)( base + (ULONG_PTR) thunkILT->u1.AddressOfData );
540 candidateName = (const char *) nameData->Name;
541 }
542 }
543
544 *func_address = candidateAddr;
545 return candidateName;
546}
547
548static BOOL is_valid_address( void *addr )
549{
550 MEMORY_BASIC_INFORMATION info;
551 SIZE_T result;
552
553 if( addr == NULL )
554 return FALSE;
555
556 /* check valid pointer */
557 result = VirtualQuery( addr, &info, sizeof( info ) );
558
559 if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS )
560 return FALSE;
561
562 return TRUE;
563}
564
565/* Return state if address points to an import thunk
566 *
567 * An import thunk is setup with a 'jmp' instruction followed by an
568 * absolute address (32bit) or relative offset (64bit) pointing into
569 * the import address table (iat), which is partially maintained by
570 * the runtime linker.
571 */
572static BOOL is_import_thunk( void *addr )
573{
574 return *(short *) addr == 0x25ff ? TRUE : FALSE;
575}
576
577/* Return adress from the import address table (iat),
578 * if the original address points to a thunk table entry.
579 */
580static void *get_address_from_import_address_table( void *iat, DWORD iat_size, void *addr )
581{
582 BYTE *thkp = (BYTE *) addr;
583 /* Get offset from thunk table (after instruction 0xff 0x25)
584 * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
585 */
586 ULONG offset = *(ULONG *)( thkp + 2 );
587#ifdef _WIN64
588 /* On 64 bit the offset is relative
589 * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery>
590 * And can be also negative (MSVC in WDK)
591 * 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060
592 * So cast to signed LONG type
593 */
594 BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset );
595#else
596 /* On 32 bit the offset is absolute
597 * 4019b4: ff 25 90 71 40 00 jmp *0x40719
598 */
599 BYTE *ptr = (BYTE *) offset;
600#endif
601
602 if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size )
603 return NULL;
604
605 return *(void **) ptr;
606}
607
608/* Holds module filename */
609static char module_filename[2*MAX_PATH];
610
611static BOOL fill_module_info( void *addr, Dl_info *info )
612{
613 HMODULE hModule;
614 DWORD dwSize;
615
616 if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL )
617 return FALSE;
618
619 dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) );
620
621 if( dwSize == 0 || dwSize == sizeof( module_filename ) )
622 return FALSE;
623
624 info->dli_fbase = (void *) hModule;
625 info->dli_fname = module_filename;
626
627 return TRUE;
628}
629
630DLFCN_EXPORT
631int dladdr( void *addr, Dl_info *info )
632{
633 void *realAddr, *funcAddress = NULL;
634 HMODULE hModule;
635 IMAGE_IMPORT_DESCRIPTOR *iid;
636 DWORD iidSize = 0;
637
638 if( addr == NULL || info == NULL )
639 return 0;
640
641 hModule = GetModuleHandleA( NULL );
642 if( hModule == NULL )
643 return 0;
644
645 if( !is_valid_address( addr ) )
646 return 0;
647
648 realAddr = addr;
649
650 if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) )
651 return 0;
652
653 if( is_import_thunk( addr ) )
654 {
655 void *iat;
656 void *iatAddr = NULL;
657 DWORD iatSize = 0;
658
659 if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) )
660 {
661 /* Fallback for cases where the iat is not defined,
662 * for example i586-mingw32msvc-gcc */
663 if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 )
664 return 0;
665
666 iat = (void *)( (BYTE *) hModule + iid->FirstThunk );
667 /* We assume that in this case iid and iat's are in linear order */
668 iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid );
669 }
670
671 iatAddr = get_address_from_import_address_table( iat, iatSize, addr );
672 if( iatAddr == NULL )
673 return 0;
674
675 realAddr = iatAddr;
676 }
677
678 if( !fill_module_info( realAddr, info ) )
679 return 0;
680
681 info->dli_sname = get_symbol_name( hModule, iid, realAddr, &funcAddress );
682
683 info->dli_saddr = !info->dli_sname ? NULL : funcAddress ? funcAddress : realAddr;
684 return 1;
685}
686
687#ifdef DLFCN_WIN32_SHARED
478BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) 688BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
479{ 689{
480 (void) hinstDLL; 690 (void) hinstDLL;
diff --git a/src/dlfcn.h b/src/dlfcn.h
index 9ddbba3..164216f 100644
--- a/src/dlfcn.h
+++ b/src/dlfcn.h
@@ -28,9 +28,13 @@
28extern "C" { 28extern "C" {
29#endif 29#endif
30 30
31#if defined(DLFCN_WIN32_SHARED)
31#if defined(DLFCN_WIN32_EXPORTS) 32#if defined(DLFCN_WIN32_EXPORTS)
32# define DLFCN_EXPORT __declspec(dllexport) 33# define DLFCN_EXPORT __declspec(dllexport)
33#else 34#else
35# define DLFCN_EXPORT __declspec(dllimport)
36#endif
37#else
34# define DLFCN_EXPORT 38# define DLFCN_EXPORT
35#endif 39#endif
36 40
@@ -59,6 +63,15 @@ extern "C" {
59/* Specifies the next object after this one that defines name. */ 63/* Specifies the next object after this one that defines name. */
60#define RTLD_NEXT ((void *)-1) 64#define RTLD_NEXT ((void *)-1)
61 65
66/* Structure filled in by dladdr() */
67typedef struct dl_info
68{
69 const char *dli_fname; /* Filename of defining object (thread unsafe and reused on every call to dladdr) */
70 void *dli_fbase; /* Load address of that object */
71 const char *dli_sname; /* Name of nearest lower symbol */
72 void *dli_saddr; /* Exact value of nearest symbol */
73} Dl_info;
74
62/* Open a symbol table handle. */ 75/* Open a symbol table handle. */
63DLFCN_EXPORT void *dlopen(const char *file, int mode); 76DLFCN_EXPORT void *dlopen(const char *file, int mode);
64 77
@@ -71,6 +84,9 @@ DLFCN_EXPORT void *dlsym(void *handle, const char *name);
71/* Get diagnostic information. */ 84/* Get diagnostic information. */
72DLFCN_EXPORT char *dlerror(void); 85DLFCN_EXPORT char *dlerror(void);
73 86
87/* Translate address to symbolic information (no POSIX standard) */
88DLFCN_EXPORT int dladdr(void *addr, Dl_info *info);
89
74#ifdef __cplusplus 90#ifdef __cplusplus
75} 91}
76#endif 92#endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 5fc6c44..00b6647 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,17 +1,33 @@
1include_directories(../src)
2 1
3add_library(testdll SHARED testdll.c) 2if(WIN32)
4set_target_properties(testdll PROPERTIES PREFIX "") 3 include_directories(../src)
5 4
6add_library(testdll2 SHARED testdll2.c) 5 add_library(testdll SHARED testdll.c)
7set_target_properties(testdll2 PROPERTIES PREFIX "") 6 set_target_properties(testdll PROPERTIES PREFIX "")
8target_link_libraries(testdll2 dl)
9 7
10add_library(testdll3 SHARED testdll3.c) 8 add_library(testdll2 SHARED testdll2.c)
11set_target_properties(testdll3 PROPERTIES PREFIX "") 9 set_target_properties(testdll2 PROPERTIES PREFIX "")
10 target_link_libraries(testdll2 dl)
11
12 add_library(testdll3 SHARED testdll3.c)
13 set_target_properties(testdll3 PROPERTIES PREFIX "")
14
15 add_executable(t_dlfcn test.c)
16 target_link_libraries(t_dlfcn dl)
17
18 if(RUN_TESTS)
19 add_test(NAME t_dlfcn COMMAND ${WRAPPER} $<TARGET_FILE:t_dlfcn> WORKING_DIRECTORY $<TARGET_FILE_DIR:t_dlfcn>)
20 endif()
21endif()
22
23add_executable(test-dladdr test-dladdr.c)
24target_link_libraries(test-dladdr dl)
25if(UNIX)
26 set_target_properties(test-dladdr PROPERTIES COMPILE_FLAGS "-Wl,--export-dynamic -fpie")
27endif()
28
29install(TARGETS test-dladdr EXPORT dlfcn-win32-targets RUNTIME DESTINATION bin)
12 30
13add_executable(t_dlfcn test.c)
14target_link_libraries(t_dlfcn dl)
15if(RUN_TESTS) 31if(RUN_TESTS)
16 add_test(NAME t_dlfcn COMMAND ${WRAPPER} $<TARGET_FILE:t_dlfcn> WORKING_DIRECTORY $<TARGET_FILE_DIR:t_dlfcn>) 32 add_test(NAME test-dladdr COMMAND ${WRAPPER} $<TARGET_FILE:test-dladdr> WORKING_DIRECTORY $<TARGET_FILE_DIR:test-dladdr>)
17endif() 33endif()
diff --git a/tests/test-dladdr.c b/tests/test-dladdr.c
new file mode 100644
index 0000000..57914cb
--- /dev/null
+++ b/tests/test-dladdr.c
@@ -0,0 +1,184 @@
1/* On Unix like os compile with "-Wl,--export-dynamic -fpie" (default with cmake) */
2
3/* required for non Windows builds, must be set in front of the first system include */
4#define _GNU_SOURCE
5
6#include <dlfcn.h>
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12static int verbose = 0;
13
14typedef enum {
15 Pass = 1,
16 PassWithoutSymbol = 2,
17 PassWithDifferentAddress = 3,
18 Fail = 0,
19 NoInfo = -1,
20} ExpectedResult;
21
22typedef void (* func) (void);
23
24static void print_dl_info( Dl_info *info, char *prefix, char *suffix )
25{
26 printf( "%sfilename: %s base: %p symbol name: '%s' addr: %p%s", prefix, info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr, suffix );
27}
28
29/**
30 * @brief check information returned by dladdr
31 * @param hint text describing what to test
32 * @param addr address to check
33 * @param addrsym
34 * @param expected check against expected result
35 * @return 0 check passed
36 * @return 1 check failed
37 */
38static int check_dladdr( const char *hint, void *addr, char *addrsym, ExpectedResult expected_result )
39{
40 Dl_info info;
41 int result = dladdr( addr, &info );
42 int passed = 0;
43 if (!result)
44 {
45 passed = expected_result == NoInfo || expected_result == Fail;
46 }
47 else
48 {
49 int sym_match = info.dli_sname && strcmp( addrsym, info.dli_sname ) == 0;
50 int addr_match = addr == info.dli_saddr;
51 passed = (expected_result == Pass && sym_match && addr_match)
52 || (expected_result == PassWithoutSymbol && addr_match && !info.dli_sname)
53 || (expected_result == PassWithDifferentAddress && sym_match && !addr_match)
54 || (expected_result == Fail && (!sym_match || !addr_match));
55 }
56 printf( "checking '%s' - address %p which should have symbol '%s' -> %s%s", hint, addr, addrsym, passed ? "passed" : "failed", verbose || !passed ? " " : "\n" );
57 if( verbose || !passed )
58 {
59 if( !result )
60 printf( "(could not get symbol information for address %p)\n", addr );
61 else
62 print_dl_info( &info, "(", ")\n");
63 }
64 return !passed;
65}
66
67#ifdef _WIN32
68/**
69 * @brief check address from a symbol located in a shared lilbrary
70 * @param hint text describing what to test
71 * @param libname libray to get the address from
72 * @param addrsym symbol to get the address for
73 * @param should_match result should match the given values
74 * @return 0 check passed
75 * @return 1 check failed
76 * @return 2 failed to open library
77 * @return 3 failed to get symbol address
78 */
79static int check_dladdr_by_dlopen( const char *hint, char *libname, char *sym, int should_match )
80{
81 void *library = NULL;
82 void *addr = NULL;
83 int result;
84
85 library = dlopen( libname, RTLD_GLOBAL );
86 if ( library == NULL )
87 {
88 fprintf( stderr, "could not open '%s'\n", libname );
89 return 2;
90 }
91
92 addr = dlsym( library, sym );
93 if ( !addr ) {
94 fprintf( stderr, "could not get address from library '%s' for symbol '%s'\n", libname, sym );
95 return 3;
96 }
97
98 result = check_dladdr( hint, addr, sym, should_match );
99 dlclose( library );
100
101 return result;
102}
103#endif
104
105#ifdef _WIN32
106#include <windows.h>
107#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
108/* hide warning "reclared without 'dllimport' attribute" */
109#pragma GCC diagnostic push
110#pragma GCC diagnostic ignored "-Wattributes"
111#elif defined(_MSC_VER)
112/* disable warning C4273 inconsistent dll linkage */
113#pragma warning(push)
114#pragma warning(disable: 4273)
115#endif
116/*
117 * Windows API functions decorated with WINBASEAPI are imported from iat.
118 * For testing force linking by import thunk for the following functions
119 */
120HMODULE WINAPI GetModuleHandleA (LPCSTR lpModuleName);
121SIZE_T WINAPI VirtualQuery (LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
122/* force linking by iat */
123/* WINBASEAPI, which is normally used here, can be overriden by compiler or application, so we cannot use it */
124__declspec(dllimport) HMODULE WINAPI LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
125__declspec(dllimport) int __cdecl atoi(const char *_Str);
126
127#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
128#pragma GCC diagnostic pop
129#elif defined(_MSC_VER)
130#pragma warning(pop)
131#endif
132#endif
133
134#ifdef _WIN32
135#define FailOnWin Fail
136#else
137#define FailOnWin Pass
138#endif
139
140#if defined(_WIN32) && !defined(DLFCN_WIN32_SHARED)
141#define PassOnSharedBuild Fail
142#else
143#define PassOnSharedBuild Pass
144#endif
145
146#define UNUSED(x) (void)x
147
148int main(int argc, char **argv)
149{
150 /* points to non reachable address */
151 unsigned char zero_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 };
152 /* points to executable base */
153 unsigned char invalid_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x40, 0x00 };
154 /* no import thunk */
155 unsigned char no_import_thunk[6] = { 0xFF, 0x26, 0x00, 0x00, 0x40, 0x00 };
156 int result = 0;
157 UNUSED(argv);
158
159 if (argc == 2)
160 verbose = 1;
161
162 result = check_dladdr( "null pointer", (void*)0, NULL , NoInfo);
163 result |= check_dladdr( "invalid pointer", (void*)0x125, NULL , NoInfo);
164 result |= check_dladdr( "function from dl library", (void*)dladdr, "dladdr" , PassOnSharedBuild );
165 result |= check_dladdr( "function from dl library", (void*)dlopen, "dlopen", PassOnSharedBuild );
166 result |= check_dladdr( "function from glibc/msvcrt library", (void*)atoi, "atoi", Pass );
167 result |= check_dladdr( "function from executable", (void*)main, "main", FailOnWin );
168 result |= check_dladdr( "static function from executable", (void*)print_dl_info, "print_dl_info", Fail );
169 result |= check_dladdr( "address with positive offset", ((char*)atoi)+1, "atoi", PassWithDifferentAddress );
170 result |= check_dladdr( "zero address from import thunk", zero_thunk_address, "", NoInfo );
171 result |= check_dladdr( "invalid address from import thunk", invalid_thunk_address, "", NoInfo );
172 result |= check_dladdr( "no import thunk", no_import_thunk, "", NoInfo );
173
174#ifdef _WIN32
175 result |= check_dladdr( "last entry in iat", (void*)VirtualQuery, "VirtualQuery", PassWithDifferentAddress );
176
177 result |= check_dladdr ( "address through import thunk", (void*)GetModuleHandleA, "GetModuleHandleA", PassWithDifferentAddress );
178 result |= check_dladdr_by_dlopen( "address by dlsym", "kernel32.dll", "GetModuleHandleA", Pass );
179
180 result |= check_dladdr ( "address by image allocation table", (void*)LoadLibraryExA, "LoadLibraryExA", Pass );
181 result |= check_dladdr_by_dlopen( "address by dlsym", "kernel32.dll", "LoadLibraryExA", Pass );
182#endif
183 return result;
184}
diff --git a/tools/ci-build.sh b/tools/ci-build.sh
index 366a407..196fd92 100755
--- a/tools/ci-build.sh
+++ b/tools/ci-build.sh
@@ -9,35 +9,72 @@ set -x
9 9
10# ci_target: 10# ci_target:
11# target to build for 11# target to build for
12: "${ci_target:=${CROSS_COMPILE%-}}" 12: "${ci_target:=x86_64-linux-gnu-gcc}"
13
14# ci_variant:
15# variant to build e.g shared, static
16: "${ci_variant:=shared}"
17
18if test -v CROSS_COMPILE; then
19 ci_target=${CROSS_COMPILE%-}
20fi
13 21
14case "$ci_buildsys" in 22case "$ci_buildsys" in
15 (Makefile) 23 (Makefile)
16 ./configure --enable-shared --enable-static --enable-wine --cross-prefix=${ci_target}- 24 case "$ci_target" in
17 make 25 (*mingw32*)
18 make test 26 ./configure --enable-shared --enable-static --enable-wine --cross-prefix=${ci_target}-
27 make
28 make test
29 ;;
30 esac
19 ;; 31 ;;
20 32
21 (cmake) 33 (cmake)
34 cmake_options="
35 --no-warn-unused-cli
36 -DBUILD_TESTS=1
37 -DCMAKE_BUILD_TYPE=RelWithDebInfo
38 "
39 case "$ci_variant" in
40 (shared)
41 cmake_options+=" -DBUILD_SHARED_LIBS=ON"
42 ;;
43 (static)
44 cmake_options+=" -DBUILD_SHARED_LIBS=OFF"
45 ;;
46 esac
47
22 cmake --version 48 cmake --version
23 rm -rf build 49
24 mkdir build 50 # create build dir
25 cd build 51 rm -rf ci-build-${ci_variant}-${ci_target}
26 cmake \ 52 mkdir -p ci-build-${ci_variant}-${ci_target}
27 --no-warn-unused-cli \ 53 cd ci-build-${ci_variant}-${ci_target}
28 -DCMAKE_FIND_ROOT_PATH=$(${ci_target}-gcc --print-sysroot)/${ci_target} \ 54
29 -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 55 case "$ci_target" in
30 -DCMAKE_C_COMPILER=$(which ${ci_target}-gcc) \ 56 (*mingw32*)
31 -DCMAKE_SYSTEM_PROCESSOR=${ci_target%-*-*} \ 57 cmake \
32 -DCMAKE_CROSSCOMPILING=TRUE \ 58 -DCMAKE_FIND_ROOT_PATH=$(${ci_target}-gcc --print-sysroot)/${ci_target} \
33 -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \ 59 -DCMAKE_C_COMPILER=$(which ${ci_target}-gcc) \
34 -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \ 60 -DCMAKE_SYSTEM_PROCESSOR=${ci_target%-*-*} \
35 -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \ 61 -DCMAKE_CROSSCOMPILING=TRUE \
36 -DCMAKE_SYSTEM_NAME=Windows \ 62 -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \
37 -DBUILD_TESTS=1 \ 63 -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \
38 -DENABLE_WINE=ON \ 64 -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \
39 -DWINE_EXECUTABLE=/usr/bin/wine \ 65 -DCMAKE_SYSTEM_NAME=Windows \
40 .. 66 -DENABLE_WINE=ON \
67 -DWINE_EXECUTABLE=/usr/bin/wine \
68 $cmake_options \
69 ..
70 ;;
71 (*linux*)
72 cmake \
73 $cmake_options \
74 ..
75 ;;
76 esac
77
41 make 78 make
42 ctest --output-on-failure 79 ctest --output-on-failure
43 make install DESTDIR=$(pwd)/DESTDIR 80 make install DESTDIR=$(pwd)/DESTDIR