roken: Take https://github.com/dlfcn-w32/dlfcn-w32
This is a much better and more complete implementation of POSIX RTLD functions than what we had. In particular this gets us a more complete `dladdr()` implementation.
This commit is contained in:
@@ -54,16 +54,30 @@ typedef int (__stdcall *DLSYM_RET_TYPE)();
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Implementation based on
|
||||
http://www.opengroup.org/onlinepubs/009695399/basedefs/dlfcn.h.html */
|
||||
/* Relocations are performed when the object is loaded. */
|
||||
#define RTLD_NOW 0
|
||||
|
||||
#define RTLD_LAZY (1<<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
|
||||
|
||||
#define RTLD_NOW (1<<1)
|
||||
/* All symbols are available for relocation processing of other modules. */
|
||||
#define RTLD_GLOBAL (1 << 1)
|
||||
|
||||
#define RTLD_GLOBAL (1<<2)
|
||||
/* All symbols are not made available for relocation processing by other modules. */
|
||||
#define RTLD_LOCAL (1 << 2)
|
||||
|
||||
#define RTLD_LOCAL (1<<3)
|
||||
/* 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 *)0LL)
|
||||
|
||||
/* Specifies the next object after this one that defines name. */
|
||||
#define RTLD_NEXT ((void *)-1LL)
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
dlclose(void *);
|
||||
@@ -77,9 +91,12 @@ dlopen(const char *, int);
|
||||
ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
|
||||
dlsym(void *, const char *);
|
||||
|
||||
typedef struct Dl_info {
|
||||
char *dli_fname;
|
||||
char _dli_buf[MAX_PATH + 2];
|
||||
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, Dl_info_t;
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
|
@@ -1,185 +1,700 @@
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2009, Secure Endpoints Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include <config.h>
|
||||
#include <windows.h>
|
||||
#include <dlfcn.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#define ERR_STR_LEN 256
|
||||
|
||||
static volatile LONG dlfcn_tls = TLS_OUT_OF_INDEXES;
|
||||
|
||||
static DWORD get_tl_error_slot(void)
|
||||
{
|
||||
if (dlfcn_tls == TLS_OUT_OF_INDEXES) {
|
||||
DWORD slot = TlsAlloc();
|
||||
DWORD old_slot;
|
||||
|
||||
if (slot == TLS_OUT_OF_INDEXES)
|
||||
return dlfcn_tls;
|
||||
|
||||
if ((old_slot = InterlockedCompareExchange(&dlfcn_tls, slot,
|
||||
TLS_OUT_OF_INDEXES)) !=
|
||||
TLS_OUT_OF_INDEXES) {
|
||||
|
||||
/* Lost a race */
|
||||
TlsFree(slot);
|
||||
return old_slot;
|
||||
} else {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
|
||||
return dlfcn_tls;
|
||||
}
|
||||
|
||||
static void set_error(const char * e)
|
||||
{
|
||||
char * s;
|
||||
char * old_s;
|
||||
size_t len;
|
||||
|
||||
DWORD slot = get_tl_error_slot();
|
||||
|
||||
if (slot == TLS_OUT_OF_INDEXES)
|
||||
return;
|
||||
|
||||
len = strlen(e) * sizeof(char) + sizeof(char);
|
||||
s = LocalAlloc(LMEM_FIXED, len);
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
old_s = (char *) TlsGetValue(slot);
|
||||
TlsSetValue(slot, (LPVOID) s);
|
||||
|
||||
if (old_s != NULL)
|
||||
LocalFree(old_s);
|
||||
}
|
||||
|
||||
static void set_error_from_last(void) {
|
||||
DWORD slot = get_tl_error_slot();
|
||||
char * s = NULL;
|
||||
char * old_s;
|
||||
|
||||
if (slot == TLS_OUT_OF_INDEXES)
|
||||
return;
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
0, GetLastError(), 0,
|
||||
(LPTSTR) &s, 0,
|
||||
NULL);
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
old_s = (char *) TlsGetValue(slot);
|
||||
TlsSetValue(slot, (LPVOID) s);
|
||||
|
||||
if (old_s != NULL)
|
||||
LocalFree(old_s);
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
dlclose(void * vhm)
|
||||
{
|
||||
BOOL brv;
|
||||
|
||||
brv = FreeLibrary((HMODULE) vhm);
|
||||
if (!brv) {
|
||||
set_error_from_last();
|
||||
}
|
||||
return !brv;
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
|
||||
dlerror(void)
|
||||
{
|
||||
DWORD slot = get_tl_error_slot();
|
||||
|
||||
if (slot == TLS_OUT_OF_INDEXES)
|
||||
return NULL;
|
||||
|
||||
return (char *) TlsGetValue(slot);
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
|
||||
dlopen(const char *fn, int flags)
|
||||
{
|
||||
HMODULE hm;
|
||||
UINT old_error_mode;
|
||||
|
||||
/* We don't support dlopen(0, ...) on Windows.*/
|
||||
if ( fn == NULL ) {
|
||||
set_error("Not implemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
|
||||
hm = LoadLibraryEx(fn, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
|
||||
if (hm == NULL) {
|
||||
set_error_from_last();
|
||||
}
|
||||
|
||||
SetErrorMode(old_error_mode);
|
||||
|
||||
return (void *) hm;
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
|
||||
dlsym(void * vhm, const char * func_name)
|
||||
{
|
||||
HMODULE hm = (HMODULE) vhm;
|
||||
|
||||
return (DLSYM_RET_TYPE)(ULONG_PTR)GetProcAddress(hm, func_name);
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
dladdr(void *addr, Dl_info *dli)
|
||||
{
|
||||
HMODULE hm;
|
||||
DWORD nsize;
|
||||
|
||||
memset(dli, 0, sizeof(*dli));
|
||||
dli->dli_fname = dli->_dli_buf;
|
||||
|
||||
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCTSTR)(ULONG_PTR)addr, &hm))
|
||||
return 0;
|
||||
|
||||
nsize = GetModuleFileName(hm, dli->_dli_buf, sizeof(dli->_dli_buf));
|
||||
dli->_dli_buf[sizeof(dli->_dli_buf) - 1] = '\0';
|
||||
if (nsize >= sizeof(dli->_dli_buf))
|
||||
return 0; /* truncated? can't be... */
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copied from https://github.com/dlfcn-win32/dlfcn-win32 and modified only to
|
||||
* fit Heimdal's lib/roken.
|
||||
*/
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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"
|
||||
|
||||
/* 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 )
|
||||
return TRUE;
|
||||
|
||||
for( pobject = &first_object; pobject->next; pobject = pobject->next );
|
||||
|
||||
nobject = (local_object*) malloc( sizeof( local_object ) );
|
||||
|
||||
if( !nobject )
|
||||
{
|
||||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||||
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 )
|
||||
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;
|
||||
|
||||
dwMessageId = GetLastError( );
|
||||
|
||||
if( dwMessageId == 0 )
|
||||
return;
|
||||
|
||||
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 )
|
||||
{
|
||||
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 = ( ( (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 );
|
||||
}
|
||||
|
||||
/* 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);
|
||||
HMODULE psapi;
|
||||
|
||||
if( !EnumProcessModulesPtr )
|
||||
{
|
||||
psapi = LoadLibraryA( "Psapi.dll" );
|
||||
if( psapi )
|
||||
EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
|
||||
if( !EnumProcessModulesPtr )
|
||||
return 0;
|
||||
}
|
||||
|
||||
return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
|
||||
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 = SetErrorMode( SEM_FAILCRITICALERRORS );
|
||||
|
||||
if( file == 0 )
|
||||
{
|
||||
/* POSIX says that if the value of file is 0, 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)" );
|
||||
}
|
||||
else
|
||||
{
|
||||
HANDLE hCurrentProc;
|
||||
DWORD dwProcModsBefore, dwProcModsAfter;
|
||||
char lpFileName[MAX_PATH];
|
||||
size_t i, len;
|
||||
|
||||
len = strlen( file );
|
||||
|
||||
if( len >= sizeof( lpFileName ) )
|
||||
{
|
||||
SetLastError( ERROR_FILENAME_EXCED_RANGE );
|
||||
save_err_str( file );
|
||||
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 );
|
||||
}
|
||||
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 );
|
||||
FreeLibrary( hModule );
|
||||
hModule = NULL;
|
||||
}
|
||||
}
|
||||
else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
|
||||
{
|
||||
local_rem( hModule );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return to previous state of the error-mode bit flags. */
|
||||
SetErrorMode( uMode );
|
||||
|
||||
return (void *) hModule;
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
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 );
|
||||
|
||||
/* dlclose's return value in inverted in relation to FreeLibrary's. */
|
||||
ret = !ret;
|
||||
|
||||
return (int) ret;
|
||||
}
|
||||
|
||||
__declspec(noinline) /* Needed for _ReturnAddress() */
|
||||
ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
|
||||
dlsym( void *handle, const char *name )
|
||||
{
|
||||
FARPROC symbol;
|
||||
HMODULE hCaller;
|
||||
HMODULE hModule;
|
||||
HANDLE hCurrentProc;
|
||||
|
||||
error_occurred = FALSE;
|
||||
|
||||
symbol = NULL;
|
||||
hCaller = NULL;
|
||||
hModule = GetModuleHandle( NULL );
|
||||
hCurrentProc = GetCurrentProcess( );
|
||||
|
||||
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 standard GetModuleHandleExA() function.
|
||||
*/
|
||||
if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) _ReturnAddress( ), &hCaller ) )
|
||||
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 )
|
||||
{
|
||||
HMODULE *modules;
|
||||
DWORD cbNeeded;
|
||||
DWORD dwSize;
|
||||
size_t i;
|
||||
|
||||
/* 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 = 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
|
||||
{
|
||||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if( symbol == NULL )
|
||||
{
|
||||
if( GetLastError() == 0 )
|
||||
SetLastError( ERROR_PROC_NOT_FOUND );
|
||||
save_err_str( name );
|
||||
}
|
||||
|
||||
return *(void **) (&symbol);
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
|
||||
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_OPTIONAL_HEADER *optionalHeader;
|
||||
|
||||
dosHeader = (IMAGE_DOS_HEADER *) module;
|
||||
|
||||
if( dosHeader->e_magic != 0x5A4D )
|
||||
return FALSE;
|
||||
|
||||
optionalHeader = (IMAGE_OPTIONAL_HEADER *) ( (BYTE *) module + dosHeader->e_lfanew + 24 );
|
||||
|
||||
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 */
|
||||
static const char *get_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address )
|
||||
{
|
||||
int i;
|
||||
void *candidateAddr = NULL;
|
||||
const char *candidateName = NULL;
|
||||
BYTE *base = (BYTE *) module; /* Required to have correct calculations */
|
||||
|
||||
for( i = 0; iid[i].Characteristics != 0 && iid[i].FirstThunk != 0; i++ )
|
||||
{
|
||||
IMAGE_THUNK_DATA *thunkILT = (IMAGE_THUNK_DATA *)( base + iid[i].Characteristics );
|
||||
IMAGE_THUNK_DATA *thunkIAT = (IMAGE_THUNK_DATA *)( base + iid[i].FirstThunk );
|
||||
|
||||
for( ; thunkILT->u1.AddressOfData != 0; thunkILT++, thunkIAT++ )
|
||||
{
|
||||
IMAGE_IMPORT_BY_NAME *nameData;
|
||||
|
||||
if( IMAGE_SNAP_BY_ORDINAL( thunkILT->u1.Ordinal ) )
|
||||
continue;
|
||||
|
||||
if( (void *) thunkIAT->u1.Function > addr || candidateAddr >= (void *) thunkIAT->u1.Function )
|
||||
continue;
|
||||
|
||||
candidateAddr = (void *) thunkIAT->u1.Function;
|
||||
nameData = (IMAGE_IMPORT_BY_NAME *)( base + (ULONG_PTR) thunkILT->u1.AddressOfData );
|
||||
candidateName = (const char *) nameData->Name;
|
||||
}
|
||||
}
|
||||
|
||||
*func_address = candidateAddr;
|
||||
return candidateName;
|
||||
}
|
||||
|
||||
static BOOL is_valid_address( 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( 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, 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_module_info( void *addr, Dl_info *info )
|
||||
{
|
||||
HMODULE hModule;
|
||||
DWORD dwSize;
|
||||
|
||||
if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL )
|
||||
return FALSE;
|
||||
|
||||
dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) );
|
||||
|
||||
if( dwSize == 0 || dwSize == sizeof( module_filename ) )
|
||||
return FALSE;
|
||||
|
||||
info->dli_fbase = (void *) hModule;
|
||||
info->dli_fname = module_filename;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
dladdr( void *addr, Dl_info *info )
|
||||
{
|
||||
void *realAddr, *funcAddress = NULL;
|
||||
HMODULE hModule;
|
||||
IMAGE_IMPORT_DESCRIPTOR *iid;
|
||||
DWORD iidSize = 0;
|
||||
|
||||
if( addr == NULL || info == NULL )
|
||||
return 0;
|
||||
|
||||
hModule = GetModuleHandleA( NULL );
|
||||
if( hModule == NULL )
|
||||
return 0;
|
||||
|
||||
if( !is_valid_address( addr ) )
|
||||
return 0;
|
||||
|
||||
realAddr = addr;
|
||||
|
||||
if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) )
|
||||
return 0;
|
||||
|
||||
if( is_import_thunk( addr ) )
|
||||
{
|
||||
void *iat;
|
||||
void *iatAddr = NULL;
|
||||
DWORD iatSize = 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 */
|
||||
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 );
|
||||
}
|
||||
|
||||
iatAddr = get_address_from_import_address_table( iat, iatSize, addr );
|
||||
if( iatAddr == NULL )
|
||||
return 0;
|
||||
|
||||
realAddr = iatAddr;
|
||||
}
|
||||
|
||||
if( !fill_module_info( realAddr, info ) )
|
||||
return 0;
|
||||
|
||||
info->dli_sname = get_symbol_name( hModule, iid, realAddr, &funcAddress );
|
||||
|
||||
info->dli_saddr = !info->dli_sname ? NULL : funcAddress ? funcAddress : realAddr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DLFCN_WIN32_SHARED
|
||||
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
|
||||
{
|
||||
(void) hinstDLL;
|
||||
(void) fdwReason;
|
||||
(void) lpvReserved;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user