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 |