diff options
| author | Ralf Habacker <ralf.habacker@freenet.de> | 2020-12-11 14:32:52 +0100 |
|---|---|---|
| committer | Ralf Habacker <ralf.habacker@freenet.de> | 2021-01-17 12:51:39 +0100 |
| commit | 0ea2334d46ca84c1a27bd086700f620a06401f94 (patch) | |
| tree | 89e768d6c77492dee9cf6b520d562b8e35a7cee5 /src | |
| parent | 06ffb62a31f8986d1cda864bb8c3a8967d5a65fa (diff) | |
| download | dlfcn-win32-0ea2334d46ca84c1a27bd086700f620a06401f94.tar.gz dlfcn-win32-0ea2334d46ca84c1a27bd086700f620a06401f94.tar.bz2 dlfcn-win32-0ea2334d46ca84c1a27bd086700f620a06401f94.zip | |
Add function dladdr() and associated test application test-dladdr
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | src/dlfcn.c | 214 | ||||
| -rw-r--r-- | src/dlfcn.h | 16 |
3 files changed, 236 insertions, 5 deletions
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 @@ | |||
| 1 | set(headers dlfcn.h) | 1 | set(headers dlfcn.h) |
| 2 | set(sources dlfcn.c) | 2 | set(sources dlfcn.c) |
| 3 | 3 | ||
| 4 | if (BUILD_SHARED_LIBS) | ||
| 5 | add_definitions(-DSHARED) | ||
| 6 | endif (BUILD_SHARED_LIBS) | ||
| 7 | 4 | ||
| 8 | add_library(dl ${sources}) | 5 | add_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 |
| 11 | target_include_directories(dl INTERFACE $<INSTALL_INTERFACE:include>) | 8 | target_include_directories(dl INTERFACE $<INSTALL_INTERFACE:include>) |
| 12 | 9 | ||
| 10 | # dot not add -D<target>_EXPORTS | ||
| 11 | set_target_properties(dl PROPERTIES DEFINE_SYMBOL "") | ||
| 12 | |||
| 13 | # set shared mode for compiling library and propagate mode to cmake clients | ||
| 14 | if (BUILD_SHARED_LIBS) | ||
| 15 | target_compile_definitions(dl PUBLIC DLFCN_WIN32_SHARED) | ||
| 16 | endif (BUILD_SHARED_LIBS) | ||
| 17 | |||
| 13 | install (TARGETS dl EXPORT dlfcn-win32-targets | 18 | install (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 | ||
| 226 | DLFCN_EXPORT | ||
| 225 | void *dlopen( const char *file, int mode ) | 227 | void *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 | ||
| 334 | DLFCN_EXPORT | ||
| 332 | int dlclose( void *handle ) | 335 | int 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() */ |
| 359 | DLFCN_EXPORT | ||
| 356 | void *dlsym( void *handle, const char *name ) | 360 | void *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 | ||
| 467 | DLFCN_EXPORT | ||
| 463 | char *dlerror( void ) | 468 | char *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 */ | ||
| 486 | static 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 */ | ||
| 516 | static 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 | |||
| 548 | static 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 | */ | ||
| 572 | static 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 | */ | ||
| 580 | static 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 */ | ||
| 609 | static char module_filename[2*MAX_PATH]; | ||
| 610 | |||
| 611 | static 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 | |||
| 630 | DLFCN_EXPORT | ||
| 631 | int 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 | ||
| 478 | BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) | 688 | BOOL 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 @@ | |||
| 28 | extern "C" { | 28 | extern "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() */ | ||
| 67 | typedef 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. */ |
| 63 | DLFCN_EXPORT void *dlopen(const char *file, int mode); | 76 | DLFCN_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. */ |
| 72 | DLFCN_EXPORT char *dlerror(void); | 85 | DLFCN_EXPORT char *dlerror(void); |
| 73 | 86 | ||
| 87 | /* Translate address to symbolic information (no POSIX standard) */ | ||
| 88 | DLFCN_EXPORT int dladdr(void *addr, Dl_info *info); | ||
| 89 | |||
| 74 | #ifdef __cplusplus | 90 | #ifdef __cplusplus |
| 75 | } | 91 | } |
| 76 | #endif | 92 | #endif |
