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 | |
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
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | src/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/dlfcn.c | 214 | ||||
-rw-r--r-- | src/dlfcn.h | 16 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 8 | ||||
-rw-r--r-- | tests/test-dladdr.c | 184 |
6 files changed, 439 insertions, 9 deletions
@@ -6,14 +6,14 @@ CFLAGS = -Wall -O3 -fomit-frame-pointer -Isrc | |||
6 | 6 | ||
7 | ifeq ($(BUILD_SHARED),yes) | 7 | ifeq ($(BUILD_SHARED),yes) |
8 | TARGETS += libdl.dll | 8 | TARGETS += libdl.dll |
9 | SHFLAGS += -Wl,--out-implib,libdl.dll.a -DSHARED | 9 | SHFLAGS += -Wl,--out-implib,libdl.dll.a |
10 | INSTALL += shared-install | 10 | INSTALL += shared-install |
11 | TESTS += test.exe | 11 | TESTS += test.exe test-dladdr.exe |
12 | endif | 12 | endif |
13 | ifeq ($(BUILD_STATIC),yes) | 13 | ifeq ($(BUILD_STATIC),yes) |
14 | TARGETS += libdl.a | 14 | TARGETS += libdl.a |
15 | INSTALL += static-install | 15 | INSTALL += static-install |
16 | TESTS += test-static.exe | 16 | TESTS += test-static.exe test-dladdr-static.exe |
17 | endif | 17 | endif |
18 | ifeq ($(BUILD_MSVC),yes) | 18 | ifeq ($(BUILD_MSVC),yes) |
19 | TARGETS += libdl.lib | 19 | TARGETS += libdl.lib |
@@ -32,7 +32,7 @@ libdl.a: $(SOURCES) | |||
32 | $(RANLIB) $@ | 32 | $(RANLIB) $@ |
33 | 33 | ||
34 | libdl.dll: $(SOURCES) | 34 | libdl.dll: $(SOURCES) |
35 | $(CC) $(CFLAGS) $(SHFLAGS) -shared -o $@ $^ | 35 | $(CC) $(CFLAGS) $(SHFLAGS) -DDLFCN_WIN32_SHARED -shared -o $@ $^ |
36 | 36 | ||
37 | libdl.lib: libdl.dll | 37 | libdl.lib: libdl.dll |
38 | $(LIBCMD) /machine:i386 /def:libdl.def | 38 | $(LIBCMD) /machine:i386 /def:libdl.def |
@@ -64,6 +64,12 @@ test.exe: tests/test.c $(TARGETS) | |||
64 | test-static.exe: tests/test.c $(TARGETS) | 64 | 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) | ||
68 | $(CC) $(CFLAGS) -DDLFCN_WIN32_SHARED -o $@ $< libdl.dll.a | ||
69 | |||
70 | test-dladdr-static.exe: tests/test-dladdr.c $(TARGETS) | ||
71 | $(CC) $(CFLAGS) -o $@ $< libdl.a | ||
72 | |||
67 | testdll.dll: tests/testdll.c | 73 | testdll.dll: tests/testdll.c |
68 | $(CC) $(CFLAGS) -shared -o $@ $^ | 74 | $(CC) $(CFLAGS) -shared -o $@ $^ |
69 | 75 | ||
@@ -81,6 +87,7 @@ clean:: | |||
81 | src/dlfcn.o \ | 87 | src/dlfcn.o \ |
82 | libdl.dll libdl.a libdl.def libdl.dll.a libdl.lib libdl.exp \ | 88 | libdl.dll libdl.a libdl.def libdl.dll.a libdl.lib libdl.exp \ |
83 | tmptest.c tmptest.dll \ | 89 | tmptest.c tmptest.dll \ |
90 | test-dladdr.exe test-dladdr-static.exe \ | ||
84 | test.exe test-static.exe testdll.dll testdll2.dll testdll3.dll | 91 | test.exe test-static.exe testdll.dll testdll2.dll testdll3.dll |
85 | 92 | ||
86 | distclean: clean | 93 | distclean: clean |
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 |
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5fc6c44..f0871e5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt | |||
@@ -9,9 +9,17 @@ target_link_libraries(testdll2 dl) | |||
9 | 9 | ||
10 | add_library(testdll3 SHARED testdll3.c) | 10 | add_library(testdll3 SHARED testdll3.c) |
11 | set_target_properties(testdll3 PROPERTIES PREFIX "") | 11 | set_target_properties(testdll3 PROPERTIES PREFIX "") |
12 | add_executable(test-dladdr test-dladdr.c) | ||
13 | target_link_libraries(test-dladdr dl) | ||
14 | if(UNIX) | ||
15 | set_target_properties(test-dladdr PROPERTIES COMPILE_FLAGS "-Wl,--export-dynamic -fpie") | ||
16 | endif() | ||
17 | |||
18 | install(TARGETS test-dladdr EXPORT dlfcn-win32-targets RUNTIME DESTINATION bin) | ||
12 | 19 | ||
13 | add_executable(t_dlfcn test.c) | 20 | add_executable(t_dlfcn test.c) |
14 | target_link_libraries(t_dlfcn dl) | 21 | target_link_libraries(t_dlfcn dl) |
15 | if(RUN_TESTS) | 22 | if(RUN_TESTS) |
16 | add_test(NAME t_dlfcn COMMAND ${WRAPPER} $<TARGET_FILE:t_dlfcn> WORKING_DIRECTORY $<TARGET_FILE_DIR:t_dlfcn>) | 23 | add_test(NAME t_dlfcn COMMAND ${WRAPPER} $<TARGET_FILE:t_dlfcn> WORKING_DIRECTORY $<TARGET_FILE_DIR:t_dlfcn>) |
24 | add_test(NAME test-dladdr COMMAND ${WRAPPER} $<TARGET_FILE:test-dladdr> WORKING_DIRECTORY $<TARGET_FILE_DIR:test-dladdr>) | ||
17 | endif() | 25 | endif() |
diff --git a/tests/test-dladdr.c b/tests/test-dladdr.c new file mode 100644 index 0000000..57914cb --- /dev/null +++ b/tests/test-dladdr.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* On Unix like os compile with "-Wl,--export-dynamic -fpie" (default with cmake) */ | ||
2 | |||
3 | /* required for non Windows builds, must be set in front of the first system include */ | ||
4 | #define _GNU_SOURCE | ||
5 | |||
6 | #include <dlfcn.h> | ||
7 | |||
8 | #include <stdio.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <string.h> | ||
11 | |||
12 | static int verbose = 0; | ||
13 | |||
14 | typedef enum { | ||
15 | Pass = 1, | ||
16 | PassWithoutSymbol = 2, | ||
17 | PassWithDifferentAddress = 3, | ||
18 | Fail = 0, | ||
19 | NoInfo = -1, | ||
20 | } ExpectedResult; | ||
21 | |||
22 | typedef void (* func) (void); | ||
23 | |||
24 | static void print_dl_info( Dl_info *info, char *prefix, char *suffix ) | ||
25 | { | ||
26 | printf( "%sfilename: %s base: %p symbol name: '%s' addr: %p%s", prefix, info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr, suffix ); | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * @brief check information returned by dladdr | ||
31 | * @param hint text describing what to test | ||
32 | * @param addr address to check | ||
33 | * @param addrsym | ||
34 | * @param expected check against expected result | ||
35 | * @return 0 check passed | ||
36 | * @return 1 check failed | ||
37 | */ | ||
38 | static int check_dladdr( const char *hint, void *addr, char *addrsym, ExpectedResult expected_result ) | ||
39 | { | ||
40 | Dl_info info; | ||
41 | int result = dladdr( addr, &info ); | ||
42 | int passed = 0; | ||
43 | if (!result) | ||
44 | { | ||
45 | passed = expected_result == NoInfo || expected_result == Fail; | ||
46 | } | ||
47 | else | ||
48 | { | ||
49 | int sym_match = info.dli_sname && strcmp( addrsym, info.dli_sname ) == 0; | ||
50 | int addr_match = addr == info.dli_saddr; | ||
51 | passed = (expected_result == Pass && sym_match && addr_match) | ||
52 | || (expected_result == PassWithoutSymbol && addr_match && !info.dli_sname) | ||
53 | || (expected_result == PassWithDifferentAddress && sym_match && !addr_match) | ||
54 | || (expected_result == Fail && (!sym_match || !addr_match)); | ||
55 | } | ||
56 | printf( "checking '%s' - address %p which should have symbol '%s' -> %s%s", hint, addr, addrsym, passed ? "passed" : "failed", verbose || !passed ? " " : "\n" ); | ||
57 | if( verbose || !passed ) | ||
58 | { | ||
59 | if( !result ) | ||
60 | printf( "(could not get symbol information for address %p)\n", addr ); | ||
61 | else | ||
62 | print_dl_info( &info, "(", ")\n"); | ||
63 | } | ||
64 | return !passed; | ||
65 | } | ||
66 | |||
67 | #ifdef _WIN32 | ||
68 | /** | ||
69 | * @brief check address from a symbol located in a shared lilbrary | ||
70 | * @param hint text describing what to test | ||
71 | * @param libname libray to get the address from | ||
72 | * @param addrsym symbol to get the address for | ||
73 | * @param should_match result should match the given values | ||
74 | * @return 0 check passed | ||
75 | * @return 1 check failed | ||
76 | * @return 2 failed to open library | ||
77 | * @return 3 failed to get symbol address | ||
78 | */ | ||
79 | static int check_dladdr_by_dlopen( const char *hint, char *libname, char *sym, int should_match ) | ||
80 | { | ||
81 | void *library = NULL; | ||
82 | void *addr = NULL; | ||
83 | int result; | ||
84 | |||
85 | library = dlopen( libname, RTLD_GLOBAL ); | ||
86 | if ( library == NULL ) | ||
87 | { | ||
88 | fprintf( stderr, "could not open '%s'\n", libname ); | ||
89 | return 2; | ||
90 | } | ||
91 | |||
92 | addr = dlsym( library, sym ); | ||
93 | if ( !addr ) { | ||
94 | fprintf( stderr, "could not get address from library '%s' for symbol '%s'\n", libname, sym ); | ||
95 | return 3; | ||
96 | } | ||
97 | |||
98 | result = check_dladdr( hint, addr, sym, should_match ); | ||
99 | dlclose( library ); | ||
100 | |||
101 | return result; | ||
102 | } | ||
103 | #endif | ||
104 | |||
105 | #ifdef _WIN32 | ||
106 | #include <windows.h> | ||
107 | #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | ||
108 | /* hide warning "reclared without 'dllimport' attribute" */ | ||
109 | #pragma GCC diagnostic push | ||
110 | #pragma GCC diagnostic ignored "-Wattributes" | ||
111 | #elif defined(_MSC_VER) | ||
112 | /* disable warning C4273 inconsistent dll linkage */ | ||
113 | #pragma warning(push) | ||
114 | #pragma warning(disable: 4273) | ||
115 | #endif | ||
116 | /* | ||
117 | * Windows API functions decorated with WINBASEAPI are imported from iat. | ||
118 | * For testing force linking by import thunk for the following functions | ||
119 | */ | ||
120 | HMODULE WINAPI GetModuleHandleA (LPCSTR lpModuleName); | ||
121 | SIZE_T WINAPI VirtualQuery (LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength); | ||
122 | /* force linking by iat */ | ||
123 | /* WINBASEAPI, which is normally used here, can be overriden by compiler or application, so we cannot use it */ | ||
124 | __declspec(dllimport) HMODULE WINAPI LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); | ||
125 | __declspec(dllimport) int __cdecl atoi(const char *_Str); | ||
126 | |||
127 | #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) | ||
128 | #pragma GCC diagnostic pop | ||
129 | #elif defined(_MSC_VER) | ||
130 | #pragma warning(pop) | ||
131 | #endif | ||
132 | #endif | ||
133 | |||
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 | ||
147 | |||
148 | int main(int argc, char **argv) | ||
149 | { | ||
150 | /* points to non reachable address */ | ||
151 | unsigned char zero_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; | ||
152 | /* points to executable base */ | ||
153 | unsigned char invalid_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x40, 0x00 }; | ||
154 | /* no import thunk */ | ||
155 | unsigned char no_import_thunk[6] = { 0xFF, 0x26, 0x00, 0x00, 0x40, 0x00 }; | ||
156 | int result = 0; | ||
157 | UNUSED(argv); | ||
158 | |||
159 | if (argc == 2) | ||
160 | verbose = 1; | ||
161 | |||
162 | result = check_dladdr( "null pointer", (void*)0, NULL , NoInfo); | ||
163 | result |= check_dladdr( "invalid pointer", (void*)0x125, NULL , NoInfo); | ||
164 | result |= check_dladdr( "function from dl library", (void*)dladdr, "dladdr" , PassOnSharedBuild ); | ||
165 | result |= check_dladdr( "function from dl library", (void*)dlopen, "dlopen", PassOnSharedBuild ); | ||
166 | result |= check_dladdr( "function from glibc/msvcrt library", (void*)atoi, "atoi", Pass ); | ||
167 | result |= check_dladdr( "function from executable", (void*)main, "main", FailOnWin ); | ||
168 | 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 ); | ||
170 | result |= check_dladdr( "zero address from import thunk", zero_thunk_address, "", NoInfo ); | ||
171 | result |= check_dladdr( "invalid address from import thunk", invalid_thunk_address, "", NoInfo ); | ||
172 | result |= check_dladdr( "no import thunk", no_import_thunk, "", NoInfo ); | ||
173 | |||
174 | #ifdef _WIN32 | ||
175 | result |= check_dladdr( "last entry in iat", (void*)VirtualQuery, "VirtualQuery", PassWithDifferentAddress ); | ||
176 | |||
177 | result |= check_dladdr ( "address through import thunk", (void*)GetModuleHandleA, "GetModuleHandleA", PassWithDifferentAddress ); | ||
178 | result |= check_dladdr_by_dlopen( "address by dlsym", "kernel32.dll", "GetModuleHandleA", Pass ); | ||
179 | |||
180 | result |= check_dladdr ( "address by image allocation table", (void*)LoadLibraryExA, "LoadLibraryExA", Pass ); | ||
181 | result |= check_dladdr_by_dlopen( "address by dlsym", "kernel32.dll", "LoadLibraryExA", Pass ); | ||
182 | #endif | ||
183 | return result; | ||
184 | } | ||