mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			820 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			820 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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 */
 | |
| #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 64K 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 (32bit) or relative offset (64bit) 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)
 | |
|      *   4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
 | |
|      */
 | |
|     ULONG offset = *(ULONG *)( thkp + 2 );
 | |
| #ifdef _WIN64
 | |
|     /* On 64 bit the offset is relative
 | |
|      *      4018c8:   ff 25 4a 8a 00 00    jmpq    *0x8a4a(%rip)    # 40a318 <__imp_VirtualQuery>
 | |
|      * And can be also negative (MSVC in WDK)
 | |
|      *   100002f20:   ff 25 3a 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
 | |
|      *   4019b4:    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
 |