aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRalf Habacker <ralf.habacker@freenet.de>2020-12-11 14:32:52 +0100
committerRalf Habacker <ralf.habacker@freenet.de>2021-01-17 12:51:39 +0100
commit0ea2334d46ca84c1a27bd086700f620a06401f94 (patch)
tree89e768d6c77492dee9cf6b520d562b8e35a7cee5 /src
parent06ffb62a31f8986d1cda864bb8c3a8967d5a65fa (diff)
downloaddlfcn-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.txt11
-rw-r--r--src/dlfcn.c214
-rw-r--r--src/dlfcn.h16
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 @@
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