2022-10-28 16:15:30 +02:00
/*
* dlfcn - win32
* Copyright ( c ) 2007 Ramiro Polla
* Copyright ( c ) 2015 Tiancheng " Timothy " Gu
* Copyright ( c ) 2019 Pali Rohár < pali . rohar @ gmail . com >
* Copyright ( c ) 2020 Ralf Habacker < ralf . habacker @ freenet . de >
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
*/
# ifdef _DEBUG
# define _CRTDBG_MAP_ALLOC
# include <stdlib.h>
# include <crtdbg.h>
# endif
# include <windows.h>
# include <stdio.h>
# include <stdlib.h>
/* Older versions do not have this type */
# if _WIN32_WINNT < 0x0500
typedef ULONG ULONG_PTR ;
# endif
/* Older SDK versions do not have these macros */
# ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
# define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4
# endif
# ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
# define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2
# endif
# ifdef _MSC_VER
/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
2026-05-23 05:58:12 +02:00
extern " C " void * _ReturnAddress ( void ) ;
2022-10-28 16:15:30 +02:00
# pragma intrinsic( _ReturnAddress )
# else
/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
# ifndef _ReturnAddress
# define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) )
# endif
# endif
# ifdef DLFCN_WIN32_SHARED
# define DLFCN_WIN32_EXPORTS
# endif
# include "dlfcn.h"
# if defined( _MSC_VER ) && _MSC_VER >= 1300
/* https://docs.microsoft.com/en-us/cpp/cpp/noinline */
# define DLFCN_NOINLINE __declspec( noinline )
# elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) )
/* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */
# define DLFCN_NOINLINE __attribute__(( noinline ))
# else
# define DLFCN_NOINLINE
# endif
/* Note:
* MSDN says these functions are not thread - safe . We make no efforts to have
* any kind of thread safety .
*/
typedef struct local_object {
HMODULE hModule ;
struct local_object * previous ;
struct local_object * next ;
} local_object ;
static local_object first_object ;
/* These functions implement a double linked list for the local objects. */
static local_object * local_search ( HMODULE hModule )
{
local_object * pobject ;
if ( hModule = = NULL )
return NULL ;
for ( pobject = & first_object ; pobject ; pobject = pobject - > next )
if ( pobject - > hModule = = hModule )
return pobject ;
return NULL ;
}
static BOOL local_add ( HMODULE hModule )
{
local_object * pobject ;
local_object * nobject ;
if ( hModule = = NULL )
return TRUE ;
pobject = local_search ( hModule ) ;
/* Do not add object again if it's already on the list */
if ( pobject ! = NULL )
return TRUE ;
for ( pobject = & first_object ; pobject - > next ; pobject = pobject - > next ) ;
nobject = ( local_object * ) malloc ( sizeof ( local_object ) ) ;
if ( ! nobject )
return FALSE ;
pobject - > next = nobject ;
nobject - > next = NULL ;
nobject - > previous = pobject ;
nobject - > hModule = hModule ;
return TRUE ;
}
static void local_rem ( HMODULE hModule )
{
local_object * pobject ;
if ( hModule = = NULL )
return ;
pobject = local_search ( hModule ) ;
if ( pobject = = NULL )
return ;
if ( pobject - > next )
pobject - > next - > previous = pobject - > previous ;
if ( pobject - > previous )
pobject - > previous - > next = pobject - > next ;
free ( pobject ) ;
}
/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
* static buffer .
* MSDN says the buffer cannot be larger than 64 K bytes , so we set it to
* the limit .
*/
static char error_buffer [ 65535 ] ;
static BOOL error_occurred ;
static void save_err_str ( const char * str , DWORD dwMessageId )
{
DWORD ret ;
size_t pos , len ;
len = strlen ( str ) ;
if ( len > sizeof ( error_buffer ) - 5 )
len = sizeof ( error_buffer ) - 5 ;
/* Format error message to:
* " <argument to function that failed> " : < Windows localized error message >
*/
pos = 0 ;
error_buffer [ pos + + ] = ' " ' ;
memcpy ( error_buffer + pos , str , len ) ;
pos + = len ;
error_buffer [ pos + + ] = ' " ' ;
error_buffer [ pos + + ] = ' : ' ;
error_buffer [ pos + + ] = ' ' ;
ret = FormatMessageA ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS , NULL , dwMessageId ,
MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) ,
error_buffer + pos , ( DWORD ) ( sizeof ( error_buffer ) - pos ) , NULL ) ;
pos + = ret ;
/* When FormatMessageA() fails it returns zero and does not touch buffer
* so add trailing null byte */
if ( ret = = 0 )
error_buffer [ pos ] = ' \0 ' ;
if ( pos > 1 )
{
/* POSIX says the string must not have trailing <newline> */
if ( error_buffer [ pos - 2 ] = = ' \r ' & & error_buffer [ pos - 1 ] = = ' \n ' )
error_buffer [ pos - 2 ] = ' \0 ' ;
}
error_occurred = TRUE ;
}
static void save_err_ptr_str ( const void * ptr , DWORD dwMessageId )
{
char ptr_buf [ 2 + 2 * sizeof ( ptr ) + 1 ] ;
char num ;
size_t i ;
ptr_buf [ 0 ] = ' 0 ' ;
ptr_buf [ 1 ] = ' x ' ;
for ( i = 0 ; i < 2 * sizeof ( ptr ) ; i + + )
{
num = ( char ) ( ( ( ( ULONG_PTR ) ptr ) > > ( 8 * sizeof ( ptr ) - 4 * ( i + 1 ) ) ) & 0xF ) ;
ptr_buf [ 2 + i ] = num + ( ( num < 0xA ) ? ' 0 ' : ( ' A ' - 0xA ) ) ;
}
ptr_buf [ 2 + 2 * sizeof ( ptr ) ] = 0 ;
save_err_str ( ptr_buf , dwMessageId ) ;
}
static UINT MySetErrorMode ( UINT uMode )
{
static BOOL ( WINAPI * SetThreadErrorModePtr ) ( DWORD , DWORD * ) = NULL ;
static BOOL failed = FALSE ;
HMODULE kernel32 ;
DWORD oldMode ;
if ( ! failed & & SetThreadErrorModePtr = = NULL )
{
kernel32 = GetModuleHandleA ( " Kernel32.dll " ) ;
if ( kernel32 ! = NULL )
SetThreadErrorModePtr = ( BOOL ( WINAPI * ) ( DWORD , DWORD * ) ) ( LPVOID ) GetProcAddress ( kernel32 , " SetThreadErrorMode " ) ;
if ( SetThreadErrorModePtr = = NULL )
failed = TRUE ;
}
if ( ! failed )
{
if ( ! SetThreadErrorModePtr ( uMode , & oldMode ) )
return 0 ;
else
return oldMode ;
}
else
{
return SetErrorMode ( uMode ) ;
}
}
static HMODULE MyGetModuleHandleFromAddress ( const void * addr )
{
static BOOL ( WINAPI * GetModuleHandleExAPtr ) ( DWORD , LPCSTR , HMODULE * ) = NULL ;
static BOOL failed = FALSE ;
HMODULE kernel32 ;
HMODULE hModule ;
MEMORY_BASIC_INFORMATION info ;
SIZE_T sLen ;
if ( ! failed & & GetModuleHandleExAPtr = = NULL )
{
kernel32 = GetModuleHandleA ( " Kernel32.dll " ) ;
if ( kernel32 ! = NULL )
GetModuleHandleExAPtr = ( BOOL ( WINAPI * ) ( DWORD , LPCSTR , HMODULE * ) ) ( LPVOID ) GetProcAddress ( kernel32 , " GetModuleHandleExA " ) ;
if ( GetModuleHandleExAPtr = = NULL )
failed = TRUE ;
}
if ( ! failed )
{
/* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */
if ( ! GetModuleHandleExAPtr ( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT , ( const char * ) addr , & hModule ) )
return NULL ;
}
else
{
/* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380
* The HMODULE of a DLL is the same value as the module ' s base address .
*/
sLen = VirtualQuery ( addr , & info , sizeof ( info ) ) ;
if ( sLen ! = sizeof ( info ) )
return NULL ;
hModule = ( HMODULE ) info . AllocationBase ;
}
return hModule ;
}
/* Load Psapi.dll at runtime, this avoids linking caveat */
static BOOL MyEnumProcessModules ( HANDLE hProcess , HMODULE * lphModule , DWORD cb , LPDWORD lpcbNeeded )
{
static BOOL ( WINAPI * EnumProcessModulesPtr ) ( HANDLE , HMODULE * , DWORD , LPDWORD ) = NULL ;
static BOOL failed = FALSE ;
UINT uMode ;
HMODULE psapi ;
if ( failed )
return FALSE ;
if ( EnumProcessModulesPtr = = NULL )
{
/* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */
psapi = GetModuleHandleA ( " Kernel32.dll " ) ;
if ( psapi ! = NULL )
EnumProcessModulesPtr = ( BOOL ( WINAPI * ) ( HANDLE , HMODULE * , DWORD , LPDWORD ) ) ( LPVOID ) GetProcAddress ( psapi , " K32EnumProcessModules " ) ;
/* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */
if ( EnumProcessModulesPtr = = NULL )
{
/* Do not let Windows display the critical-error-handler message box */
uMode = MySetErrorMode ( SEM_FAILCRITICALERRORS ) ;
psapi = LoadLibraryA ( " Psapi.dll " ) ;
if ( psapi ! = NULL )
{
EnumProcessModulesPtr = ( BOOL ( WINAPI * ) ( HANDLE , HMODULE * , DWORD , LPDWORD ) ) ( LPVOID ) GetProcAddress ( psapi , " EnumProcessModules " ) ;
if ( EnumProcessModulesPtr = = NULL )
FreeLibrary ( psapi ) ;
}
MySetErrorMode ( uMode ) ;
}
if ( EnumProcessModulesPtr = = NULL )
{
failed = TRUE ;
return FALSE ;
}
}
return EnumProcessModulesPtr ( hProcess , lphModule , cb , lpcbNeeded ) ;
}
DLFCN_EXPORT
void * dlopen ( const char * file , int mode )
{
HMODULE hModule ;
UINT uMode ;
error_occurred = FALSE ;
/* Do not let Windows display the critical-error-handler message box */
uMode = MySetErrorMode ( SEM_FAILCRITICALERRORS ) ;
if ( file = = NULL )
{
/* POSIX says that if the value of file is NULL, a handle on a global
* symbol object must be provided . That object must be able to access
* all symbols from the original program file , and any objects loaded
* with the RTLD_GLOBAL flag .
* The return value from GetModuleHandle ( ) allows us to retrieve
* symbols only from the original program file . EnumProcessModules ( ) is
* used to access symbols from other libraries . For objects loaded
* with the RTLD_LOCAL flag , we create our own list later on . They are
* excluded from EnumProcessModules ( ) iteration .
*/
hModule = GetModuleHandle ( NULL ) ;
if ( ! hModule )
save_err_str ( " (null) " , GetLastError ( ) ) ;
}
else
{
HANDLE hCurrentProc ;
DWORD dwProcModsBefore , dwProcModsAfter ;
char lpFileName [ MAX_PATH ] ;
size_t i , len ;
len = strlen ( file ) ;
if ( len > = sizeof ( lpFileName ) )
{
save_err_str ( file , ERROR_FILENAME_EXCED_RANGE ) ;
hModule = NULL ;
}
else
{
/* MSDN says backslashes *must* be used instead of forward slashes. */
for ( i = 0 ; i < len ; i + + )
{
if ( file [ i ] = = ' / ' )
lpFileName [ i ] = ' \\ ' ;
else
lpFileName [ i ] = file [ i ] ;
}
lpFileName [ len ] = ' \0 ' ;
hCurrentProc = GetCurrentProcess ( ) ;
if ( MyEnumProcessModules ( hCurrentProc , NULL , 0 , & dwProcModsBefore ) = = 0 )
dwProcModsBefore = 0 ;
/* POSIX says the search path is implementation-defined.
* LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
* to UNIX ' s search paths ( start with system folders instead of current
* folder ) .
*/
hModule = LoadLibraryExA ( lpFileName , NULL , LOAD_WITH_ALTERED_SEARCH_PATH ) ;
if ( ! hModule )
{
save_err_str ( lpFileName , GetLastError ( ) ) ;
}
else
{
if ( MyEnumProcessModules ( hCurrentProc , NULL , 0 , & dwProcModsAfter ) = = 0 )
dwProcModsAfter = 0 ;
/* If the object was loaded with RTLD_LOCAL, add it to list of local
* objects , so that its symbols cannot be retrieved even if the handle for
* the original program file is passed . POSIX says that if the same
* file is specified in multiple invocations , and any of them are
* RTLD_GLOBAL , even if any further invocations use RTLD_LOCAL , the
* symbols will remain global . If number of loaded modules was not
* changed after calling LoadLibraryEx ( ) , it means that library was
* already loaded .
*/
if ( ( mode & RTLD_LOCAL ) & & dwProcModsBefore ! = dwProcModsAfter )
{
if ( ! local_add ( hModule ) )
{
save_err_str ( lpFileName , ERROR_NOT_ENOUGH_MEMORY ) ;
FreeLibrary ( hModule ) ;
hModule = NULL ;
}
}
else if ( ! ( mode & RTLD_LOCAL ) & & dwProcModsBefore = = dwProcModsAfter )
{
local_rem ( hModule ) ;
}
}
}
}
/* Return to previous state of the error-mode bit flags. */
MySetErrorMode ( uMode ) ;
return ( void * ) hModule ;
}
DLFCN_EXPORT
int dlclose ( void * handle )
{
HMODULE hModule = ( HMODULE ) handle ;
BOOL ret ;
error_occurred = FALSE ;
ret = FreeLibrary ( hModule ) ;
/* If the object was loaded with RTLD_LOCAL, remove it from list of local
* objects .
*/
if ( ret )
local_rem ( hModule ) ;
else
save_err_ptr_str ( handle , GetLastError ( ) ) ;
/* dlclose's return value in inverted in relation to FreeLibrary's. */
ret = ! ret ;
return ( int ) ret ;
}
DLFCN_NOINLINE /* Needed for _ReturnAddress() */
DLFCN_EXPORT
void * dlsym ( void * handle , const char * name )
{
FARPROC symbol ;
HMODULE hCaller ;
HMODULE hModule ;
DWORD dwMessageId ;
error_occurred = FALSE ;
symbol = NULL ;
hCaller = NULL ;
hModule = GetModuleHandle ( NULL ) ;
dwMessageId = 0 ;
if ( handle = = RTLD_DEFAULT )
{
/* The symbol lookup happens in the normal global scope; that is,
* a search for a symbol using this handle would find the same
* definition as a direct use of this symbol in the program code .
* So use same lookup procedure as when filename is NULL .
*/
handle = hModule ;
}
else if ( handle = = RTLD_NEXT )
{
/* Specifies the next object after this one that defines name.
* This one refers to the object containing the invocation of dlsym ( ) .
* The next object is the one found upon the application of a load
* order symbol resolution algorithm . To get caller function of dlsym ( )
* use _ReturnAddress ( ) intrinsic . To get HMODULE of caller function
* use MyGetModuleHandleFromAddress ( ) which calls either standard
* GetModuleHandleExA ( ) function or hack via VirtualQuery ( ) .
*/
hCaller = MyGetModuleHandleFromAddress ( _ReturnAddress ( ) ) ;
if ( hCaller = = NULL )
{
dwMessageId = ERROR_INVALID_PARAMETER ;
goto end ;
}
}
if ( handle ! = RTLD_NEXT )
{
symbol = GetProcAddress ( ( HMODULE ) handle , name ) ;
if ( symbol ! = NULL )
goto end ;
}
/* If the handle for the original program file is passed, also search
* in all globally loaded objects .
*/
if ( hModule = = handle | | handle = = RTLD_NEXT )
{
HANDLE hCurrentProc ;
HMODULE * modules ;
DWORD cbNeeded ;
DWORD dwSize ;
size_t i ;
hCurrentProc = GetCurrentProcess ( ) ;
/* GetModuleHandle( NULL ) only returns the current program file. So
* if we want to get ALL loaded module including those in linked DLLs ,
* we have to use EnumProcessModules ( ) .
*/
if ( MyEnumProcessModules ( hCurrentProc , NULL , 0 , & dwSize ) ! = 0 )
{
modules = ( HMODULE * ) malloc ( dwSize ) ;
if ( modules )
{
if ( MyEnumProcessModules ( hCurrentProc , modules , dwSize , & cbNeeded ) ! = 0 & & dwSize = = cbNeeded )
{
for ( i = 0 ; i < dwSize / sizeof ( HMODULE ) ; i + + )
{
if ( handle = = RTLD_NEXT & & hCaller )
{
/* Next modules can be used for RTLD_NEXT */
if ( hCaller = = modules [ i ] )
hCaller = NULL ;
continue ;
}
if ( local_search ( modules [ i ] ) )
continue ;
symbol = GetProcAddress ( modules [ i ] , name ) ;
if ( symbol ! = NULL )
{
free ( modules ) ;
goto end ;
}
}
}
free ( modules ) ;
}
else
{
dwMessageId = ERROR_NOT_ENOUGH_MEMORY ;
goto end ;
}
}
}
end :
if ( symbol = = NULL )
{
if ( ! dwMessageId )
dwMessageId = ERROR_PROC_NOT_FOUND ;
save_err_str ( name , dwMessageId ) ;
}
return * ( void * * ) ( & symbol ) ;
}
DLFCN_EXPORT
char * dlerror ( void )
{
/* If this is the second consecutive call to dlerror, return NULL */
if ( ! error_occurred )
return NULL ;
/* POSIX says that invoking dlerror( ) a second time, immediately following
* a prior invocation , shall result in NULL being returned .
*/
error_occurred = FALSE ;
return error_buffer ;
}
/* 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
* for details */
/* Get specific image section */
static BOOL get_image_section ( HMODULE module , int index , void * * ptr , DWORD * size )
{
IMAGE_DOS_HEADER * dosHeader ;
IMAGE_NT_HEADERS * ntHeaders ;
IMAGE_OPTIONAL_HEADER * optionalHeader ;
dosHeader = ( IMAGE_DOS_HEADER * ) module ;
if ( dosHeader - > e_magic ! = IMAGE_DOS_SIGNATURE )
return FALSE ;
ntHeaders = ( IMAGE_NT_HEADERS * ) ( ( BYTE * ) dosHeader + dosHeader - > e_lfanew ) ;
if ( ntHeaders - > Signature ! = IMAGE_NT_SIGNATURE )
return FALSE ;
optionalHeader = & ntHeaders - > OptionalHeader ;
if ( optionalHeader - > Magic ! = IMAGE_NT_OPTIONAL_HDR_MAGIC )
return FALSE ;
if ( index < 0 | | index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
return FALSE ;
if ( optionalHeader - > DataDirectory [ index ] . Size = = 0 | | optionalHeader - > DataDirectory [ index ] . VirtualAddress = = 0 )
return FALSE ;
if ( size ! = NULL )
* size = optionalHeader - > DataDirectory [ index ] . Size ;
* ptr = ( void * ) ( ( BYTE * ) module + optionalHeader - > DataDirectory [ index ] . VirtualAddress ) ;
return TRUE ;
}
/* Return symbol name for a given address from export table */
static const char * get_export_symbol_name ( HMODULE module , IMAGE_EXPORT_DIRECTORY * ied , const 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 ( const void * addr )
{
MEMORY_BASIC_INFORMATION info ;
SIZE_T result ;
if ( addr = = NULL )
return FALSE ;
/* check valid pointer */
result = VirtualQuery ( addr , & info , sizeof ( info ) ) ;
if ( result = = 0 | | info . AllocationBase = = NULL | | info . AllocationProtect = = 0 | | info . AllocationProtect = = PAGE_NOACCESS )
return FALSE ;
return TRUE ;
}
/* Return state if address points to an import thunk
*
* An import thunk is setup with a ' jmp ' instruction followed by an
* absolute address ( 32 bit ) or relative offset ( 64 bit ) pointing into
* the import address table ( iat ) , which is partially maintained by
* the runtime linker .
*/
static BOOL is_import_thunk ( const void * addr )
{
return * ( short * ) addr = = 0x25ff ? TRUE : FALSE ;
}
/* Return adress from the import address table (iat),
* if the original address points to a thunk table entry .
*/
static void * get_address_from_import_address_table ( void * iat , DWORD iat_size , const void * addr )
{
BYTE * thkp = ( BYTE * ) addr ;
/* Get offset from thunk table (after instruction 0xff 0x25)
* 4018 c8 < _VirtualQuery > : ff 25 4 a 8 a 00 00
*/
ULONG offset = * ( ULONG * ) ( thkp + 2 ) ;
# ifdef _WIN64
/* On 64 bit the offset is relative
* 4018 c8 : ff 25 4 a 8 a 00 00 jmpq * 0x8a4a ( % rip ) # 40 a318 < __imp_VirtualQuery >
* And can be also negative ( MSVC in WDK )
* 100002f 20 : ff 25 3 a e1 ff ff jmpq * - 0x1ec6 ( % rip ) # 0x100001060
* So cast to signed LONG type
*/
BYTE * ptr = ( BYTE * ) ( thkp + 6 + ( LONG ) offset ) ;
# else
/* On 32 bit the offset is absolute
* 4019 b4 : ff 25 90 71 40 00 jmp * 0x40719
*/
BYTE * ptr = ( BYTE * ) offset ;
# endif
if ( ! is_valid_address ( ptr ) | | ptr < ( BYTE * ) iat | | ptr > ( BYTE * ) iat + iat_size )
return NULL ;
return * ( void * * ) ptr ;
}
/* Holds module filename */
static char module_filename [ 2 * MAX_PATH ] ;
static BOOL fill_info ( const void * addr , Dl_info * info )
{
HMODULE hModule ;
DWORD dwSize ;
IMAGE_EXPORT_DIRECTORY * ied ;
void * funcAddress = NULL ;
/* Get module of the specified address */
hModule = MyGetModuleHandleFromAddress ( addr ) ;
if ( hModule = = NULL )
return FALSE ;
dwSize = GetModuleFileNameA ( hModule , module_filename , sizeof ( module_filename ) ) ;
if ( dwSize = = 0 | | dwSize = = sizeof ( module_filename ) )
return FALSE ;
info - > dli_fname = module_filename ;
info - > dli_fbase = ( void * ) hModule ;
/* 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 ) ;
else
info - > dli_sname = NULL ;
info - > dli_saddr = info - > dli_sname = = NULL ? NULL : funcAddress ! = NULL ? funcAddress : ( void * ) addr ;
return TRUE ;
}
DLFCN_EXPORT
int dladdr ( const void * addr , Dl_info * info )
{
if ( info = = NULL )
return 0 ;
if ( ! is_valid_address ( addr ) )
return 0 ;
if ( is_import_thunk ( addr ) )
{
void * iat ;
DWORD iatSize ;
HMODULE hModule ;
/* Get module of the import thunk address */
hModule = MyGetModuleHandleFromAddress ( addr ) ;
if ( 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 ;
iat = ( void * ) ( ( BYTE * ) hModule + iid - > FirstThunk ) ;
/* We assume that in this case iid and iat's are in linear order */
iatSize = iidSize - ( DWORD ) ( ( BYTE * ) iat - ( BYTE * ) iid ) ;
}
addr = get_address_from_import_address_table ( iat , iatSize , addr ) ;
if ( ! is_valid_address ( addr ) )
return 0 ;
}
if ( ! fill_info ( addr , info ) )
return 0 ;
return 1 ;
}
# ifdef DLFCN_WIN32_SHARED
BOOL WINAPI DllMain ( HINSTANCE hinstDLL , DWORD fdwReason , LPVOID lpvReserved )
{
( void ) hinstDLL ;
( void ) fdwReason ;
( void ) lpvReserved ;
return TRUE ;
}
# endif