From 86a41b921ca5c4bad1d0d7e54f9276046a25e319 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 25 Jan 2021 23:27:22 +0100 Subject: 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. --- Makefile | 4 +- src/dlfcn.c | 106 ++++++++++++++++++++++++++++++++++++++------------- tests/CMakeLists.txt | 7 ++++ tests/test-dladdr.c | 23 ++++------- 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/Makefile b/Makefile index 944358f..899334a 100644 --- a/Makefile +++ b/Makefile @@ -65,10 +65,10 @@ test-static.exe: tests/test.c $(TARGETS) $(CC) $(CFLAGS) -o $@ $< libdl.a test-dladdr.exe: tests/test-dladdr.c $(TARGETS) - $(CC) $(CFLAGS) -DDLFCN_WIN32_SHARED -o $@ $< libdl.dll.a + $(CC) $(CFLAGS) -Wl,--export-all-symbols -DDLFCN_WIN32_SHARED -o $@ $< libdl.dll.a test-dladdr-static.exe: tests/test-dladdr.c $(TARGETS) - $(CC) $(CFLAGS) -o $@ $< libdl.a + $(CC) $(CFLAGS) -Wl,--export-all-symbols -o $@ $< libdl.a testdll.dll: tests/testdll.c $(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 return TRUE; } -/* Return symbol name for a given address */ -static const char *get_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address ) +/* Return symbol name for a given address from import table */ +static const char *get_import_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address ) { int i; void *candidateAddr = NULL; @@ -545,6 +545,40 @@ static const char *get_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid return candidateName; } +/* Return symbol name for a given address from export table */ +static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, void *addr, void **func_address ) +{ + DWORD i; + void *candidateAddr = NULL; + int candidateIndex = -1; + BYTE *base = (BYTE *) module; + DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions); + DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames); + USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals); + + for( i = 0; i < ied->NumberOfFunctions; i++ ) + { + if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) ) + continue; + + candidateAddr = (void *) ( base + functionAddressesOffsets[i] ); + candidateIndex = i; + } + + if( candidateIndex == -1 ) + return NULL; + + *func_address = candidateAddr; + + for( i = 0; i < ied->NumberOfNames; i++ ) + { + if( functionNameOrdinalsIndexes[i] == candidateIndex ) + return (const char *) ( base + functionNamesOffsets[i] ); + } + + return NULL; +} + static BOOL is_valid_address( void *addr ) { MEMORY_BASIC_INFORMATION info; @@ -608,11 +642,16 @@ static void *get_address_from_import_address_table( void *iat, DWORD iat_size, v /* Holds module filename */ static char module_filename[2*MAX_PATH]; -static BOOL fill_module_info( void *addr, Dl_info *info ) +static BOOL fill_info( HMODULE hModuleImport, void *addr, Dl_info *info ) { HMODULE hModule; DWORD dwSize; + IMAGE_EXPORT_DIRECTORY *ied; + IMAGE_IMPORT_DESCRIPTOR *iid; + const char *name; + void *funcAddress = NULL; + /* Get module of the specified address */ if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL ) return FALSE; @@ -621,8 +660,27 @@ static BOOL fill_module_info( void *addr, Dl_info *info ) if( dwSize == 0 || dwSize == sizeof( module_filename ) ) return FALSE; - info->dli_fbase = (void *) hModule; info->dli_fname = module_filename; + info->dli_fbase = (void *) hModule; + info->dli_sname = NULL; + + /* First try to find function name and function address in module's export table */ + if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) ) + info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress ); + + /* If symbol name is not known and we know which module is importing this address + * then try to find symbol name in this module's import table as the last resort. */ + if( info->dli_sname == NULL && hModuleImport != NULL ) + { + if( get_image_section( hModuleImport, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, NULL ) ) + { + name = get_import_symbol_name( hModuleImport, iid, addr, &funcAddress ); + if( name != NULL ) + info->dli_sname = name; + } + } + + info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : addr; return TRUE; } @@ -630,36 +688,33 @@ static BOOL fill_module_info( void *addr, Dl_info *info ) DLFCN_EXPORT int dladdr( void *addr, Dl_info *info ) { - void *realAddr, *funcAddress = NULL; - HMODULE hModule; - IMAGE_IMPORT_DESCRIPTOR *iid; - DWORD iidSize = 0; + HMODULE hModule = NULL; if( addr == NULL || info == NULL ) return 0; - hModule = GetModuleHandleA( NULL ); - if( hModule == NULL ) - return 0; - if( !is_valid_address( addr ) ) return 0; - realAddr = addr; - - if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) ) - return 0; - if( is_import_thunk( addr ) ) { void *iat; - void *iatAddr = NULL; - DWORD iatSize = 0; + DWORD iatSize; + + /* Get module of the import thunk address */ + if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL ) + return 0; if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) ) { /* Fallback for cases where the iat is not defined, * for example i586-mingw32msvc-gcc */ + IMAGE_IMPORT_DESCRIPTOR *iid; + DWORD iidSize; + + if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) ) + return 0; + if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 ) return 0; @@ -668,19 +723,18 @@ int dladdr( void *addr, Dl_info *info ) iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid ); } - iatAddr = get_address_from_import_address_table( iat, iatSize, addr ); - if( iatAddr == NULL ) + addr = get_address_from_import_address_table( iat, iatSize, addr ); + + if( addr == NULL ) return 0; - realAddr = iatAddr; + if( !is_valid_address( addr ) ) + return 0; } - if( !fill_module_info( realAddr, info ) ) + if( !fill_info( hModule, addr, info ) ) return 0; - info->dli_sname = get_symbol_name( hModule, iid, realAddr, &funcAddress ); - - info->dli_saddr = !info->dli_sname ? NULL : funcAddress ? funcAddress : realAddr; return 1; } 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) if(UNIX) set_target_properties(test-dladdr PROPERTIES COMPILE_FLAGS "-Wl,--export-dynamic -fpie") endif() +if(WIN32 AND NOT BUILD_SHARED_LIBS) + if(MSVC) + set_property(TARGET test-dladdr APPEND_STRING PROPERTY LINK_FLAGS "/EXPORT:dlopen /EXPORT:dladdr") + else() + set_property(TARGET test-dladdr APPEND_STRING PROPERTY LINK_FLAGS "-Wl,--export-all-symbols") + endif() +endif() install(TARGETS test-dladdr EXPORT dlfcn-win32-targets RUNTIME DESTINATION bin) 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 @@ /* On Unix like os compile with "-Wl,--export-dynamic -fpie" (default with cmake) */ +/* On Windows gcc compile with "-Wl,--export-all-symbols" (default with cmake) */ +/* On Windows msvc compile with "/EXPORT:dlopen /EXPORT:dladdr" (default with cmake) */ /* required for non Windows builds, must be set in front of the first system include */ #define _GNU_SOURCE @@ -131,20 +133,11 @@ __declspec(dllimport) int __cdecl atoi(const char *_Str); #endif #endif -#ifdef _WIN32 -#define FailOnWin Fail -#else -#define FailOnWin Pass -#endif - -#if defined(_WIN32) && !defined(DLFCN_WIN32_SHARED) -#define PassOnSharedBuild Fail -#else -#define PassOnSharedBuild Pass -#endif - #define UNUSED(x) (void)x +#ifdef _WIN32 +__declspec(dllexport) +#endif int main(int argc, char **argv) { /* points to non reachable address */ @@ -161,10 +154,10 @@ int main(int argc, char **argv) result = check_dladdr( "null pointer", (void*)0, NULL , NoInfo); result |= check_dladdr( "invalid pointer", (void*)0x125, NULL , NoInfo); - result |= check_dladdr( "function from dl library", (void*)dladdr, "dladdr" , PassOnSharedBuild ); - result |= check_dladdr( "function from dl library", (void*)dlopen, "dlopen", PassOnSharedBuild ); + result |= check_dladdr( "function from dl library", (void*)dladdr, "dladdr" , Pass ); + result |= check_dladdr( "function from dl library", (void*)dlopen, "dlopen", Pass ); result |= check_dladdr( "function from glibc/msvcrt library", (void*)atoi, "atoi", Pass ); - result |= check_dladdr( "function from executable", (void*)main, "main", FailOnWin ); + result |= check_dladdr( "function from executable", (void*)main, "main", Pass ); result |= check_dladdr( "static function from executable", (void*)print_dl_info, "print_dl_info", Fail ); result |= check_dladdr( "address with positive offset", ((char*)atoi)+1, "atoi", PassWithDifferentAddress ); result |= check_dladdr( "zero address from import thunk", zero_thunk_address, "", NoInfo ); -- cgit v1.2.3-55-g6feb