diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/dlfcn-win32/dlfcn.cc | 820 | ||||
| -rw-r--r-- | libs/dlfcn-win32/dlfcn.h | 94 | 
2 files changed, 914 insertions, 0 deletions
| diff --git a/libs/dlfcn-win32/dlfcn.cc b/libs/dlfcn-win32/dlfcn.cc new file mode 100644 index 000000000..03ed34dba --- /dev/null +++ b/libs/dlfcn-win32/dlfcn.cc @@ -0,0 +1,820 @@ +/*
 + * 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
 diff --git a/libs/dlfcn-win32/dlfcn.h b/libs/dlfcn-win32/dlfcn.h new file mode 100644 index 000000000..bf5c7d4d1 --- /dev/null +++ b/libs/dlfcn-win32/dlfcn.h @@ -0,0 +1,94 @@ +/*
 + * dlfcn-win32
 + * Copyright (c) 2007 Ramiro Polla
 + *
 + * 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.
 + */
 +
 +#ifndef DLFCN_H
 +#define DLFCN_H
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +#if defined(DLFCN_WIN32_SHARED)
 +#if defined(DLFCN_WIN32_EXPORTS)
 +#   define DLFCN_EXPORT __declspec(dllexport)
 +#else
 +#   define DLFCN_EXPORT __declspec(dllimport)
 +#endif
 +#else
 +#   define DLFCN_EXPORT
 +#endif
 +
 +/* Relocations are performed when the object is loaded. */
 +#define RTLD_NOW    0
 +
 +/* Relocations are performed at an implementation-defined time.
 + * Windows API does not support lazy symbol resolving (when first reference
 + * to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW.
 + */
 +#define RTLD_LAZY   RTLD_NOW
 +
 +/* All symbols are available for relocation processing of other modules. */
 +#define RTLD_GLOBAL (1 << 1)
 +
 +/* All symbols are not made available for relocation processing by other modules. */
 +#define RTLD_LOCAL  (1 << 2)
 +
 +/* These two were added in The Open Group Base Specifications Issue 6.
 + * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.
 + */
 +
 +/* The symbol lookup happens in the normal global scope. */
 +#define RTLD_DEFAULT    ((void *)0)
 +
 +/* Specifies the next object after this one that defines name. */
 +#define RTLD_NEXT       ((void *)-1)
 +
 +/* Structure filled in by dladdr() */
 +typedef struct dl_info
 +{
 +   const char *dli_fname;  /* Filename of defining object (thread unsafe and reused on every call to dladdr) */
 +   void       *dli_fbase;  /* Load address of that object */
 +   const char *dli_sname;  /* Name of nearest lower symbol */
 +   void       *dli_saddr;  /* Exact value of nearest symbol */
 +} Dl_info;
 +
 +/* Open a symbol table handle. */
 +DLFCN_EXPORT void *dlopen(const char *file, int mode);
 +
 +/* Close a symbol table handle. */
 +DLFCN_EXPORT int dlclose(void *handle);
 +
 +/* Get the address of a symbol from a symbol table handle. */
 +DLFCN_EXPORT void *dlsym(void *handle, const char *name);
 +
 +/* Get diagnostic information. */
 +DLFCN_EXPORT char *dlerror(void);
 +
 +/* Translate address to symbolic information (no POSIX standard) */
 +DLFCN_EXPORT int dladdr(const void *addr, Dl_info *info);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif /* DLFCN_H */
 | 
