diff options
author | Pali Rohár <pali.rohar@gmail.com> | 2021-01-25 23:27:22 +0100 |
---|---|---|
committer | Pali Rohár <pali.rohar@gmail.com> | 2021-01-27 18:58:21 +0100 |
commit | 86a41b921ca5c4bad1d0d7e54f9276046a25e319 (patch) | |
tree | a8b64ba1d9a4cf2432d1f65333a1de1dd6a9299e | |
parent | a5af061ce1d1bb3f58e74c48c0698dad59d79937 (diff) | |
download | dlfcn-win32-86a41b921ca5c4bad1d0d7e54f9276046a25e319.tar.gz dlfcn-win32-86a41b921ca5c4bad1d0d7e54f9276046a25e319.tar.bz2 dlfcn-win32-86a41b921ca5c4bad1d0d7e54f9276046a25e319.zip |
Function dladdr() now retrieve symbol name and symbol address from both export and import tables
dladdr tests for Windows now should always pass like on other systems.
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | src/dlfcn.c | 106 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 7 | ||||
-rw-r--r-- | tests/test-dladdr.c | 23 |
4 files changed, 97 insertions, 43 deletions
@@ -65,10 +65,10 @@ test-static.exe: tests/test.c $(TARGETS) | |||
65 | $(CC) $(CFLAGS) -o $@ $< libdl.a | 65 | $(CC) $(CFLAGS) -o $@ $< libdl.a |
66 | 66 | ||
67 | test-dladdr.exe: tests/test-dladdr.c $(TARGETS) | 67 | test-dladdr.exe: tests/test-dladdr.c $(TARGETS) |
68 | $(CC) $(CFLAGS) -DDLFCN_WIN32_SHARED -o $@ $< libdl.dll.a | 68 | $(CC) $(CFLAGS) -Wl,--export-all-symbols -DDLFCN_WIN32_SHARED -o $@ $< libdl.dll.a |
69 | 69 | ||
70 | test-dladdr-static.exe: tests/test-dladdr.c $(TARGETS) | 70 | test-dladdr-static.exe: tests/test-dladdr.c $(TARGETS) |
71 | $(CC) $(CFLAGS) -o $@ $< libdl.a | 71 | $(CC) $(CFLAGS) -Wl,--export-all-symbols -o $@ $< libdl.a |
72 | 72 | ||
73 | testdll.dll: tests/testdll.c | 73 | testdll.dll: tests/testdll.c |
74 | $(CC) $(CFLAGS) -shared -o $@ $^ | 74 | $(CC) $(CFLAGS) -shared -o $@ $^ |
diff --git a/src/dlfcn.c b/src/dlfcn.c index 5dd1ccf..b52b307 100644 --- a/src/dlfcn.c +++ b/src/dlfcn.c | |||
@@ -512,8 +512,8 @@ static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *siz | |||
512 | return TRUE; | 512 | return TRUE; |
513 | } | 513 | } |
514 | 514 | ||
515 | /* Return symbol name for a given address */ | 515 | /* Return symbol name for a given address from import table */ |
516 | static const char *get_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address ) | 516 | static const char *get_import_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address ) |
517 | { | 517 | { |
518 | int i; | 518 | int i; |
519 | void *candidateAddr = NULL; | 519 | void *candidateAddr = NULL; |
@@ -545,6 +545,40 @@ static const char *get_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid | |||
545 | return candidateName; | 545 | return candidateName; |
546 | } | 546 | } |
547 | 547 | ||
548 | /* Return symbol name for a given address from export table */ | ||
549 | static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, void *addr, void **func_address ) | ||
550 | { | ||
551 | DWORD i; | ||
552 | void *candidateAddr = NULL; | ||
553 | int candidateIndex = -1; | ||
554 | BYTE *base = (BYTE *) module; | ||
555 | DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions); | ||
556 | DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames); | ||
557 | USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals); | ||
558 | |||
559 | for( i = 0; i < ied->NumberOfFunctions; i++ ) | ||
560 | { | ||
561 | if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) ) | ||
562 | continue; | ||
563 | |||
564 | candidateAddr = (void *) ( base + functionAddressesOffsets[i] ); | ||
565 | candidateIndex = i; | ||
566 | } | ||
567 | |||
568 | if( candidateIndex == -1 ) | ||
569 | return NULL; | ||
570 | |||
571 | *func_address = candidateAddr; | ||
572 | |||
573 | for( i = 0; i < ied->NumberOfNames; i++ ) | ||
574 | { | ||
575 | if( functionNameOrdinalsIndexes[i] == candidateIndex ) | ||
576 | return (const char *) ( base + functionNamesOffsets[i] ); | ||
577 | } | ||
578 | |||
579 | return NULL; | ||
580 | } | ||
581 | |||
548 | static BOOL is_valid_address( void *addr ) | 582 | static BOOL is_valid_address( void *addr ) |
549 | { | 583 | { |
550 | MEMORY_BASIC_INFORMATION info; | 584 | MEMORY_BASIC_INFORMATION info; |
@@ -608,11 +642,16 @@ static void *get_address_from_import_address_table( void *iat, DWORD iat_size, v | |||
608 | /* Holds module filename */ | 642 | /* Holds module filename */ |
609 | static char module_filename[2*MAX_PATH]; | 643 | static char module_filename[2*MAX_PATH]; |
610 | 644 | ||
611 | static BOOL fill_module_info( void *addr, Dl_info *info ) | 645 | static BOOL fill_info( HMODULE hModuleImport, void *addr, Dl_info *info ) |
612 | { | 646 | { |
613 | HMODULE hModule; | 647 | HMODULE hModule; |
614 | DWORD dwSize; | 648 | DWORD dwSize; |
649 | IMAGE_EXPORT_DIRECTORY *ied; | ||
650 | IMAGE_IMPORT_DESCRIPTOR *iid; | ||
651 | const char *name; | ||
652 | void *funcAddress = NULL; | ||
615 | 653 | ||
654 | /* Get module of the specified address */ | ||
616 | if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL ) | 655 | if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL ) |
617 | return FALSE; | 656 | return FALSE; |
618 | 657 | ||
@@ -621,8 +660,27 @@ static BOOL fill_module_info( void *addr, Dl_info *info ) | |||
621 | if( dwSize == 0 || dwSize == sizeof( module_filename ) ) | 660 | if( dwSize == 0 || dwSize == sizeof( module_filename ) ) |
622 | return FALSE; | 661 | return FALSE; |
623 | 662 | ||
624 | info->dli_fbase = (void *) hModule; | ||
625 | info->dli_fname = module_filename; | 663 | info->dli_fname = module_filename; |
664 | info->dli_fbase = (void *) hModule; | ||
665 | info->dli_sname = NULL; | ||
666 | |||
667 | /* First try to find function name and function address in module's export table */ | ||
668 | if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) ) | ||
669 | info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress ); | ||
670 | |||
671 | /* If symbol name is not known and we know which module is importing this address | ||
672 | * then try to find symbol name in this module's import table as the last resort. */ | ||
673 | if( info->dli_sname == NULL && hModuleImport != NULL ) | ||
674 | { | ||
675 | if( get_image_section( hModuleImport, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, NULL ) ) | ||
676 | { | ||
677 | name = get_import_symbol_name( hModuleImport, iid, addr, &funcAddress ); | ||
678 | if( name != NULL ) | ||
679 | info->dli_sname = name; | ||
680 | } | ||
681 | } | ||
682 | |||
683 | info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : addr; | ||
626 | 684 | ||
627 | return TRUE; | 685 | return TRUE; |
628 | } | 686 | } |
@@ -630,36 +688,33 @@ static BOOL fill_module_info( void *addr, Dl_info *info ) | |||
630 | DLFCN_EXPORT | 688 | DLFCN_EXPORT |
631 | int dladdr( void *addr, Dl_info *info ) | 689 | int dladdr( void *addr, Dl_info *info ) |
632 | { | 690 | { |
633 | void *realAddr, *funcAddress = NULL; | 691 | HMODULE hModule = NULL; |
634 | HMODULE hModule; | ||
635 | IMAGE_IMPORT_DESCRIPTOR *iid; | ||
636 | DWORD iidSize = 0; | ||
637 | 692 | ||
638 | if( addr == NULL || info == NULL ) | 693 | if( addr == NULL || info == NULL ) |
639 | return 0; | 694 | return 0; |
640 | 695 | ||
641 | hModule = GetModuleHandleA( NULL ); | ||
642 | if( hModule == NULL ) | ||
643 | return 0; | ||
644 | |||
645 | if( !is_valid_address( addr ) ) | 696 | if( !is_valid_address( addr ) ) |
646 | return 0; | 697 | return 0; |
647 | 698 | ||
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 ) ) | 699 | if( is_import_thunk( addr ) ) |
654 | { | 700 | { |
655 | void *iat; | 701 | void *iat; |
656 | void *iatAddr = NULL; | 702 | DWORD iatSize; |
657 | DWORD iatSize = 0; | 703 | |
704 | /* Get module of the import thunk address */ | ||
705 | if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL ) | ||
706 | return 0; | ||
658 | 707 | ||
659 | if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) ) | 708 | if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) ) |
660 | { | 709 | { |
661 | /* Fallback for cases where the iat is not defined, | 710 | /* Fallback for cases where the iat is not defined, |
662 | * for example i586-mingw32msvc-gcc */ | 711 | * for example i586-mingw32msvc-gcc */ |
712 | IMAGE_IMPORT_DESCRIPTOR *iid; | ||
713 | DWORD iidSize; | ||
714 | |||
715 | if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) ) | ||
716 | return 0; | ||
717 | |||
663 | if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 ) | 718 | if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 ) |
664 | return 0; | 719 | return 0; |
665 | 720 | ||
@@ -668,19 +723,18 @@ int dladdr( void *addr, Dl_info *info ) | |||
668 | iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid ); | 723 | iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid ); |
669 | } | 724 | } |
670 | 725 | ||
671 | iatAddr = get_address_from_import_address_table( iat, iatSize, addr ); | 726 | addr = get_address_from_import_address_table( iat, iatSize, addr ); |
672 | if( iatAddr == NULL ) | 727 | |
728 | if( addr == NULL ) | ||
673 | return 0; | 729 | return 0; |
674 | 730 | ||
675 | realAddr = iatAddr; | 731 | if( !is_valid_address( addr ) ) |
732 | return 0; | ||
676 | } | 733 | } |
677 | 734 | ||
678 | if( !fill_module_info( realAddr, info ) ) | 735 | if( !fill_info( hModule, addr, info ) ) |
679 | return 0; | 736 | return 0; |
680 | 737 | ||
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; | 738 | return 1; |
685 | } | 739 | } |
686 | 740 | ||
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 00b6647..47fef38 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt | |||
@@ -25,6 +25,13 @@ target_link_libraries(test-dladdr dl) | |||
25 | if(UNIX) | 25 | if(UNIX) |
26 | set_target_properties(test-dladdr PROPERTIES COMPILE_FLAGS "-Wl,--export-dynamic -fpie") | 26 | set_target_properties(test-dladdr PROPERTIES COMPILE_FLAGS "-Wl,--export-dynamic -fpie") |
27 | endif() | 27 | endif() |
28 | if(WIN32 AND NOT BUILD_SHARED_LIBS) | ||
29 | if(MSVC) | ||
30 | set_property(TARGET test-dladdr APPEND_STRING PROPERTY LINK_FLAGS "/EXPORT:dlopen /EXPORT:dladdr") | ||
31 | else() | ||
32 | set_property(TARGET test-dladdr APPEND_STRING PROPERTY LINK_FLAGS "-Wl,--export-all-symbols") | ||
33 | endif() | ||
34 | endif() | ||
28 | 35 | ||
29 | install(TARGETS test-dladdr EXPORT dlfcn-win32-targets RUNTIME DESTINATION bin) | 36 | install(TARGETS test-dladdr EXPORT dlfcn-win32-targets RUNTIME DESTINATION bin) |
30 | 37 | ||
diff --git a/tests/test-dladdr.c b/tests/test-dladdr.c index 57914cb..e2f6c01 100644 --- a/tests/test-dladdr.c +++ b/tests/test-dladdr.c | |||
@@ -1,4 +1,6 @@ | |||
1 | /* On Unix like os compile with "-Wl,--export-dynamic -fpie" (default with cmake) */ | 1 | /* On Unix like os compile with "-Wl,--export-dynamic -fpie" (default with cmake) */ |
2 | /* On Windows gcc compile with "-Wl,--export-all-symbols" (default with cmake) */ | ||
3 | /* On Windows msvc compile with "/EXPORT:dlopen /EXPORT:dladdr" (default with cmake) */ | ||
2 | 4 | ||
3 | /* required for non Windows builds, must be set in front of the first system include */ | 5 | /* required for non Windows builds, must be set in front of the first system include */ |
4 | #define _GNU_SOURCE | 6 | #define _GNU_SOURCE |
@@ -131,20 +133,11 @@ __declspec(dllimport) int __cdecl atoi(const char *_Str); | |||
131 | #endif | 133 | #endif |
132 | #endif | 134 | #endif |
133 | 135 | ||
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 | 136 | #define UNUSED(x) (void)x |
147 | 137 | ||
138 | #ifdef _WIN32 | ||
139 | __declspec(dllexport) | ||
140 | #endif | ||
148 | int main(int argc, char **argv) | 141 | int main(int argc, char **argv) |
149 | { | 142 | { |
150 | /* points to non reachable address */ | 143 | /* points to non reachable address */ |
@@ -161,10 +154,10 @@ int main(int argc, char **argv) | |||
161 | 154 | ||
162 | result = check_dladdr( "null pointer", (void*)0, NULL , NoInfo); | 155 | result = check_dladdr( "null pointer", (void*)0, NULL , NoInfo); |
163 | result |= check_dladdr( "invalid pointer", (void*)0x125, NULL , NoInfo); | 156 | result |= check_dladdr( "invalid pointer", (void*)0x125, NULL , NoInfo); |
164 | result |= check_dladdr( "function from dl library", (void*)dladdr, "dladdr" , PassOnSharedBuild ); | 157 | result |= check_dladdr( "function from dl library", (void*)dladdr, "dladdr" , Pass ); |
165 | result |= check_dladdr( "function from dl library", (void*)dlopen, "dlopen", PassOnSharedBuild ); | 158 | result |= check_dladdr( "function from dl library", (void*)dlopen, "dlopen", Pass ); |
166 | result |= check_dladdr( "function from glibc/msvcrt library", (void*)atoi, "atoi", Pass ); | 159 | result |= check_dladdr( "function from glibc/msvcrt library", (void*)atoi, "atoi", Pass ); |
167 | result |= check_dladdr( "function from executable", (void*)main, "main", FailOnWin ); | 160 | result |= check_dladdr( "function from executable", (void*)main, "main", Pass ); |
168 | result |= check_dladdr( "static function from executable", (void*)print_dl_info, "print_dl_info", Fail ); | 161 | 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 ); | 162 | 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 ); | 163 | result |= check_dladdr( "zero address from import thunk", zero_thunk_address, "", NoInfo ); |